Dates and Times: the time and datetime Modules

When processing dates and times, we have a number of problems. Most of these problems stem from the irregularities and special cases in the units we use to measure time. We generally measure time in a number of compatible as well as incompatible units. For example, weeks, days, hours, minutes and seconds are generally compatible, with the exception of leap-second handling. Months, and years, however are incompatible with days and require sophisticated conversion.

Problems which mix month-oriented dates and numbers of days are particularly difficult. The number of days between two dates, or a date which is 90 days in the future are notoriously difficult to compute correctly.

We need to represent a point in time, a date, a time of day or a date-time stamp. We need to be able to do arithmetic on this point in time. And, we need to represent this point in time as a properly-punctuated string.

The time module contains a number of portable functions needed to format times and dates. The datetime module builds on this to provide a representation that is slightly more convenient for some things. We’ll look at the definition of a moment in time in Semantics: What is Time?.

Semantics: What is Time?

The Gregorian calendar is extremely complex. Some of that complexity stems from trying to impose a fixed “year” on the wobbly, irregular orbit of our planet. There are several concesssions required to impose a calendar year with integer numbers of days that will match the astronomial year of approximately 365.2425 days. The Gregorian calendar’s concession is the periodic addition of a leap day to approximate this fractional day. The error is just under .25, so one leap day each four years gets close to the actual duration of the year.

Some additional complexity stems from trying to break the year into a sequence of months. Fixed-length months don’t work well because the year is 73 × 5 days long: there aren’t many pleasant factors that divide this length. If the year were only 360 days long, we’d be able to create fixed-length months. The Gregorian calendar’s concession to the individibility of the year is 12 months of 28, 29, 30 or 31 days in length.

We have several systems of units available to us for representing a point in time.

  • Seconds are the least common denominator. We can easily derive hours and minutes from seconds. There are 24 × 60 × 60 = 86,400 seconds in a day. (Astronomers will periodically add a leap second, so this is not absolutely true.) We can use seconds as a simple representation for a point in time. We can pick some epoch and represent any other point in time as the number of seconds after the epoch. This makes arithmetic very simple. However, it’s hard to read; what month contains second number 1,190,805,137?
  • Days are another common denominator in the calendar. There are seven days in a week, and (usually) 86,400 seconds in day, so those conversions are simple. We can pick some epoch and represent any other point in time as the number of days after the epoch. This also makes arithmetic very simple. However, it’s hard to read; what month contains day number 732,945?
  • Months are serious problem. If we work with the conventional date triple of year, month, and day, we can’t compute intervals between dates very well at all. We can’t locate a date 90 days in the future without a rather complex algorithm.

Local Time. We also to have acknowledge the subtlety of local time and the potential differences between local standard time and local daylight time (or summer time). Since the clock shifts, some time numbers (1:30 AM, for example) will appear to repeat, this can require the timezone hint to decode the time number.

The more general solution is to do all work in UTC. Input is accepted and displayed in the current local time for the convenience of users. This has the advantage of being timezone neutral, and it makes time values monotonically increasing with no confusing repeats of a given time of day during the hour in which the clock is shifted.

The time Solution. The time module uses two different representations for a point in time, and provides numerous functions to help us convert back and forth between the two representations.

  • A float seconds number. This is the UNIX internal representation for time. (The number is seconds past an epoch; the epoch happens to January 1st, 1970.) In this representation, a duration between points in time is also a float number.
  • A struct_time object. This object has nine attributes for representing a point in time as a Gregorian calendar date and time. We’ll look at this structure in detail below. In this representation, there is no representation for the duration between points in time. You need to convert back and forth between struct_time and seconds representations.

The datetime Solution. The datetime module contain all of the objects and methods required to correctly handle the sometimes obscure rules for the Gregorian calendar. Additionally, it is possible to use date information in the datetime to usefully conver among the world’s calendars. For details on conversions between calendar systems, see Calendrical Calculations [Dershowitz97].

The datetime module has just one representation for a point in time. It assigns an ordinal number to each day. The numbers are based on an epochal date, and algorithms to derive the year, month and day information for that ordinal day number. Similarly, this class provides algorithms to convert a calendar date to an ordinal day number. (Note: the Gregorian calendar was not defined until 1582, all dates before the official adoption are termed proleptic. Further, the calendar was adopted at different times in different countries.)

There are four classes in this module that help us handle dates and times in a uniform and correct manner. We’ll skip the more advanced topic of the datetime.tzinfo class.

