datetime2 Documentation Release 0.7.6dev3

Francesco Ricciardi

Jun 16, 2020

Contents

1 Indices and tables 5 1.1 Base classes...... 5 1.2 ...... 9 1.3 of ...... 13 1.4 datetime2 customization...... 16

Python Module Index 21

Index 23

i ii datetime2 Documentation, Release 0.7.6dev3

A in is independent from the way it is represented in different cultures. There are indeed many calendars in which the same day is represented in different ways. The datetime2 module detaches operations on time or objects from their representation, and also allows to add other representations at run time. The module does all this in an efficient and syntactically clear way. A very simple internal representation has been chosen so that it becomes easy to convert this internal representation to the many different calendars. The idea, inspired by the excellent book “”2, is to identify each day by simply counting the days starting with 1 for January 1st of 1, 2 for January 2nd is day 2, and so on. Using the the datetime2 module we write:

>>> d1= Date(1) # January 1st, 1 >>> d2= Date(737109) # February 19th, 2019

However entering dates in this way is nearly useless, but the module comes in help. E.g. we can use the Gregorian or the ISO calendar:

>>> d3= Date.gregorian(1965,3,1) # Gregorian: 1965-03-01 >>> d4= Date.iso(2011,1,1) # ISO: 2011-W01-1

Similarly, each Date object can be printed with the internal representation:

>>> print(d3) 717396 >>> print(d4) 734140

And again, this way of using the day is nearly useless. But you can use attributes also on Date objects to access different representations:

>>> print(d1.gregorian) 0001-01-01 >>> print(d2.gregorian) 2019-02-19 >>> print(d4.gregorian) 2011-01-03 >>> print(d1.iso) 0001-W01-1 >>> print(d2.iso) 2019-W08-2 >>> print(d3.iso) 1965-W09-1

As you can see, it is not required to use the same calendar with which an object has been created. Each calendar also presents different attributes that can be accessed as usual. These may be components of the date, like in:

>>> print(d2.gregorian.year) 2019 >>> print(d3.iso.) 9

But also other date attributes:

>>> print(d3.gregorian.weekday()) 1 (continues on next page)

2 “Calendrical Calculations: The Ultimate Edition”, E. M. Reingold, N. Dershowitz, Cambridge University Press, 2018, and all its previous versions

Contents 1 datetime2 Documentation, Release 0.7.6dev3

(continued from previous page) >>> print(d3.gregorian.day_of_year()) 60

Finally, Date objects can often be created with other constructors:

>>> print(Date.gregorian.year_day(2020, 120).gregorian) # the 120th day of 2020 2020-04-29

Although there exist much less time representations, the datetime2 module uses the same reasoning to represent the time of the day: a moment of the day is represented as a fraction of the day, starting from midnight. In the following examples we use the Time, a subdivision of days in 10 of 100 , each being 100 .

>>> t1= Time((7, 10)) >>> print(t1.western) 16:48:00 >>> print(t1.decimal) 7:00:00

As with dates, also time can be entered and printed with different representations, and also the time of day representa- tions have attributes, methods and alternate constructors:

>>> t2= Time.western(15, 47, 16) >>> t3= Time.decimal(4, 83, 18) >>> print(t2.decimal) 6:55:06 >>> print(t3.western) 11:35:46 >>> print(t3.western.minute) 35 >>> print(t3.western.to_hours()) Fraction(72477, 6250) >>> t4= Time.western.in_minutes(1000) >>> print(t4) 25/36 of a day >>> print(t4.western) 16:40:00 >>> print(t4.decimal) 6:94:44

The reference between time objects can be either implicit time, i.e. depending on implementation (it usually is the local time,but can also be UTC). It is also possible to have an explicit reference to UTC, passed as an additional parameter to the object constructor. In the first case the Time object is said to be “naive”, in the case it is said to be “aware”. As in dates and time classes, also the time to UTC is indicated by a culturally independent value, i.e. a fraction of a day. Additional time representations can support naive references, or are implicitly aware.

