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 the section called “Semantics: What is Time?”.
Semantics: What is Time?
The Gregorian calendar is extremely complex. Most 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.
Clearly, 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 the real root cause of our problems. 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.
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 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
[Deshowitz97].
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.dates, and
converting datetime.dates 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.datetimes,
and converting datetime.datetimes 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.
Published under the terms of the Open Publication License