datetime.time
An instance of datetime.time has four attributes: hour, minute, second and microsecond. Additionally, it can also carry an instance of tzinfo which describes the time zone for this time.
datetime.date
An instance of datetime.date has three attributes: year, month and day. There are a number of methods for creating datetime.date objects, and converting datetime.date objectss to various other forms, like floating-point timestamps, 9-tuples for use with the time module, and ordinal day numbers.
datetime.datetime
An instance of datetime.datetime combines datetime.date and datetime.time. There are a number of methods for creating datetime.datetime objects, and converting datetime.datetime s to various other forms, like floating-point timestamps, 9-tuples for use with the time module, and ordinal day numbers.
datetime.timedelta
A datetime.timedelta is the duration between two dates, times or datetimes. It has a value in days, seconds and microseconds. These can be added to or subtracted from dates, times or datetimes to compute new dates, times or datetimes.

Some Class Definitions

A time.struct_time object behaves like an object as well as a tuple. You can access the attributes of the structure by position as well as by name. Note that this class has no methods of it’s own; you manipulate these objects using functions in the time module.

tm_year:The year. This will be a full four digit year, e.g. 1998.
tm_mon:The month. This will be in the range of 1 to 12.
tm_mday:The day of the month. This will be in the range of 1 to the number of days in the given month.
tm_hour:The hour of the day, in the range 0 to 23.
tm_min:The minutes of the hour, in the range 0 to 59.
tm_sec:The seconds of the minute, in the range 0 to 61 because leap seconds may be included. Not all platforms support leap seconds.
tm_wday:The day of the week. This will be in the range of 0 to 6. 0 is Monday, 6 is Sunday.
tm_yday:The day of the year, in the range 1 to 366.
tm_isdst:Is the time in local daylight savings time. 0 means that this is standard time; 1 means daylight time. If you are creating this object, you can provide -1; the time.mktime() can then determine DST based on the date and time.

We’ll focus on the datetime.datetime class, since it includes datetime.date and datetime.time. This class has the following attributes.

MINYEAR:
MAXYEAR:

These two attributes bracket the time span for which datetime works correctly. This is year 1 to year 9999, which covers the forseeable future as well as a proleptic past the predates the invention of the Gregorian calendar in 1582.

min:
max:

The earliest and laterst representable datetimes. In effect these are datetime(MINYEAR, 1, 1, tzinfo=None) and (MAXYEAR, 12, 31, 23, 59, 59, 999999, tzinfo=None).

resolution:

The smallest differences between datetimes. This is typically equaly to timedelta(microseconds=1).

year:

The year. This will be a full four digit year, e.g. 1998. It will always be between MINYEAR and MAXYEAR, inclusive.

month:

The month. This will be in the range 1 to 12.

day:

The day. This will be in the range 1 to the number of days in the given month.

hour:

The hour. This will be in the range 0 to 23.

minute:

The minute. This will be in the range 0 to 59.

second:

The second. This will be in the range 0 to 59.

microsecond:

The microsecond (millionths of a second). This will in the range 0 to 999,999. Some platforms don’t have a system clock which is this accurate. However, the SQL standard imposes this resolution on most date time values.

To get fractions of a second, you’ll compute second+microsecond/1000000.

tzinfo:

The datetime.tzinfo object that was provided to the initial datetime.datetime constructor. Otherwise it will be None.

Creating a Date-Time

There are two use cases for creating date , time, struct_time or datetime instances. In the simplest case, we’re asking our operating system for the current date-time or the date-time associated with some resource or event. In the more complex case, we are asking a user for input (perhaps on an interactive GUI, a web form, or reading a file prepared by a person); we are parsing some user-supplied values to see if they are a valid date-time and using that value.

From The OS. We often get time from the OS when we want the current time, or we want one of the timestamps associated with a system resource like a file or directory. Here’s a sampling of techniques for getting a date-time.

time.time() → float

Returns the current moment in time as a float seconds number. See File Handling Modules for examples of getting file timestamps; these are always a float seconds value. We’ll often need to convert this to a struct_time or datetime object so that we can provide formatted output for users.

The functions time.localtime() or time.gmtime() will convert this value to a struct_time. The class methods datetime.datetime.fromtimestamp(), and datetime.datetime.utcfromtimestamp() will create a datetime object from this time value.

Then, we can use time.strftime() or time.asctime() to format and display the time.

time.ctime() → string
Returns a string representation of the current time. These values aren’t terribly useful for further calculation, but they are handy, standardized timestamp strings.
time.asctime() → string
Returns a string representation of the current time. These values aren’t terribly useful for further calculation, but they are handy, standardized timestamp strings.
time.localtime() → struct_time

