datetime2
overview¶
The definition of a moment in history is independent from the way it is
represented in different cultures. Indeed the same day is represented in
different ways by different calendars. The datetime2
module detaches
operations on date and time objects from their representation, and allows to
add other representations at run time. The module does all this in an
efficient and syntactically clear way.
We can create a date object by calling the relevant access attribute of the
base class Date
:
>>> d1 = Date.gregorian(1965, 3, 1) # Gregorian: 1965-03-01
>>> d2 = Date.iso(2011, 23, 4) # ISO: 2011-W23-4
Each of these date objects can be printed and has its own attributes and methods:
>>> print(d1.gregorian)
1965-03-01
>>> print(d1.gregorian.month)
3
>>> print(d1.gregorian.weekday())
1
>>> print(d2.iso)
2011-W23-4
>>> print(d2.iso.week)
23
>>> print(d2.iso.day_of_year())
158
One of the strength of datetime2
is that we can mix these attributes,
independently from how the date object is built:
>>> print(d1.iso)
1965-W09-1
>>> print(d1.iso.week)
9
>>> print(d1.iso.day_of_year())
57
>>> print(d2.gregorian)
2011-06-09
>>> print(d2.gregorian.month)
6
>>> print(d2.gregorian.weekday())
4
The same strength is true also for time, where different representations can be mixed like in dates:
>>> t1 = Time.western(15, 47, 16, timezone=-6)
>>> t2 = Time.internet(895)
>>> print(t1.western)
15:47:16-06:00
>>> print(t1.internet)
@949
>>> print(t2.western)
21:28:48+01:00
>>> print(t2.western.minute)
28
The relation between time objects can be either implicit, i.e. depending on
implementation, or explicit, which means that the objects know how they
relate to each other. Then standard way for the latter is with UTC. An object
of the first kind is said to be naive, of the second kind is called aware
(like in Aware and Naive Objects of the datetime2
module). For
aware objects a second value is used in the constructor to indicate the
distance from UTC.
Many representations of the time of day of this module are aware by definition, so in those cases the UTC offset must not be given. E.g., the Internet time representation is based on Basel time zone (UTC+1).
Internal representation¶
In order to be able to convert between the different calendars and between
the different times of day, a generic and culturally independent way of
internally representing them has been chosen. For calendars, the idea,
inspired by the excellent book “Calendrical Calculations”[1], is to
identify each day by simply counting the days starting with 1 for January
1st of year 1, 2 for January 2nd is day 2, and so on. Using
the datetime2
module we write:
>>> d3 = Date(1)
>>> d4 = Date(737109)
These dates can then be handled like above:
>>> print(d3.gregorian)
0001-01-01
>>> print(d3.iso)
0001-W01-1
>>> print(d4.gregorian)
2019-02-19
>>> print(d4.iso)
2019-W08-2
Similarly, each Date
object can be printed with the internal
representation (even if it is of little use):
>>> print(d1)
R.D. 717396
>>> print(d2)
R.D. 734297
Where R.D.
stands for Rata Die, the Latin for “fixed date”.
There are similar generic representations for all base classes of the
datetime2
module:
Time
: a moment of the day is represented as a fraction of the day, starting from midnight. Distance from UTC, if given is also given as a fraction of a day.
>>> print(t1)
14209/21600 of a day, -1/4 of a day from UTC
>>> print(t2)
179/200 of a day, 1/24 of a day from UTC
>>> t3 = Time(7, 10)
>>> t4 = Time(0.796875, utcoffset="1/4")
>>> print(t3.western)
16:48:00
>>> print(t4.western)
19:07:30+06:00
>>> print(t4.internet)
@588
TimeDelta
: a time interval is given in number of days, possibly fractional to account for parts of day.
>>> td1 = TimeDelta(8, 10)
>>> print(td1)
4/5 of a day
>>> print(td1.western)
19 hours, 12 minutes
>>> td2 = TimeDelta(118, 12)
>>> print(td2)
9 days and 5/6 of a day
>>> print(td2.western)
9 days and 20 hours
[1] | “Calendrical Calculations: The Ultimate Edition”, E. M. Reingold, N. Dershowitz, Cambridge University Press, 2018 |
Index¶
datetime2
- New date and time types¶
The heart of the datetime2
module is made of four base classes,
each having a very simple definition. All base classes implement
operations for date and time independently of the way they are created.
datetime2
class names use the CapitalizedWords convention required by
PEP 8, so they differ from the names of their similar counterparts in
datetime
module.
Date
objects¶
A Date
object represents a specific date. To do so it uses an
idealized calendar, in which it counts the days elapsed from Gregorian Dec
31st of year 0, i.e. January 1st of year 1 is day number 1,
January 2nd of year 1 is day number 2, and so on. This calendar
ideally extends indefinitely in both directions.
There are two ways of creating a Date
instance:
-
class
datetime2.
Date
(day_count)¶ Return an object that represent a date which is
day_count
days after December 31st of year 0 in an ideal Gregorian calendar that has no limit. The argumentday_count
is required and it must be an integer, otherwise aTypeError
exception is raised. There is no restriction on its numeric value.
Date
instances have one attribute:
-
Date.
day_count
¶ An integer that represents the number of days between the given date and January 1st, year 1. This attribute is read-only: an
AttributeError
exception is raised when trying to change it.
Date
instances are immutable, so they can be used as dictionary keys.
When two aware instances indicate the same time, even if they have different
UTC offsets, the have the same hash. The hash function takes into consideration also that They can also be pickled and unpickled.
In boolean contexts, all Date
instances are considered to be true.
Date
has one instance method:
-
Date.
__str__
()¶ Return
R.D.
followed by the day count.R.D.
stands for Rata Die, the Latin for “fixed date”.
Available calendars¶
Currently (version 0.9.4) the calendars listed below are available.
Calendar | Access attribute | Calendar class | Module |
---|---|---|---|
Gregorian | gregorian |
GregorianCalendar | datetime2.western |
ISO | iso |
IsoCalendar | datetime2.modern |
Supported operations¶
Operation | Result |
---|---|
date2 = date1 + timedelta |
date2 is timedelta days after
date1. Reverse addition (timedelta +
date1 ) is allowed. (1) (2) |
date2 = date1 - timedelta |
date2 is timedelta days before
date1. (1) (3) |
timedelta = date1 - date2 |
A TimeDelta object is returned
representing the number of days
between date1 and date2. (4) |
date1 < date2 |
date1 is less than date2 when it represents a day earlier that that of date2. (5) (6) |
Notes:
- A
ValueError
exception is raised if timedelta is not an integral number of days. timedelta object with non-integral number of days must be added or subtracted fromDateTime
instances. - If timedelta is negative,
date2
will be beforedate1
. - If timedelta is negative,
date2
will be afterdate1
. - The timedelta instance created when subtracting
Date
instances will always have an integral number of days, positive ifdate1
is later thandate2
, negative otherwise. - In other words,
date1 < date2
if and only ifdate1.day_count < date2.day_count
. All other comparison operators (<=
,>
,>=
,==
and!=
) behave similarly. - When comparing a
Date
object and an object of another class, if the latter has aday_count
attribute,NotImplemented
is returned. This allows a Date-like instance to perform reflected comparison if it is the second operator. When the second object doesn’t have aday_count
attribute, if the operator is equality (==
) or inequality (!=
), the value returned is alwaysFalse
andTrue
respectively. If the operator is one of the other four (<=
,>
,>=
or==
), aTypeError
exception is raised.
Time
objects¶
An indication of time, independent of any particular day, expressed as a
fraction of day. There might be an indication of time difference from UTC,
e.g. due to time zone or daylight saving time. This time difference is
expressed as fraction of a day and represents the time to be added to local
time to get UTC. If there is this indication, the Time
object is
said to be “aware” and it is used to represent a precise moment (regardless
of the day). An object without indication is said to be “naive”, and its
interpretation is left to the program that uses it.
There are five Time
constructors:
-
class
datetime2.
Time
(day_frac, *, utcoffset=None)¶
-
class
datetime2.
Time
(numerator, denominator, *, utcoffset=None) Return an object that represents a moment in a day as a fraction of the whole day, given in the
day_frac
argument. If needed, it is possible to assign to the instance an indication of the time offset from UTC, for whatever political, algorithmic or geographic need (e.g. time zone), using theutcoffset
argument, which must be explicitly named.The
day_frac
andutcoffset
arguments can be anything that can be passed to thefractions.Fraction
constructor, i.e. an integer, a float, another Fraction, a Decimal number or a string representing an integer, a float or a fraction. Theday_frac
argument only can also be passed with two values that represent numerator and denominator of the fraction. ATypeError
exception is raised if the type of any argument is not one of the accepted types. AZeroDivisionError
exception is raised if the denominator is 0.The value for
day_frac
must be equal or greater than 0 and less than 1. The value forutcoffset
in aware objects must be equal or greater than -1 and less or equal to 1. AValueError
exception is raised if values are outside these ranges.
-
classmethod
Time.
now
(utcoffset=None)¶ Return an aware
Time
object that represents the current time. Without argument, the time represented inday_frac
will be local standard time andutcoffset
will be set to the difference between local standard time and UTC.If
utcoffset
is given, the returned object will be the current time at the given time difference from UTC.utcoffset
follows the same requirements of the default constructor.
-
classmethod
Time.
localnow
()¶ Return a naive
Time
object that represents the current local standard time.
Two read-only attributes store the day_frac
and utcoffset
arguments.
The former is always a Fraction object, the latter is either a Fraction
object or None
, for naive time. An attempt to directly set the values of
these two attributes will raise an AttributeError
exception.
Time
objects support comparison, where time1 is considered less
than time2 when the former represents a moment earlier than the latter.
UTC offset in aware instances is always taken into account. When both
objects are Time
instances they must have the same naivety,
otherwise TypeError
is raised if an order comparison is attempted,
while for equality comparisons, naive instances are never equal to aware
instances.
When comparing a Time
object and an object of another class, if the
latter has the day_frac
and utcoffset
attributes, NotImplemented
is returned. This allows a Time-like instance to perform reflected comparison
if it is the second operator. In this case, the second object is responsible
for checking naivety.
Time
instances are immutable, so they can be used as dictionary keys.
They can also be pickled and unpickled. In boolean contexts, all Time
instances are considered to be true.
Instance method:
-
Time.
__str__
()¶ Return the string
<fraction> of a day
, where fraction is the value of theday_frac
attribute. UTC offset, if present, is represented as well:
>>> print(Time(4, 12))
1/3 of a day
>>> print(Time(3, 24, utcoffset="-4/24"))
1/8 of a day, -1/6 of a day from UTC
Available time representations¶
Currently (version 0.9.4) the time of day listed below are available.
Representation | Attribute | Time representation class | Module |
---|---|---|---|
Western | western |
WesternTime | datetime2.western |
Internet | internet |
InternetTime | datetime2.modern |
Supported operations¶
Operation | Result |
---|---|
time2 = time1 + timedelta |
time2 is timedelta time after
time1. Reverse addition (timedelta +
time1 ) is allowed. (1) (2) |
time2 = time1 - timedelta |
time2 is timedelta time before
time1. (1) (3) |
timedelta = time1 - time2 |
A TimeDelta object is returned
representing the day fraction
between time1 and time2. (4) |
time1 < time2 |
time1 is less than time2 when the former represents a moment earlier than the latter. UTC offset, if present, is taken into consideration. (5) (6) (7) |
Notes:
- The result of this operation will always be a valid
Time
instance. If overflow or underflow occur, the full day part will be truncated so that only the fractional part will remain. Naivety is preserved: iftime1
has a UTC offset, this will be copied totime2
. - If timedelta is negative,
time2
will be beforetime1
. - If timedelta is negative,
time2
will be aftertime1
. - The timedelta object created when subtracting two
Time
instances will always represent a fractional part of a day, with thedays
attribute value greater than -0.5 and less or equal to 0.5.time1
andtime2
must have the same naivety; if they don’t, aValueError
exception is raised. If they are aware, UTC offset of both instances will be taken into account to generate the result. - All other comparison operators (
<=
,>
,>=
,==
and!=
) behave similarly. - If both objects to be compared are
Time
instances, they must have the same naivety; if they don’t, aValueError
exception is raised. - When comparing a
Time
object and an object of another class, if the latter has aday_frac
attribute,NotImplemented
is returned. This allows a Time-like instance to perform reflected comparison if it is the second operator. In this case, the second object is responsible for checking naivety. If the second object doesn’t have aday_frac
attribute, if the operator is equality (==
) or inequality (!=
), the value returned is alwaysFalse
andTrue
respectively. If the operator is one of the other four (<=
,>
,>=
or==
), aTypeError
exception is raised.
Note
Given the rules above it, if time1
and time2
are aware instances,
time1 + (time2 - time1)
compares equal to time2
, but it will have
the same day_frac
value only if the UTC offsets of time1
and
time2
are equal.
TimeDelta
objects¶
An interval of time, expressed in fractional days.
There are two TimeDelta
constructors:
-
class
datetime2.
TimeDelta
(fractional_days)¶
-
class
datetime2.
TimeDelta
(numerator, denominator) Return an object that represents a time interval in fractional days, given in the
fractional_days
argument. This value will be greater than 1 to indicate an interval longer than 1 day.The
fractional_days
argument can be anything that can be passed to thefractions.Fraction
constructor, i.e. an integer, a float, another Fraction, a Decimal number or a string representing an integer, a float or a fraction. The argument can also be passed with two values that represent numerator and denominator of the fraction. ATypeError
exception is raised if the type of any argument is not one of the accepted types. AZeroDivisionError
exception is raised if the denominator is 0. There are no limits on the value offractional_days
.
The read-only attribute fractional_days
stores the value, always as a
Python Fraction object. An attempt to directly set the values of this
attribute will raise an AttributeError
exception. It is also possible
to access the integral and fractional parts of fractional_days
with two
calculated attributes: int_part
and frac_part
. If the time interval
is negative, both int_part
and frac_part
are negative. Given any
TimeDelta
instance td
, it is always:
td.fractional_days == td.int_part() + td.frac_part()
>>> td1 = Timedelta(16, 3)
>>> td1.int_part
5
>>> td1.frac_part
Fraction(1, 3)
>>> td2 = TimeDelta(-7.625)
>>> td2.int_part
-7
>>> td2.frac_part
Fraction(-5, 8)
TimeDelta
objects support comparison, where timedelta1 is
considered greater than timedelta2 when the former represents a time
interval longer than the latter. When comparing a TimeDelta
object
and an object of another class, if the latter has the fractional_days
attribute, NotImplemented
is returned. This allows a TimeDelta-like
instance to perform reflected comparison if it is the second operator.
TimeDelta
instances are immutable, so they can be used as dictionary
keys. They can also be pickled and unpickled.
In boolean contexts, a TimeDelta
instance is considered to be true
if and only if it isn’t equal to TimeDelta(0)
.
Instance methods:
-
TimeDelta.
int
()¶
-
TimeDelta.
frac
()¶ The first method return the same instance with only the integer part. The last method returns the same instance with only the fractional part. All methods return a negative value if the time interval is negative. In this way, given any
TimeDelta
instancetd
, it is always:td == td.int() + td.frac()
>>> td1 = Timedelta(16, 3)
>>> td1.int()
TimeDelta(Fraction(5, 1))
>>> td1.frac()
TimeDelta(Fraction(1, 3))
>>> td2 = TimeDelta(-7.625)
>>> int(td2)
TimeDelta(Fraction(-7, 1))
>>> td2.frac()
TimeDelta(Fraction(-5, 8))
-
TimeDelta.
is_integer
()¶ Returns
True
if the time interval is made of an integer number of days.
>>> Timedelta("3/4").is_integer()
False
>>> TimeDelta(-1).is_integer()
True
-
TimeDelta.
__str__
()¶ Returns a string indicating the number of days and the remaining fraction of a day. Note that whilst in
datetime.timedelta
the fractional part is always positive, inTimeDelta
the fractional part has the same sign of the integer part.
>>> td1 = Timedelta("1/12")
>>> print(td1)
1/12 of a day
>>> td2 = TimeDelta(3)
>>> print(td2)
3 days
>>> td3 = TimeDelta(11, -7)
>>> print(td3)
-1 day and -4/7 of a day
Available time interval representations¶
The following table lists the available time representations interval and the attributes by which they are reachable:
Representation | Attribute | Time representation class | Module |
---|---|---|---|
Western | western |
WesternTimeDelta | datetime2.western |
Note
Not available in version 0.9.0.
Supported operations¶
Operation | Result |
---|---|
timedelta1 = timedelta2 + timedelta3 |
Sum of two time intervals. |
timedelta1 = timedelta2 - timedelta3 |
Difference of two time intervals. |
timedelta1 = timedelta2 * number or
timedelta1 = number * timedelta2 |
Multiplication of a time interval by a number. (1) |
timedelta1 = timedelta2 / number |
Division of a time interval by a number. (1) |
number = timedelta1 / timedelta2 |
Returns a fraction which is the ratio between the two time intervals. |
timedelta1 = timedelta2 // number |
Floor division. Returns a time interval with an integer number of days. If dividend and divisor are of different sign, the result is negative and, if not integer, it is more negative than the true result. |
number = timedelta1 // timedelta2 |
Integer number of times timedelta2 is
contained in timedelta1 . If dividend and
divisor are of different sign, the result is
negative and, if not integer, it is more
negative than the true result. |
timedelta1 = timedelta2 % divisor |
Remainder of the division. This result always has the same sign of the divisor. |
divmod(timedelta, divisor) |
Return a tuple made of the integral quotient
and remainder of timedelta divided by
dividend. (2) |
timedelta1 < timedelta2 |
timedelta1 is less than timedelta2 when the former represents an interval shorter than the latter. (3) |
The table above does not include mixed type operations between TimeDelta
and Date
, Time
or DateTime
. For more information, see the
Supported operations chapter of each of these classes.
Class TimeDelta
also upports unary arithmetic operators +
, -
and abs()
.
Notes:
- The number is first converted to a fraction, then multiplication or division takes place. As such, if number is a float, float to Fraction conversion error may happen, and result may not be exact.
- If dividend is a number, see note (1).
- All other comparison operators (
<=
,>
,>=
,==
and!=
) behave similarly.
datetime2.western
- Gregorian calendar and western time¶
This module implements the calendar and time representation used in the western world:
- The Gregorian calendar
- The western time representation in hours, minutes and seconds.
Of course, they all conform to the requirements for interface classes listed in Customization.
Gregorian calendar¶
An instance of the GregorianCalendar
class represents a day in the
calendar as generally done in western countries. It is a solar calendar dividing
day count in years of 365 or 366 days, each year is then divided in 12 months
of 28 (or 29), 30 and 31 days.
The default constructor for a Gregorian day is:
-
class
datetime2.western.
GregorianCalendar
(year, month, day)¶ Return an object that represents the date given with Gregorian year, month and day. Month is entered as a number, not as a string. All arguments are required and must be integers. Values for
month
andday
must lie in the following ranges:1 <= month <= 12
1 <= day <= number of days in the given month and year
If an argument is outside those ranges, a
ValueError
exception is raised.
Another constructor can be used if the day in the year is known:
-
classmethod
GregorianCalendar.
year_day
(year, day_of_year)¶ Return an object that represents the day specified by a Gregorian year and the day in that year. Both arguments are required and must be integers. Value for
day_of_year
must be between 1 and the number of days in the year (either 365 or 366), otherwise aValueError
exception is raised.
A GregorianCalendar
object has three attributes:
-
GregorianCalendar.
year
¶
-
GregorianCalendar.
month
¶
-
GregorianCalendar.
day
¶ These attributes are read-only integer numbers. There is no restriction on the value of the year. Month will be between 1 and 12. Day will be between 1 and the number of days in the corresponding month. These attributes are read-only: an
AttributeError
exception is raised when trying to change any of them.
Two static method have been implemented to return details of a Gregorian year:
-
static
GregorianCalendar.
is_leap_year
(year)¶ Return
True
if year is a leap year in the Gregorian calendar.False
otherwise. For example,GregorianCalendar.is_leap_year(2008) == True
.
-
static
GregorianCalendar.
days_in_year
(year)¶ Return 366 if year is a leap year in the Gregorian calendar, 365 otherwise. For example,
GregorianCalendar.days_in_year(2100) == 365
.
An instance of the GregorianCalendar
class has the following
methods:
-
GregorianCalendar.
weekday
()¶ Return the day of the week as an integer, where Monday is 1 and Sunday is 7. For example,
GregorianCalendar(2002, 12, 4).weekday() == 3
, a Wednesday. Note that this is the ISO convention for weekdays, not the one used bydatetime.date.weekday()
, where Monday is 0 and Sunday is 6.
-
GregorianCalendar.
day_of_year
()¶ Return the number of days elapsed since January 1st. The result is a number from 1 to 365 or 366 (in leap years). For example,
GregorianCalendar(2008, 3, 1).day_of_year() == 61
.
-
GregorianCalendar.
replace
(year, month, day)¶ Returns a new
GregorianCalendar
object with the same value, except for those parameters given new values by whichever keyword arguments are specified. All values are optional; if used, they must be integers. If any argument is outside its validity range or would create an invalid Gregorian date, aValueError
exception is raised. For example:
>>> greg = GregorianCalendar(2002, 12, 31)
>>> print(greg.replace(day=26))
2002-12-26
>>> greg.replace(month=11) # November has 30 days
Traceback (most recent call last):
|
ValueError: Day must be between 1 and number of days in month, while it is 31.
-
GregorianCalendar.
__str__
()¶ Return a string representing the date with the ‘YYYY-MM-DD’ format. Years above 9999 are represented adding necessary figures. Negative years are represented prepending the minus sign. For example:
>>> str(GregorianCalendar(2002, 12, 4))
'2002-12-04'
>>> str(GregorianCalendar(-1, 1, 1))
'-0001-01-01'
-
GregorianCalendar.
cformat
(format)¶ Return a string representing the date, controlled by an explicit format string. The formatting directives are a subset of those accepted by
datetime.date.strftime()
, and their meaning does not depend on the underlying C library (i.e. there are no platform variations). The table below lists the accepted formatting directives, all other character are not interpreted.Directive Meaning Notes %a
Abbreviated weekday name. (1) %A
Full weekday name. (1) %b
Abbreviated month name. (1) %B
Full month name. (1) %d
Day of the month as a decimal number [01, 31]. %j
Day of the year as a decimal number [001, 366]. %m
Month as a decimal number [01, 12]. %U
Week number of the year (Sunday as the first day of the week) as a decimal number [00, 53]. All days in a new year preceding the first Sunday are considered to be in week 0. %w
Weekday as a decimal number [1 (Monday), 7 (Sunday)]. %W
Week number of the year (Monday as the first day of the week) as a decimal number [00, 53]. All days in a new year preceding the first Monday are considered to be in week 0. %y
Year without century as a decimal number [00, 99]. (2) %Y
Year with century as a decimal number. At least four figures will be returned. (3) %%
A literal '%'
character.
Notes:
- The
%a
,%A
,%b
and%B
directives return a localized name in Standard C++. This is not true fordatetime2
, which only returns English names. - Since this is a truncated representation, negative years will not have a sign.
- Negative years will have a trailing
'-'
.
Western time¶
An instance of the WesternTime
class represents a moment of a day as
generally done in western countries, dividing each day in 24 hours, each hour
in 60 minutes and each minute in 60 seconds.
The default western time constructor is:
-
class
datetime2.western.
WesternTime
(hour, minute, second, timezone=None)¶ Return an object that represents the moment of a day in hour, minute and second elapsed from midnight. This representation does not take into account the possibility of one or two additional seconds that sometimes are added in specific dates to compensate earth rotation. All arguments except
timezone
are required. The following requirements must be satisfied:hour
must be an integer and0 <= hour < 24
minute
must be an integer and0 <= minute < 60
second
must be a rational number and its value must be0 <= second < 60
timezone
, if present, must be a rational number and its value must be-24 <= timezone <= 24
Here a rational number is anything that can be passed to the
fractions.Fraction
constructor, i.e. an integer, a float, another Fraction, a Decimal number or a string representing an integer, a float or a fraction.If an argument is not of the accepted type, a
TypeError
exception is raised. If an argument is outside its accepted range, aValueError
exception is raised.The
timezone
argument, if present, makes the object aware and defines the number of hours that must be added to UTC to get local time.
Note
The timezone
parameter is likely to change its values in future.
A WesternTime
object has four attributes, all of which are read-only
numbers: an attempt to change them will raise an AttributeError
exception. These attributes store the corresponding values in the constructor:
-
WesternTime.
hour
¶ An integer with values between
0
and23
.
-
WesternTime.
minute
¶ An integer with values between
0
and59
.
-
WesternTime.
second
¶ A Python Fraction with value grater or equal to
0
and less than60
.
-
WesternTime.
timezone
¶ If this attribute is not
None
, it a Python Fraction with values between -24 and 24.
An instance of the WesternTime
class has the following methods:
-
WesternTime.
replace
(hour, minute, second, *, timezone)¶ Returns a new
WesternTime
object with the same value, except for those parameters given new values by whichever keyword arguments are specified. The value, if given, they must respect the same requirements of the default constructor, otherwise aTypeError
orValueError
exception is raised.timezone
parameter can be replaced only for aware instances. For example:
>>> my_time = WesternTime(19, 6, 29)
>>> print(my_time.replace(minute=38))
19:38:29
>>> my_time.replace(hour=24)
Traceback (most recent call last):
|
ValueError: Hour must be between 0 and 23, while it is 24.
>>> my_time.replace(timezone=1)
Traceback (most recent call last):
|
TypeError: Can replace timezone only in aware instances.
-
WesternTime.
__str__
()¶ For a naive instance, return a string representing the time with the ‘HH:MM:SS’ format. For an aware instance, the format is ‘HH:MM:SS+HH:MM’. The number of seconds in the time part and the number of minutes in the timezone part will be truncated. For example:
>>> str(WesternTime(12, 44, 14.8))
'12:44:14'
>>> str(WesternTime(12, 34, 56.7, timezone=12.256))
'12:34:56+12:15'
-
WesternTime.
cformat
(format)¶ Return a string representing the time, controlled by an explicit format string. The formatting directives are a subset of those accepted by
datetime.date.strftime()
, and their meaning does not depend on the underlying C library (i.e. there are no platform variations). The table below lists the accepted formatting directives, all other characters are not interpreted.Directive Meaning Notes %H
Hour (24-hour clock) as a zero-padded decimal number [00, 23]. %I
Hour (12-hour clock) as a zero-padded decimal number [01, 12]. %p
Returns ‘AM’ if hour is between 0 and 11, ‘PM’ if hour is between 12 and 23. (1) %M
Minute as a zero-padded decimal number [00, 59]. %S
Second as a zero-padded decimal number [00, 59]. %f
Microsecond as a decimal number, zero-padded on the left [000000, 999999]. %z
UTC offset in the form ±HHMM[SS[.ffffff]] (empty string if the object is naive). %%
A literal '%'
character.
Notes:
- The
%p
directive returns a localized string in Standard C++. This is not true fordatetime2
, which only returns the English string.
Western time interval¶
An instance of the WesternTimeDelta
class represents a time interval
given in days, hours, minutes and seconds.
The default constructor is:
-
class
datetime2.western.
WesternTimeDelta
(days, hours, minutes, seconds)¶ Return an object that represents a time interval in hours, minutes and seconds. All arguments are required.
hours
,minutes
andseconds
must have the same sign ofdays
. The types and absolute values of each parameter are listed below:days
must be an integer of any valuehours
must be an integer and its absolute value must be0 <= hours <= 23
minutes
must be an integer and its absolute value must be0 <= minutes <= 59
seconds
must be a rational number and its absolute value must be0 <= second < 60
Here a rational number is anything that can be passed to the
fractions.Fraction
constructor, i.e. an integer, a float, another Fraction, a Decimal number or a string representing an integer, a float or a fraction.If an argument is not of the accepted type, a
TypeError
exception is raised. If an argument is outside its accepted range or all parameter haven’t the same sign, aValueError
exception is raised.
A WesternTimeDelta
object has four attributes, all of which are
read-only numbers: an attempt to change them will raise an
AttributeError
exception. These attributes store the corresponding
values in the constructor:
-
WesternTime.
days
¶ An integer of any value.
-
WesternTime.
hours
¶ An integer of the same sign as
days
and with absolute value between0
and23
.
-
WesternTime.
minutes
¶ An integer of the same sign as
days
and with absolute value between0
and59
.
-
WesternTime.
seconds
¶ A Python Fraction of the same sign as
days
and with absolute value grater or equal to0
and less than60
.
An instance of the WesternTimeDelta
class has the following methods:
-
WesternTimeDelta.
replace
(days, hours, minutes, seconds)¶ Returns a new
WesternTimeDelta
object with the same value, except for those parameters given new values by whichever keyword arguments are specified. The value, if given, they must respect the same requirements of the default constructor, otherwise aTypeError
orValueError
exception is raised. For example:
>>> my_td = WesternTimeDelta(1, 23, 45, 6)
>>> print(my_td.replace(minutes=0))
1 day, 23 hours and 6 seconds
>>> my_td.replace(hours=24)
Traceback (most recent call last):
|
ValueError: Hours must be between 0 and 23, while it is 24.
>>> my_time.replace(secondsì'33')
Traceback (most recent call last):
|
ValueError: Seconds must be of the same sign of 'days'.
-
WesternTimeDelta.
__str__
()¶ Return a string representing the time interval. When a component of the interval is zero, it is not printed. The number of seconds will be truncated. For example:
>>> str(WesternTimeDelta(9, 8, 7, 6.5))
'9 days, 8 hours, 7 minutes and 6 seconds'
>>> str(WesternTimeDelta(0, 0, -5, -2))
'-5 minutes and -2 seconds'
-
WesternTimeDelta.
cformat
(format)¶ Return a string representing the time, controlled by an explicit format string. The formatting directives are a subset of those accepted by
datetime.date.strftime()
, and their meaning does not depend on the underlying C library (i.e. there are no platform variations). The table below lists the accepted formatting directives, all other characters are not interpreted.Directive Meaning %d
Full days as a decimal number. %H
Hours as a zero-padded decimal number [00, 23]. %M
Minutes as a zero-padded decimal number [00, 59]. %S
Seconds as a zero-padded decimal number [00, 59]. %f
Microseconds as a zero-padded decimal number [000000, 999999]. %%
A literal '%'
character.
datetime2.modern
- ISO calendar and Internet time¶
This module implements a calendar and a time representation that have been defined in the recent years:
- The ISO calendar
- The internet time representation where a day is divided in 1000 parts.
Of course, they all conform to the requirements for interface classes listed in Customization.
ISO calendar¶
The ISO calendar divides the days into weeks, from Monday to Sunday, and groups 52 or 53 whole weeks into a year. The first calendar week of a year is the one that includes the first Thursday of the corresponding Gregorian year. This definition can be seen also as: the first calendar weeks of a ISO year is the week including January, 4th Gregorian.
A good discussion of the ISO calendar can be read at The Mathematics of the ISO 8601 Calendar.
The constructor of an ISO calendar is:
-
class
datetime2.modern.
IsoCalendar
(year, week, day)¶ Return an object that represents the date given with ISO year, week number and day. All arguments are required and must be integers. Values for
week
andday
must lie in the following ranges:1 <= week <= number of weeks in the given year
1 <= day <= 7
If an argument is outside those ranges, a
ValueError
exception is raised. They day number goes from 1 (Monday) to 7 (Sunday).
An IsoCalendar
object has three attributes:
-
IsoCalendar.
year
¶
-
IsoCalendar.
week
¶
-
IsoCalendar.
day
¶ These attributes are read-only integer numbers. Week will be between 1 and the number of weeks in the ISO year (52 or 53), day will be between 1 and 7.
Two static method have been implmented to give details of an ISO year:
-
classmethod
IsoCalendar.
is_long_year
(year)¶ Return
True
if year is a long year, i.e. a year with 53 weeks, in the ISO calendar,False
otherwise. For example,IsoCalendar.is_leap_year(2004) == True
.
-
classmethod
IsoCalendar.
weeks_in_year
(year)¶ Return the number of weeks in a ISO year, either 52 or 53. For example,
IsoCalendar.weeks_in_year(2009) == 53
.
An instance of the IsoCalendar
class has the following methods:
-
IsoCalendar.
day_of_year
()¶ Return the day of the year as an integer, from 1 to 364 (in short years) or 371 (in long years). For example,
IsoCalendar(2008, 3, 1).day_of_year() == 62
.
-
IsoCalendar.
replace
(year, week, day)¶ Returns a new
IsoCalendar
object with the same value, except for those parameters given new values by whichever keyword arguments are specified. All values are optional; if used, they must be integers. If any argument is outside its validity range or would create an invalid Gregorian date, aValueError
exception is raised. For example:
>>> iso = IsoCalendar(2004, 53, 3)
>>> print(iso.replace(week=26))
2004-W26-3
>>> iso.replace(year=2003) # 2003 has 52 weeks
Traceback (most recent call last):
|
ValueError: Week must be between 1 and number of weeks in year, while it is 53.
-
IsoCalendar.
__str__
()¶ Return a string representing the date with the ‘YYYY-WWW-DD’ format. Years above 9999 are represented adding necessary figures. Negative years are represented prepending the minus sign. For example:
>>> str(IsoCalendar(2002, 12, 4))
'2002-W12-4'
>>> str(IsoCalendar(-1, 1, 1))
'-0001-W01-1'
-
IsoCalendar.
cformat
(format)¶ Return a string representing the ISO date, controlled by an explicit format string. The formatting directives are a subset of those accepted by
datetime.date.strftime()
, and their meaning does not depend on the underlying C library (i.e. there are no platform variations). The table below lists the accepted formatting directives, all other character are not interpreted.Directive Meaning Notes %a
Abbreviated weekday name. (1) %A
Full weekday name. (1) %j
Day of the year as a decimal number [001,371]. %w
Weekday as a decimal number [1 (Monday), 7 (Sunday)]. %W
Week number in the ISO year as a decimal number [01, 53]. %y
ISO year without century as a decimal number [00, 99]. (2) %Y
ISO year with century as a decimal number. At least four figures will be returned. (3) %%
A literal '%'
character.
Notes:
- The
%a
and%A
directives return a localized name in Standard C++. This is not true fordatetime2
, which only returns English names. - Since this is a truncated representation, negative years will not have a sign.
- Negative years will have a trailing
'-'
.
Internet time¶
The Internet Time (or beat time) is a decimal time concept introduced in 1998, marketed by a large Swiss watch company, and divides the day in 1000 parts, called “beats”. A beat is equivalent to 1 minute and 26.4 seconds. A Wikipedia article describes the Internet time. The Internet time is aware by definition.
The default constructor for Internet time is:
-
class
datetime2.modern.
InternetTime
(beat)¶ Return an object that represents the time in thousandths of a day. The
beat
argument is required and must be a anything that can be passed to thefractions.Fraction
constructor, i.e. an integer, a float, another Fraction, a Decimal number or a string representing an integer, a float or a fraction. Its value must be equal or greater than 0 and less than 1000. If the argument is not of one of the possible types, aTypeError
exception is raised. If the argument is outside its accepted range, aValueError
exception is raised.
An InternetTime
object has one attribute:
-
InternetTime.
beat
¶ This attribute is a read-only Python Fraction greater than or equal to 0 and less than 1000.
and the following methods:
-
InternetTime.
__str__
()¶ Return a string representing the moment of the day in beats, ‘@BBB’ format. For example:
>>> str(InternetTime(345.25))
'@345'
-
InternetTime.
cformat
(format)¶ Return a string representing the Internet time, controlled by an explicit format string with formatting directives close to that used in C. The table below lists the accepted formatting directives, all other character are not interpreted.
Directive Meaning Notes %b
Integer number of beats [000, 999]. %f
Thousandths of a beat, zero-padded on the left [000, 999]. (1)
Notes:
- One thousandth of a beat is a millionth of a day, i.e. 86.4 milliseconds.
datetime2 customization¶
Interface¶
Base classes of the datetime2
module have little if no practical
use as they natively are: even if it stands out for its simplicity,
rata die is not a common way of representing dates in the real world.
Same consideration can be done about time as a fraction of a day.
A mechanism based on attributes, here called “access” attributes, has been implemented to give access to a wide variety of calendars and time representations.
When used on the base class, the attribute behaves as a constructor of the base class, but with the arguments of the specific representation:
>>> d = Date.gregorian(2013, 4, 18)
>>> d
datetime2.Date(734976)
>>> t = Time.western(17, 16, 28)
>>> t
datetime2.Time('15547/21600')
When used on a base class instance, the attribute allows to see the instance using a specific representation, or to call methods defined for that specific representation:
>>> d = Date(1)
>>> str(d.gregorian)
'0001-01-01'
>>> d.gregorian.month
1
>>> d.gregorian.weekday()
1
>>> t = Time(Fraction(697, 1440))
>>> str(t.western)
'11:37:00'
>>> t.western.minute
37
The attribute gives access to what is called an “interface class”. The interface class is the one that manages converting a specific representation (e.g. the Gregorian calendar) to the base class. The Available calendars table lists all available interface classes for calendars. The Available time representations chapter lists all available interface classes for time.
The real power of this paradigm is that we can create a base class instance with an access attribute and see its value with another access attribute, or use different access attributes on the same base class instance. In this way, the base class object is unchanged, but it can bee seen in many different ways.
>>> d = Date.gregorian(2013, 4, 22)
>>> d.iso.week
17
>>> t = Time(0.5, utcoffset='-1/6')
>>> str(t.western)
'12:00:00'
>>> t.internet.beat
Fraction(625, 1)
An intended feature of datetime2
is that any representations is computed
only once, when first accessed, then remains available to the base class.
Interface class may have, as it is normal, additional constructors. E.g. the
GregorianCalendar
class has the GregorianCalendar.year_day()
and
GregorianCalendar.replace()
methods that return a
GregorianCalendar
instance. However, thanks to some magic explained
later, when such constructors are accessed via the attribute mechanisms on the
base class or an instance of it, constructors of the interface class return
instances of the base class instead, as shown in this example:
>>> greg = GregorianCalendar.year_day(2012, 366)
>>> greg
GregorianCalendar(2012, 12, 31)
>>> d1 = Date.gregorian.year_day(2012, 366)
>>> d1
datetime2.Date(734868)
>>> str(d1.gregorian)
'2012-12-31'
>>> d2 = d1.gregorian.replace(year = 2013, month = 7)
>>> d2
datetime2.Date(735080)
>>> str(d2.gregorian)
'2013-07-31'
And, as expected, static methods of the interface classes are unchanged even when invoked via access attribute:
>>> Date.gregorian.is_leap_year(2012)
True
Customization¶
Base classes provide a mechanism to register new interface classes at run-time. The same mechanism is indeed used to register already available interface classes at module import time. The interface class must respect a few simple requirements shown later.
Before examining these requisites in detail, let’s have a look at a simple example: we want to define a new calendar that defines each day by indicating the week number and the week day, counting the week of January 1st of year 1 as week 1 and so on. In addition, this new calendar has a non-default constructor that takes as argument also thousands of weeks:
>>> class SimpleWeekCalendar():
... def __init__(self, week, day):
... self.week = week
... self.day = day
... @classmethod
... def from_rata_die(cls, rata_die):
... return cls((rata_die - 1) // 7 + 1, (rata_die - 1) % 7 + 1)
... def to_rata_die(self):
... return 7 * (self.week - 1) + self.day
... def __str__(self):
... return 'W{}-{}'.format(self.week, self.day)
... @classmethod
... def with_thousands(cls, thousands, week, day):
... return cls(1000 * thousands + week, day)
...
>>> Date.register_new_calendar('week_count', SimpleWeekCalendar)
>>> d1 = Date.week_count(1, 1)
>>> d1
datetime2.Date(1)
>>> str(d1.gregorian)
'0001-01-01'
>>> d2 = Date.gregorian(2013, 4, 26)
>>> str(d2.week_count)
'W104998-5'
>>> d3 = Date.week_count.with_thousands(104, 998, 5)
>>> d2 == d3
True
As can be seen in the example, the new interface class completely ignores the way a base class instance works. The requirements for an interface class to be used by the registration module are:
- Have a non-default forward constructor, that creates an instance of the interface class using the base class attribute.
- Have a backward method that returns the base class attribute corresponding to the interface class value.
- All other non-default constructors and all methods returning an interface class instance must use the interface class default constructor.
Once the new interface class is ready, the call of a registration method of the base class does the magic.
Each datetime2
base class has a specific registration function.
Required methods also have names depending on the base class they are
registered to. The following table lists all these names:
Base Classes | ||||
---|---|---|---|---|
Date |
Time |
DateTime |
TimeDelta |
|
Registration function | register_new_calendar |
register_new_time |
TBD | register_new_time_interval |
Non-default constructor | from_rata_die |
from_time_pair |
TBD | from_fractional_days |
Conversion method | to_rata_die |
to_time_pair |
TBD | to_fractional_days |
These methods are detailed below:
-
classmethod
Date.
register_new_calendar
(access_attribute, CalendarInterface)¶ Register the
CalendarInterface
class to theDate
class, using theaccess_attribute
identifier to access it. Ifaccess_attribute
is already defined, anAttributeError
exception is raised. Ifaccess_attribute
isn’t a valid identifier, aValueError
exception is raised.CalendarInterface
must obey the requirements for thedatetime2
interface classes, otherwise aTypeError
exception is raised.
-
classmethod
Time.
register_new_time
(access_attribute, TimeInterface)¶ Register the
TimeInterface
class to theTime
class, using theaccess_attribute
identifier to access it. Ifaccess_attribute
is already defined, anAttributeError
exception is raised. Ifaccess_attribute
isn’t a valid identifier, aValueError
exception is raised.TimeInterface
must obey the requirements for thedatetime2
interface classes, otherwise aTypeError
exception is raised.
-
classmethod
TimeDelta.
register_new_time_interval
(access_attribute, TimeDeltaInterface)¶ Register the
TimeDeltaInterface
class to theTimeDelta
class, using theaccess_attribute
identifier to access it. Ifaccess_attribute
is already defined, anAttributeError
exception is raised. Ifaccess_attribute
isn’t a valid identifier, aValueError
exception is raised.TimeDeltaInterface
must obey the requirements for thedatetime2
interface classes, otherwise aTypeError
exception is raised.
-
classmethod
calendar_class.
from_rata_die
(day_count)¶ Return a calendar object that corresponds to the day identified by the given day count.
-
calendar_obj.
to_rata_die
()¶ Return a rata die value that corresponds to the day represented by the calendar instance.
-
classmethod
time_of_day_class.
from_time_pair
(day_frac, utcoffset)¶ Return a time of day object that corresponds to the moment identified by the given day fraction, possibly with reference to the given UTC distance.
-
time_of_day_obj.
to_time_pair
()¶ Return a tuple of two values: the first one is the day fraction corresponding to the moment of the day identified by the time object, the second is null for naive time objects, or is a fraction of day to be added to UTC to get the time of day object.
-
classmethod
time_interval_class.
from_fractional_days
(fractional_days)¶ Return a time interval object that corresponds to the interval identified by the given number of possibly fractional days.
-
time_interval_obj.
to_fractional_days
()¶ Return a possibly fractional number of days that corresponds to the time interval represented by the object.
Inner workings¶
At registration time, some magic needs to be performed to obtain the wanted results:
- A new class is created on the fly, inheriting from the interface class. The new class changes the default constructor so it returns a base class instance when called. Since all other constructors use the default one (see the requirements above), all constructors of the new class return a base class instance.
- A new attribute is added to the base class. This attribute is special because its semantic depend on whether it is called on the base class or on a base class instance. In the former case, it creates a new base class instance. In the latter case, it uses the methods corresponding to the registered interface class.
The latter is obtained by exploiting the standard attribute lookup mechanisms, implementing a context-dependent attribute retrieval. This is well described in Descriptor HowTo Guide:
- If the attribute is retrieved directly from the class (e.g. as in
Date.week_count(1, 1)
), the modified interface class (contained inDate.week_count
) is returned, so that when invoked with the interface class signature, it returns a base class instance. The modified interface class was created at registration time, so no additional time is required to create it. - If the attribute is retrieved from a base class instance, there are two
cases:
- The instance does not have the attribute: the attribute lookup mechanism
looks for it in the corresponding
Date
class definition, where it is found since it was created at registration time. The attribute is created and added to the instance by monkey patching, so the next time the interface class instance is returned as indicated below. - The instance already has the attribute, which is retrieved normally. Note that this attribute is an instance of the modified interface class, not of the original one.
- The instance does not have the attribute: the attribute lookup mechanism
looks for it in the corresponding
This quite complex implementation has a few advantages:
- Base class instances do not store access attributes unless they are retrieved.
- Modified interface classes are built at registration time, which happens only once per program invocation.
- The registration mechanism is common to built-in and custom calendars.
- Interface classes are completely independent from each other and from their use in base classes.