>>> t5= Time('2/3', to_utc=(1, 12)) >>> print(t8) 2/3 of a day, 1/12 of a day to UTC >>> print(t5.western) 16:00:00+02 >>> t6= Time.internet(456) >>> print(t6.western) 10:56:03+01

2 Contents datetime2 Documentation, Release 0.7.6dev3

The Internet time is by definition on Basel . This is equivalent to central Europe time zone, but doesnt have . Other prominent features of the datetime2 module are the ability to add other representations at run time and the fact that representations do not consume memory unless they are effectively used (this is especially important for calendars, where many representation exists1 ). Currently (version 0.7.6dev3) the following calendars and time representations are available:

Module Calendar(s) Time representation(s) datetime2.western Gregorian Western datetime2.modern ISO Internet

See also: Module datetime Basic date and time types. Module calendar General calendar related functions. Module time Time access and conversions.

1 Well, this should be read as “will exist”, since current version (0.7.6dev3) only has two of them.

Contents 3 datetime2 Documentation, Release 0.7.6dev3

4 Contents CHAPTER 1

Indices and tables

1.1 Base classes

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.

1.1.1 Date objects

A Date object represents a date in an idealized calendar, just counting 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 Date(day_count) Return an object that represent a date which is day_count - 1 days after January 1:sup:st of year 1 of the current . The argument is required and must be an integer; there is no restriction on its numeric value. Using any other type of parameter, a TypeError exception is raised. classmethod Date.today() Return a Date object that represents the current local date. Date instances are immutable, so they can be used as dictionary keys. They can also be pickled and unpickled. In boolean contexts, all Date instances are considered to be true. 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 has one instance method:

5 datetime2 Documentation, Release 0.7.6dev3

Date.__str__() Return R.D. followed by the day count. R.D. stands for Rata Die, the Latin for “fixed date”. The following table lists all available calendars and the attributes by which they are reachable:

Calendar Access attribute Calendar class Module Gregorian gregorian GregorianCalendar datetime2.western ISO iso IsoCalendar datetime2.modern

Supported operations

Operation Result date2 = date1 + date2 is timedelta days after date1. Reverse addition (timedelta + timedelta date1) is allowed. (1) (2) date2 = date1 - date2 is timedelta days before date1. (1) (3) timedelta timedelta = date1 - A TimeDelta object is returned representing the number of days between date1 date2 and date2. (4) date1 < date2 date1 is less than date2 when it represents a day earlier that that of date2. (5) (6)

Notes: (1)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 from DateTime instances. (2) If timedelta is negative, date2 will be before date1. (3) If timedelta is negative, date2 will be after date1. (4) The timedelta instance created when subtracting Date instances will always have an integral number of days, positive if date1 is later than date2, negative otherwise. (5) In other words, date1 < date2 if and only if date1.day_count < date2.day_count. All other comparison operators (<=, >, >=, == and !=) behave similarly. (6) When comparing a Datee object and an object of another class, if the latter has a day_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 a day_count attribute, if the operator is equality(==) or inequality(!=), the value returned is always False and True respectively. If the operator is one of the other four (<=, >, >= or ==), a TypeError exception is raised.

1.1.2 Time objects

Warning: This version of the documentation is under revision for the part concerning the correction of local time for UTC. Code and tests about this part are under development.

An indication of time, independent of any particular day, expressed as a fraction of day. There might be an indication of time difference to 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:

6 Chapter 1. Indices and tables datetime2 Documentation, Release 0.7.6dev3