When evaluated with no argument value, this will create a struct_time object from the current time. Since we can’t do arithmetic with these values, we often need to convert them to something more useful.

This time is converted to localtime using your current locale settings.

time.gmtime() → struct_time

When evaluated with no argument value, this will create a struct_time object from the current time. Since we can’t do arithmetic with these values, we often need to convert them to something more useful.

The time.mktime() function will convert the struct_time to a float seconds time.

We have to use the datetime.datetime constructor to create a datetime from a struct_time. This can be long-winded, it will look like datetime.date( ts.tm_year, ts.tm_month, ts.tm_day ).

datetime.datetime.today() → datetime
datetime.datetime.now() → datetime
datetime.datetime.utcnow() → datetime

All of these are class methods of the datetime class; the create a datetime object. The today() function uses the simple time.time() notion of the current moment and returns local time. The now() function may use a higher-precision time, but it will be local time. The utcnow() function uses high-precision time, and returns UTC time, not local time.

We can’t directly get a float seconds number number from a datetime value. However, we can do arithmetic directly with datetime values, making the float seconds value superflous.

We can get the struct_time value from a datetime, using the timetuple() or utctimetuple() method functions of the datetime object.

Getting Time From A User. Human-readable time information generally has to be parsed from one or more string values. Human-readable time can include any of the endless variety of formats in common use. This will include some combination of years, days, months, hours, minutes and seconds, and timezone names.

There are two general approaches to parsing time. In most cases, it is simplest to use datetime.strptime() to parse a string and create a datetime object. In other cases, we can use time.strptime(). In the most extreme case, we have to either use the re module (Complex Strings: the re Module), or some other string manipulation, and then create the date-time object directly.

datetime.datetime.strptime(string[, format]) → datetime

This function will use the given format to attempt to parse the input string. If the value doesn’t match the format, it will raise a ValueError exception. If the format is not a complete datetime, then defaults are filled in. The default year is 1900, the default month is 1 the default day is 1. The default time values are all zero.

We’ll look at the format string under the time.strftime() function, below.

time.strptime(string[, format]) → struct_time

This function will use the given format to attempt to parse the input string. If the value doesn’t match the format, it will raise a ValueError exception. If the format is not a complete time, then defaults are filled in. The default year is 1900, the default month is 1 the default day is 1. The default time values are all zero.

We’ll look at the format string under the time.strftime() function, below.

time.struct_time(9-tuple) → struct_time

Creates a struct_time from a 9-valued tuple: (year, month, day, hour, minute, second, day of week, day of year, dst-flag). Generally, you can supply 0 for day of week and day of year. The dst flag is 0 for standard time, 1 for daylight (or summer) time, and -1 when the date itself will define if the time is standard or daylight.

This constructor does no validation; it will tolerate invalid values. If we use the time.mktime() function to do a conversion, this may raise an OverflowError if the time value is invalid.

Typically, you’ll build this 9-tuple from user-supplied inputs. We could parse a string using the re module, or we could be collecting input from fields in a GUI or the values entered in a web-based form. Then you attempt a time.mktime() conversion to see if it is valid.

datetime.datetime(year, month, day[, hour, minute, second, microsecond, timezone]) → datetime

Creates a datetime from individual parameter values. Note that the time fields are optional; if omitted the time value is 0:00:00, which is midnight.

This constructor will not tolerate a bad date. It will raise a ValueError for an invalid date.

Date-Time Calculations and Manipulations

There are two common classes of date-time calculations:

  • Duration or interval calculations in days (or seconds), where the month, week and year boundaries don’t matter. The time representation as a single floating-point number of seconds works well for this. Also, the datetime provides a datetime.timedelta that works well for this.
  • Calendar calculations where the month, week of month and day of week matter. The time representation as a 9-element struct_time structure works well for this. Generally, we use datetime.datetime for this.

Duration or interval calculations in days (or seconds). In this case, the month, week and year boundaries don’t matter. For example, the number of hours, days or weeks between two dates doesn’t depend on months or year boundaries. Similarly, calculating a date 90 days in the future or past doesn’t depen on month or year considerations. Even the difference between two times is properly a date-time calculation so that we can allow for rollover past midnight.

We have two ways to do this.

  • We can use float seconds information, as produced by the time module. When we’re using this representation, a day is 24 \times 60 \times 60 = 86,400 seconds, and a week is 7 \times 24 \times 60 \times 60 = 604,800 seconds. For the following examples, t1 and t2 and float seconds times.

    (t2-t1)/3600 is the number of hours between two times.

    (t2-t1)/86400 is the days between two dates.

    t1+90*86400 is the date 90 days in the future.

  • We can also use datetime objects for this, since datetime objects correctly handle arithmetic operations. When we’re using this representation, we’ll also work with datetime.timedelta objects; these have days, seconds and microseconds attributes. For the following examples, t1 and t2 and datetime objects.

    In a relatively simple case, the hours between two datetimes is (t2-t1).seconds/3600. This works when we’re sure that there sill be less than 24 hours between the two datetimes.

    In the more general case, we have a two-part calculation: First we get the timedelta between two datetimes with td = t2-t1.

    td= (t2-t1); seconds= td.days*86400+td.seconds is seconds between two dates.

    td= (t2-t1); secdonds= td.days*86400+td.seconds+td.microseconds/1000000.0 is seconds down to the datetime.resolution.

    td= (t2-t1); seconds= td.days+td.seconds/86400.0 is days between two dates.

Calendar calculations where the month, week of month and day of week matter. For example, the number of months between two dates rarely involves the day of the month. A date that is 3 months in the future, will land on the same day of the month, except in unusual cases where it would be the 30th of February. For these situations, highly problem-specific rules have to be applied; there’s no general principle.

We have two ways to do this.

  • We can use struct_time objects as produced by the time module. We can replace any struct_time fields, and possibly create an invalid date. We may need to use time.mktime() to validate the resulting struct_time object. In the following examples, t1 is a struct_time object.

    Adding some offset in months, correctly allowing for year-end rollover, is done as follows.

    monthSequence= (t1.tm_year*12 + t1.tm_mon-1) + offset
    futureYear, futureMonth0 = divmod( monthSequence, 12 )
    t1.tm_year= futureYear
    t1.tm_month= futureMonth0 + 1
    
  • We can also use datetime objects for this. In this case, we we’ll use the replace() method to replace a value in a datetime with other values. In the following examples, t1 is a datetime object.

    Adding some offset in months, correctly allowing for year-end rollover, is done as follows.

    monthSequence= (t1.year*12 + t1.month-1) + offset
    futureYear, futureMonth0 = divmod( monthSequence, 12 )
    t1= t1.replace( year=futureYear, month=futureMonth0+1 )
    

The following methods return information about a given datetime object. In the following definitions, dt is a datetime object.

datetime.datetime.date() → date
Return a new date object from the date fields of this datetime object.
datetime.datetime.time() → date
Return a new time object from the time fields of this datetime object.
datetime.datetime.replace([year, month, day, hour, minute, second, microsecond]) → datetime
Return a new datetime object from the current datetime object after replacing any values provided by the keyword arguments.
datetime.datetime.toordinal() → int
Return the ordinal day number for this datetime. This a unique day number.
datetime.datetime.weekday() → int
Return the day of the week. Monday = 0, Sunday = 6.
datetime.datetime.isoweekday() → int
Return the ISO day of the week. Monday = 1, Sunday = 7.
datetime.datetime.isocalendar() → int
Return a tuple with ( ISO year, ISO week, ISO week day ).

Presenting a Date-Time

To format human-readable time, we have a number of functions in the time module, and methods of a datetime object. Here are the functions in the time module.

time.strftime(format, time_struct) → string

Convert a struct_time to a string according to a format specification. The specification rules are provided below.

This is an example of how to produce a timestamp with the fewest implicit assumptions.

time.strftime( "%x %X", time.localtime( time.time() ) )

This line of code shows a standardized and portable way to produce a time stamp. The time.time() function produces the current time in UTC (Coordinated Universal Time). Time is represented as a floating point number of seconds after an epoch.

time.asctime(time_struct) → string
Convert a struct_time to a string, e.g. ‘Sat Jun 06 16:26:11 1998’. This is the same as a the format string "%a %b %d %H:%M:%S %Y".
time.ctime(seconds) → string
Convert a time in seconds since the Epoch to a string in local time. This is equivalent to asctime(localtime(seconds)).

A datetime object has the following methods for producting formatted output. In the following definitions, dt is a datetime object.

datetime.datetime.isoformat([separator]) → string

Return string representing this date in ISO 8601 standard format. The separator string is used between the date and the time; the default value is “T”.