class Time(day_frac, *, to_utc=None) class Time(numerator, denominator, *, to_utc=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 to be added to get UTC, for whatever political, algorithmic or geographic need (e.g. time zone). This indication is given in the to_utc argument, which must be explicitly named. The day_frac and to_utc arguments can be 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. In addition, as in a Fraction object, two values can be used to represent numerator and denominator of the fraction. This is possible only for the day_frac argument, not for to_utc. The day_frac argument is stored in a read-only attribute with the same name. In addition to the types listed above, the to_utc argument can also be an object that has a to_utc method returning a fractions. Fraction value. When a Time instance is created giving an indication of time to UTC, one of the two following cases can happen: • to_utc is a fractional value, expressed in one of the possibilities above. This value is stored in the to_utc attribute. • to_utc is an object that has a to_utc method. This method is called and its value is stored in the to_utc read-only attribute. In any case, the resulting value for day_frac must be equal or greater than 0 and less than 1. The resulting value for to_utc must be equal or greater than -1 and less or equal to 1. A ValueError exception is raised if the resulting value are outside these ranges. A TypeError exception is raised if the type of any argument is not one of the accepted types. A ZeroDivisionError exception is raised if the denominator is 0. classmethod Time.now(to_utc=None) Return an aware Time object that represents the current time. Without argument, the time represented in day_frac will be local , to_utc will be set to the difference between UTC and local standard time. If to_utc is given, the returned object will be the current time at the given time difference from UTC. to_utc will be treated as in the default constructor. classmethod Time.localnow() Return a naive Time object that represents the current local standard time. classmethod Time.utcnow() Return a naive Time object that represents the current standard UTC. 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. Time instances have two read-only attributes: an AttributeError exception is raised when trying to change any of them. Time.day_frac A Python fractions.Fraction that represents the part of the day after midnight. The value is given as a fraction of a day. Time.to_utc If not None, this attribute is a Python fractions.Fraction that represents the fraction of a day that must be added to current time to get UTC. The value is given as a fraction of a day. Time has two instance methods:

1.1. Base classes 7 datetime2 Documentation, Release 0.7.6dev3 time.__str__() Return the string of a day, where fraction is the value of the day_frac attribute. Time correction, if , is represented as well:

>>> t1= Time((4, 12)) >>> print(t1) 1/3 of a day >>> t2= Time((3, 24), to_utc=(-4, 24)) >>> print(t2) 1/8 of a day, -1/6 of a day to UTC time.relocate(new_to_utc) Applicable only to aware instances, return another Time instance that identifies the same moment, but at a different time distance from UTC. The new_to_utc argument has the same meaning as in the default creator. If called on a naive instance, a TypeError exception is raised. Example:

>>> t1= Time(0.25, to_utc=-0.5) >>> print(t1) 1/4 of a day, -1/2 of a day to UTC >>> t2= t1.relocate(0.25) >>> print(t2) 1/2 of a day, 1/4 of a day to UTC

The following table lists all available time representations and the attributes by which they are reachable:

Representation Attribute Time representation class Module Western western WesternTime datetime2.western Internet internet InternetTime datetime2.modern

Supported operations

Operation Result time2 = time1 + time2 is timedelta time after time1. Reverse addition (timedelta + time1) is timedelta allowed. (1) (2) time2 = time1 - time2 is timedelta time before time1. (1) (3) timedelta timedelta = A TimeDelta object is returned representing the day fraction between time1 and time2. time1 - time2 (4) time1 < time2 time1 is less than time2 when the former represents a moment earlier than the latter. Time correction, if present, is taken into consideration. (5) (6)

Notes: (1) 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: if time1 has a correction, this will be copied to time2. (2) If timedelta is negative, time2 will be before time1. (3) If timedelta is negative, time2 will be after time1. (4) The timedelta object created when subtracting two Time instances will always represent a fractional part of a day, either positive or negative. time1 and time2 must have the same naivety; if they don’t, a ValueError exception is raised. If they are aware, correction of both instances will be taken into account to generate the

8 Chapter 1. Indices and tables datetime2 Documentation, Release 0.7.6dev3

result. Result will be more than -1 and less than 0 if time1 is after than time2, or between 0 and 1 if time1 is before than time2. (5) All other comparison operators (<=, >, >=, == and !=) behave similarly. (6) If both objects to be compared are Time instances, they must have the same naivety; if they don’t, a ValueError exception is raised. When comparing a Time object and an object of another class, if the latter has a day_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. When the second object doesn’t have a day_frac attribute, if the operator is equality(==) or inequal- ity(!=), the value returned is always False and True respectively. If the operator is one of the other four (<=, >, >= or ==), a TypeError exception is raised.

1.2 Calendars

This chapter lists the calendars classes available in the datetime2 package. Of course, they all conform to the requirements for interface classes listed in Customization. As such, they all have the six standard comparison operators: <, >, ==, >=, <=, and !=, which return a meaningful result when comparing calendar objects of the same type. When comparing a calendar object with an object of a different type, the == and != operators always consider them to be unequal, while the <, >, >= and <= operators raise a TypeError exception. Description of the comparison operators and interface methods is then omitted from the calendar class descriptions below.

1.2.1 Gregorian calendar

An instance of the GregorianCalendar class represents a day in the calendar as generally done in western coun- tries. It is a dividing day count in of 365 or 366 days, each year is then divided in 12 of 28 (or 29), 30 and 31 days. The default constructor for a Gregorian day is: class GregorianCalendar(year, , 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 and day 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 a ValueError exception is raised. A GregorianCalendar object has three attributes: GregorianCalendar.year GregorianCalendar.month

1.2. Calendars 9 datetime2 Documentation, Release 0.7.6dev3

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 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 con- vention for weekdays, not the one used by datetime.date.weekday(), where Monday is 0 and Sunday is 6. GregorianCalendar.day_of_year() Return the day of the year as an integer, 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, a ValueError 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.

10 Chapter 1. Indices and tables datetime2 Documentation, Release 0.7.6dev3

Di- Meaning Notes rec- tive %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 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: (1) The %a, %A, %b and %B directives return a localized name in Standard C++. This is not true for datetime2, which only returns English names. (2) Since this is a truncated representation, negative years will not have a sign. (3) Negative years will have a trailing '-'.

1.2.2 ISO calendar

The ISO calendar divides the days into , 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 and day 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.

1.2. Calendars 11 datetime2 Documentation, Release 0.7.6dev3

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, a ValueError 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:

12 Chapter 1. Indices and tables datetime2 Documentation, Release 0.7.6dev3

(1) The %a and %A directives return a localized name in Standard C++. This is not true for datetime2, which only returns English names. (2) Since this is a truncated representation, negative years will not have a sign. (3) Negative years will have a trailing '-'.

1.3 Time of day

This chapter lists the time representations classes available in the datetime2 package. Of course, they all conform to the requirements for interface classes listed in Customization. As such, they all have the six standard comparison operators: <, >, ==, >=, <=, and !=, which return a meaningful result when comparing calendar objects of the same type. When comparing a calendar object with an object of a different type, the == and != operators always consider them to be unequal, while the <, >, >= and <= operators raise a TypeError exception. Description of the comparison operators and interface methods is then omitted from the time of day class descriptions below. In the following we will call a rational number anything that can be passed to the fractions.Fraction con- structor, i.e. an integer, a float, another Fraction, a Decimal number or a string representing an integer, a float or a fraction. In addition, it is also possible to use a 2-value tuple with integer values. This tuple represents the numerator and denominator of a fraction that will be passed to the fractions.Fraction constructor.

1.3.1 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 in 60 minutes and each minute in 60 seconds. There are four constructors for a western time. The default one is: class western.WesternTime(hour, minute, second, to_utc=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 rotation. All arguments are required and must satisfy the following requirements: • hour must be an integer and 0 <= month <= 23 • minute must be an integer and 0 <= minute <= 59 • second must be a rational number and its value must be 0 <= second < 60 • to_utc, if present, must be a rational number and its value must be -24 <= to_utc <= 24 If an argument is not of the accepted type, a TypeError exception is raised. If an argument is outside its accepted range, a ValueError exception is raised. The to_utc argument, if present, makes the object aware and defines the number of hours that must be added to it to get UTC time. The other three constructors are: class WesternTime.in_hours(hour, to_utc=None) Return an object that represents the moment of the day specified in hours, possibly fractional, elapsed from midnight. The argument must be a rational number, otherwise a TypeError exception is raised. Its value must be greater or equal to 0 and less than 24, otherwise a ValueError exception is raised.

1.3. Time of day 13 datetime2 Documentation, Release 0.7.6dev3

The to_utc argument, if present, makes the object aware and defines the number of hours that must be added to it to get UTC time. It must be a rational number and its value must be -24 <= to_utc <= 24 class WesternTime.in_minutes(minute, to_utc=None) Return an object that represents the moment of the day specified in minutes, possibly fractional, elapsed from midnight. The argument must be a rational number, otherwise a TypeError exception is raised. Its value must be greater or equal to 0 and less than 1440, otherwise a ValueError exception is raised. The to_utc argument, if present, makes the object aware and defines the number of hours that must be added to it to get UTC time. It must be a rational number and its value must be -24 <= to_utc <= 24 class WesternTime.in_seconds(second, to_utc=None) Return an object that represents the moment of the day specified in seconds, possibly fractional, elapsed from midnight. The argument must be a rational number, otherwise a TypeError exception is raised. Its value must be greater or equal to 0 and less than 86400, otherwise a ValueError exception is raised. The to_utc argument, if present, makes the object aware and defines the number of hours that must be added to it to get UTC time. It must be a rational number and its value must be -24 <= to_utc <= 24 A WesternTime object has four attributes: western.hour western.minute western.second These attributes are read-only numbers. The first two are integers; the last one is a Python Fraction. The three attributes will respect the value requirements listed in the default constructor description. western.to_utc If this attribute is not None, it is the number of hours that must be added the object’s time to it to get UTC time An instance of the WesternTime class has the following methods: western.to_hours() Return a Python Fraction representing the moment of the day in hours. Thus the returned value will be equal or greater than 0, and less than 24. western.to_minutes() Return a Python Fraction representing the moment of the day in minutes. Thus the returned value will be equal or greater than 0, and less than 1440. western.to_seconds() Return a Python Fraction representing the moment of the day in seconds. Thus the returned value will be equal or greater than 0, and less than 86400. western.replace(hour, minute, second) Returns a new WesternTime 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 respect the requirements of the default constructor, otherwise a TypeError or ValueError exception is raised. 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. western_time.__str__() Return a string representing the time with the ‘HH:MM:SS’ format. Any decimal will be truncated from the number of seconds. For example:

14 Chapter 1. Indices and tables datetime2 Documentation, Release 0.7.6dev3

>>> str(WesternTime(12, 44, 14.8)) '12:44:14' western_time.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 character are not interpreted.

Directive Meaning Notes %H Hour (24-hour ) 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]. %% A literal '%' character.

Notes: (1) The %p directive returns a localized string in Standard C++. This is not true for datetime2, which only returns the English string.

1.3.2 Internet time

The Internet Time (or beat time) is a decimal time concept introduced in 1998, marketed by a large Swiss company, and divides the day in 1000 parts, called “beats”. A beat is equivalent to 1 minute and 26.4 seconds. A Wikipedia article well describes the Internet time. The default constructor for Internet time is: class InternetTime(beat) Return an object that represents the time in thousandths of a day. The beat argument is required and must be a rational number; its value must be equal or greater than 0 and less than 1000. If the argument is not a Python number, a TypeError exception is raised. If the argument is outside its accepted range, a ValueError exception is raised. An InternetTime object has one attribute: internet_time.beat This attribute is a read-only Python Fraction greater than or equal 0 and less than 1000. and the following methods: internet_time.__str__() Return a string representing the moment of the day in beats, ‘@BBB’ format. For example:

>>> str(InternetTime(345.25)) '@345' internet_time.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.

1.3. Time of day 15 datetime2 Documentation, Release 0.7.6dev3

Directive Meaning Notes %b Integer number of beats [000, 999]. %f Thousandths of a beat, zero-padded on the left [000, 999]. (1)

Notes: (1) One thousandth of a beat is a millionth of a day, i.e. 86.4 .

1.4 datetime2 customization

1.4.1 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:

>>> 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 with a specific representation, or tu use 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 atttribute 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 Calendars chapter lists all available interface classes for calendars. The Time of day 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 (continues on next page)

16 Chapter 1. Indices and tables datetime2 Documentation, Release 0.7.6dev3

(continued from previous page) 17 >>> t= Time(0.5) >>> str(t.western) '12:00:00' >>> t.internet.beat Fraction(500, 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 ac- cessed 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:

>>> 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

1.4.2 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 (continues on next page)

1.4. datetime2 customization 17 datetime2 Documentation, Release 0.7.6dev3

(continued from previous page) ... 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: These methods are detailed below: classmethod Date.register_new_calendar(access_attribute, CalendarInterface) Register the CalendarInterface class to the Date class, using the access_attribute identifier to access it. If access_attribute is already defined, an AttributeError exception is raised. If access_attribute isn’t a valid identifier, a ValueError exception is raised. CalendarInterface must obey the requirements for the datetime2 interface classes, otherwise a TypeError exception is raised. classmethod Time.register_new_time(access_attribute, TimeInterface) Register the TimeInterface class to the Time class, using the access_attribute identifier to access it. If access_attribute is already defined, an AttributeError exception is raised. If access_attribute isn’t a valid identifier, a ValueError exception is raised. CalendarInterface must obey the requirements for the datetime2 interface classes, otherwise a TypeError exception is raised. calendar_obj.to_rata_die() Return a rata die value that corresponds to the day represented by the calendar instance.

1.4.3 Inner workings

At registration time, some magic needs to be performed to obtain the wanted results:

18 Chapter 1. Indices and tables datetime2 Documentation, Release 0.7.6dev3

• A new class in created on the fly, inheriting from the interface class. The new class changes the default con- structor 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 in Date.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. 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. • genindex

1.4. datetime2 customization 19 datetime2 Documentation, Release 0.7.6dev3

20 Chapter 1. Indices and tables Python Module Index

d datetime2,1

21 datetime2 Documentation, Release 0.7.6dev3

22 Python Module Index Index

Symbols is_leap_year() (GregorianCalendar static method), __str__() (Date method),5 10 __str__() (GregorianCalendar method), 10 is_long_year() (datetime2.modern.IsoCalendar __str__() (datetime2.modern.IsoCalendar method), class method), 12 12 IsoCalendar (class in datetime2.modern), 11 __str__() (internet_time method), 15 __str__() (time method),7 L __str__() (western_time method), 14 localnow() (Time class method),7 B M beat (internet_time attribute), 15 minute (western attribute), 14 month (GregorianCalendar attribute),9 C cformat() (datetime2.modern.IsoCalendar method), N 12 now() (Time class method),7 cformat() (GregorianCalendar method), 10 cformat() (internet_time method), 15 P cformat() (western_time method), 15 Python Enhancement Proposals D PEP 8,5 Date (built-in class),5 R datetime2 (module),1 register_new_calendar() (Date class method), day (datetime2.modern.IsoCalendar attribute), 11 18 day (GregorianCalendar attribute),9 register_new_time() (Time class method), 18 day_count (Date attribute),5 relocate() (time method),8 day_frac (Time attribute),7 replace() (datetime2.modern.IsoCalendar method), day_of_year() (datetime2.modern.IsoCalendar 12 method), 12 replace() (GregorianCalendar method), 10 day_of_year() (GregorianCalendar method), 10 replace() (western method), 14 days_in_year() (GregorianCalendar static method), 10 S G second (western attribute), 14 GregorianCalendar built-in class ( ),9 T H Time (built-in class),6,7 hour (western attribute), 14 to_hours() (western method), 14 to_minutes() (western method), 14 I to_rata_die() (calendar_obj method), 18 InternetTime (built-in class), 15 to_seconds() (western method), 14

23 datetime2 Documentation, Release 0.7.6dev3

to_utc (Time attribute),7 to_utc (western attribute), 14 today() (Date class method),5 U utcnow() (Time class method),7 W week (datetime2.modern.IsoCalendar attribute), 11 weekday() (GregorianCalendar method), 10 weeks_in_year() (datetime2.modern.IsoCalendar class method), 12 western.WesternTime (built-in class), 13 WesternTime.in_hours (built-in class), 13 WesternTime.in_minutes (built-in class), 14 WesternTime.in_seconds (built-in class), 14 Y year (datetime2.modern.IsoCalendar attribute), 11 year (GregorianCalendar attribute),9 year_day() (GregorianCalendar class method),9

24 Index