>>> d= datetime.datetime.now()
>>> d.isoformat()
'2009-11-08T09:34:17.945641'
datetime.datetime.ctime() → string
Return string representing this date and time. This is equivalent to time.ctime(time.mktime( dt.timetuple())) for some datetime object, dt.
datetime.datetime.strftime(format) → string
Return string representing this date and time, formatted using the given format string. This is equivalent to time.strftime( format, time.mktime( dt.timetuple() ) for some datetime object, dt.

Formatting Symbols

The time.strftime() and time.strptime() functions use the following formatting symbols to convert between time_struct or datetime.datetime and strings.

Formatting symbols like %c, %x and %X produce standard formats for whole date-time stamps, dates or time. Other symbols format parts of the date or time value. The following examples show a particular date (Satuday, August 4th) formatted with each of the formatting strings.

Time Formatting Symbols
"%a" Locale’s 3-letter abbreviated weekday name. ‘Sat’
"%A" Locale’s full weekday name. ‘Saturday’
"%b" Locale’s 3-letter abbreviated month name. ‘Aug’
"%B" Locale’s full month name. ‘August’
"%c" Locale’s appropriate full date and time representation. ‘Saturday August 04 17:11:20 2001’
"%d" Day of the month as a 2-digit decimal number. ‘04’
"%H" Hour (24-hour clock) as a 2-digit decimal number. ‘17’
"%I" Hour (12-hour clock) as a 2-digit decimal number. ‘05’
"%j" Day of the year as a 3-digit decimal number. ‘216’
"%m" Month as a 2-digit decimal number. ‘08’
"%M" Minute as a 2-digit decimal number. ‘11’
"%p" Locale’s equivalent of either AM or PM. ‘pm’
"%S" Second as a 2-digit decimal number. ‘20’
"%U" Week number of the year (Sunday as the first day of the week) as a 2-digit decimal number. All days in a new year preceding the first Sunday are considered to be in week 0. ‘30’
"%w" Weekday as a decimal number, 0 = Sunday. ‘6’
"%W" Week number of the year (Monday as the first day of the week) as a 2-digit decimal number. All days in a new year preceding the first Monday are considered to be in week 0. ‘31’
"%x" Locale’s appropriate date representation. ‘Saturday August 04 2001’
"%X" Locale’s appropriate time representation. ‘17:11:20’
"%y" Year without century as a 2-digit decimal number. ‘01’
"%Y" Year with century as a decimal number. ‘2001’
"%Z" Time zone name (or ‘’ if no time zone exists). ‘’
"%%" A literal ‘%’ character. ‘%’

Time Exercises

  1. Return on Investment. Return on investment (ROI) is often stated on an annual basis. If you buy and sell stock over shorter or longer periods of time, the ROI must be adjusted to be a full year’s time period. The basic calculation is as follows:

    Given the sale date, purchase date, sale price, sp, and purchase price, pp.

    Compute the period the asset was held: use time.mktime() to create floating point time values for sale date, s, and purchase date, p. The weighting, w, is computed as

    w = (86400*365.2425) / ( s - p )
    

    Write a program to compute ROI for some fictitious stock holdings. Be sure to include stocks held both more than one year and less than one year. See Stock Valuation in Classes for some additional information on this kind of calculation.

  2. Surface Air Consumption Rate. When doing SACR calculations (see Surface Air Consumption Rate, and Dive Logging and Surface Air Consumption Rate) we’ve treated the time rather casually. In the event of a night dive that begins before midnight and ends after midnight the next day, our quick and dirty time processing doesn’t work correctly.

    Revise your solution to use a more complete date-time stamp for the start and end time of the dive. Use the time module to parse those date-time stamps and compute the actual duration of the dive.

Additional time Module Features

Here are some additional functions in the time module.

time.sleep(seconds)
Delay execution for a given number of seconds. The argument may be a floating point number for subsecond precision.
time.accept2dyear(arg)
If non-zero, 2-digit years are accepted. 69-99 is treated as 1969 to 1999, 0 to 68 is treated as 2000 to 2068. This is 1 by default, unless the PYTHONY2K environment variable is set; then this variable will be zero.
time.timezone

Difference in seconds between UTC and local time. Often a multiple of 3600 (all US timezones are in whole hours).

For example, if your locale is US Eastern Time, this is 18000 (5 hours).

time.altzone

Difference in seconds between UTC and local alternate time (“Daylight Savings Time”). Often a multiple of 3600 (US time zones are in whole hours).

For example, if your locale was set to US Eastern Time, this would be 14400 (4 hours).

time.daylight
Non-zero if the current locale uses daylight savings time. Zero if it does not.
time.tzname
The name of the timezone.

Table Of Contents

Previous topic

Complex Strings: the re Module

Next topic

File Handling Modules

This Page