Date and time in Python

Often, in our projects, we need to work with dates and times. For example, we may want to track the current date and time or see how long our code runs. For these purposes, we can use the datetime module.

The datetime module has several classes that make working with time easy:

  • datetime.date represents standard date;
  • datetime.time represents standard time, independent from the date;
  • datetime.timedelta represents the difference between two points in time;
  • datetime.tzinfo represents timezones;
  • datetime.datetime represents both time and date together.

Creating Date Objects

Initially, we should know a datetime object. Since datetime is both a module and a class inside that module, we’ll start by importing the datetime class from the datetime module.

At that point, we’ll print the current date and time to see what’s contained in a datetime object. We can do this utilizing datetime’s .now() function. We’ll print our datetime object, and afterward additionally print its type utilizing type().

from datetime import datetime

# get current date
datetime_obj = datetime.now()
print(datetime_obj)
print('Type : ',type(datetime_obj))

#output
#2020-08-01 16:09:30.266459
#Type :  <class 'datetime.datetime'>

We can see from the results above that datetime_obj is indeed a datetime object of the datetime class. This includes the year, month, day, hour, minute, second, and microsecond.

datetime.date

Calendar date values are represented with the date class. Instances have attributes for year, month, and day. It is simple to create a date representing the current date utilizing the today() class method.

import datetime
today = datetime.date.today()
print(today)
print('ctime  :', today.ctime())
tt = today.timetuple()
print('tuple  : tm_year  =', tt.tm_year)
print('         tm_mon   =', tt.tm_mon)
print('         tm_mday  =', tt.tm_mday)
print('         tm_hour  =', tt.tm_hour)
print('         tm_min   =', tt.tm_min)
print('         tm_sec   =', tt.tm_sec)
print('         tm_wday  =', tt.tm_wday)
print('         tm_yday  =', tt.tm_yday)
print('         tm_isdst =', tt.tm_isdst)
print('ordinal:', today.toordinal())
print('Year   :', today.year)
print('Mon    :', today.month)
print('Day    :', today.day)

There are likewise class methods for creating instances from POSIX timestamps or whole numbers representing date values from the Gregorian calendar, where January 1 of the year 1 will be 1 and each resulting day increases the value by 1.

import datetime
import time

o = 733114
print('o               :', o)
print('fromordinal(o)  :', datetime.date.fromordinal(o))

t = time.time()
print('t               :', t)
print('fromtimestamp(t):', datetime.date.fromtimestamp(t))

The result will be

As with time, the range of date values supported can be determined using the min and max attributes.

import datetime

print('Earliest  :', datetime.date.min)
print('Latest    :', datetime.date.max)
print('Resolution:', datetime.date.resolution)

The resolution of dates should be whole numbers

Another way to create new date instances uses the replace() method of an existing date.

import datetime

d1 = datetime.date(2020, 7, 29)
print('d1:', d1.ctime())

d2 = d1.replace(year=2019)
print('d2:', d2.ctime())

datetime.time

Time values are represented to with the time class. A time instance has attributes for hour, minute, second, and microsecond and can likewise include time zone data.

import datetime

t = datetime.time(1, 2, 3)
print(t)
print('hour       :', t.hour)
print('minute     :', t.minute)
print('second     :', t.second)
print('microsecond:', t.microsecond)
print('tzinfo     :', t.tzinfo)

A time instance only holds values of time, and not a date associated with the time.

import datetime

print('Earliest  :', datetime.time.min)
print('Latest    :', datetime.time.max)
print('Resolution:', datetime.time.resolution)

The resolution for time is limited to whole microseconds.

import datetime

for m in [1, 0, 0.1, 0.6]:
    try:
        print('{:02.1f} :'.format(m),
              datetime.time(0, 0, 0, microsecond=m))
    except TypeError as err:
        print('ERROR:', err)

datetime.timedelta

Future and past dates can be determined utilizing fundamental arithmetic on two datetime objects, or by combining a datetime with a timedelta. Subtracting dates produces a timedelta, and a timedelta can be included or deducted from a date to create another date. The internal values for a timedelta are stored in days, seconds, and microseconds.

import datetime

print('microseconds:', datetime.timedelta(microseconds=1))
print('milliseconds:', datetime.timedelta(milliseconds=1))
print('seconds     :', datetime.timedelta(seconds=1))
print('minutes     :', datetime.timedelta(minutes=1))
print('hours       :', datetime.timedelta(hours=1))
print('days        :', datetime.timedelta(days=1))
print('weeks       :', datetime.timedelta(weeks=1))

The full duration of a timedelta can be retrieved as a number of seconds using total_seconds().

import datetime

for delta in [datetime.timedelta(microseconds=1),
              datetime.timedelta(milliseconds=1),
              datetime.timedelta(seconds=1),
              datetime.timedelta(minutes=1),
              datetime.timedelta(hours=1),
              datetime.timedelta(days=1),
              datetime.timedelta(weeks=1),
              ]:
    print('{:15} = {:8} seconds'.format(
        str(delta), delta.total_seconds())
    )

datetime.datetime

The datetime.datetime class is a sort of combination of the date and time classes. Similarly to those two, it assumes the current Gregorian calendar and that there are exactly 86 400 seconds in each day.

The constructor of the datetime.datetime objects takes the following parameters:

import datetime
 
# necessary parameters
datetime.datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None)

The year, month and day parameters are required, others are optional. All arguments (except tzinfo) should be integers and just like in real life, their values are restricted:

  • datetime.MINYEAR (1) ≤ year ≤ datetime.MAXYEAR (9999)
  • 1 ≤ month ≤ 12
  • 1 ≤ day ≤ number of days in this month and year
  • 0 ≤ hour < 24
  • 0 ≤ minute < 60
  • 0 ≤ second < 60
  • 0 ≤ microsecond < 1000000.

The tzinfo argument can be an instance of the datetime.tzinfo class, but its default value is None, so we don’t need to worry about it here.

To see how this all works, let’s create a datetime.datetime object.

import datetime
 
vostok_1 = datetime.datetime(2019, 4, 12, 6, 7)
print(vostok_1)

#output
#2019-04-12 06:07:00

datetime methods

The datetime.datetime class has a few helpful methods.

If you have to get the current time and date, there are two methods you can utilize: datetime.datetime.today() and datetime.datetime.now(tz=None). They are fundamentally the same as and the main difference between these two methods is that datetime.datetime.now() has a keyword argument tz. If you don’t specify it, the two methods work the equivalent. Be that as it may, at times or on certain stages, the datetime.datetime.now() method might be progressively exact.

This is a case of how they perform:

print(datetime.datetime.now()) 
print(datetime.datetime.today())

#output
#2020-08-01 20:48:56.506217
#2020-08-01 20:48:56.506718

You can also transform a datetime.datetime object to a datetime.time or datetime.date objects using datetime.datetime.time() or datetime.datetime.date() methods respectively:

print(vostok_1.time())  
print(vostok_1.date()) 

#output
#06:07:00
#2019-04-12

date arithmetic

Date math uses the standard arithmetic operators.

import datetime

today = datetime.date.today()
print('Today    :', today)

one_day = datetime.timedelta(days=1)
print('One day  :', one_day)

yesterday = today - one_day
print('Yesterday:', yesterday)

tomorrow = today + one_day
print('Tomorrow :', tomorrow)

print()
print('tomorrow - yesterday:', tomorrow - yesterday)
print('yesterday - tomorrow:', yesterday - tomorrow)

A timedelta object also supports arithmetic with integers, floats, and other timedelta instances.

import datetime

one_day = datetime.timedelta(days=1)
print('1 day    :', one_day)
print('5 days   :', one_day * 5)
print('1.5 days :', one_day * 1.5)
print('1/4 day  :', one_day / 4)

work_day = datetime.timedelta(hours=7)
meeting_length = datetime.timedelta(hours=1)
print('meetings per day :', work_day / meeting_length)

Comparing values

Both date and time values can be compared using the standard comparison operators to determine which is earlier or later.

import datetime
import time

print('Times:')
t1 = datetime.time(12, 55, 0)
print('  t1:', t1)
t2 = datetime.time(13, 5, 0)
print('  t2:', t2)
print('  t1 < t2:', t1 < t2)

print()
print('Dates:')
d1 = datetime.date.today()
print('  d1:', d1)
d2 = datetime.date.today() + datetime.timedelta(days=1)
print('  d2:', d2)
print('  d1 > d2:', d1 > d2)

Combining dates and times

Utilize the datetime class to hold values comprising of both date and time components. Similarly as with date, there are a few convenient class methods to make creating datetime instances from other normal values.

import datetime

print('Now    :', datetime.datetime.now())
print('Today  :', datetime.datetime.today())
print('UTC Now:', datetime.datetime.utcnow())
print()

FIELDS = [
    'year', 'month', 'day',
    'hour', 'minute', 'second',
    'microsecond',
]

d = datetime.datetime.now()
for attr in FIELDS:
    print('{:15}: {}'.format(attr, getattr(d, attr)))

Just as with date, datetime provides convenient class methods for creating new instances. It also includes fromordinal() and fromtimestamp().

import datetime

t = datetime.time(1, 2, 3)
print('t :', t)

d = datetime.date.today()
print('d :', d)

dt = datetime.datetime.combine(d, t)
print('dt:', dt)

Formatting and parsing

The default string representation of a datetime object uses the ISO-8601 format (YYYY-MM-DDTHH:MM:SS.mmmmmm). Alternate formats can be generated using strftime().

import datetime

format = "%a %b %d %H:%M:%S %Y"

today = datetime.datetime.today()
print('ISO     :', today)

s = today.strftime(format)
print('strftime:', s)

d = datetime.datetime.strptime(s, format)
print('strptime:', d.strftime(format))

The same formatting codes can be used with Python’s string formatting mini-language by placing them after the : in the field specification of the format string.

import datetime

today = datetime.datetime.today()
print('ISO     :', today)
print('format(): {:%a %b %d %H:%M:%S %Y}'.format(today))

Each datetime format code must still be prefixed with %, and subsequent colons are treated as literal characters to include in the output.

Each datetime format code must still be prefixed with %, and subsequent colons are treated as literal characters to include in the output.

The following table demonstrates all formatting code:

Date and Time Formatting in Python

strptime/strftime format codes

Symbol

Meaning

Example

%a

Abbreviated weekday name ‘Wed’
%A Full weekday name ‘Wednesday’
%w Weekday number – 0 (Sunday) through 6 (Saturday) ‘3’
%d Day of the month (zero padded) ’13’
%b Abbreviated month name ‘Jan’
%B Full month name ‘January’
%m Month of the year ’01’
%y Year without century ’16’
%Y Year with century ‘2016’
%H Hour from 24-hour clock ’17’
%I Hour from 12-hour clock ’05’
%p AM/PM ‘PM’
%M Minutes ’00’
%S Seconds ’00’
%f Microseconds ‘000000’
%z UTC offset for time zone-aware objects ‘-0500’
%Z Time Zone name ‘EST’
%j Day of the year ‘013’
%W Week of the year ’02’
%c Date and time representation for the current locale ‘Wed Jan 13 17:00:00 2016’
%x Date representation for the current locale ’01/13/16′
%X Time representation for the current locale ’17:00:00′
%% A literal % character ‘%’

Time Zones

Inside datetime, time regions are represented to by sub-classes of tzinfo. Since tzinfo is a abstract base class, applications need to define a subclass and give proper usage to a couple of methods to make it useful.

datetime includes some naive implementation in the class timezone that utilizes a fixed offset from UTC, and doesn’t support distinctive offset values on various days of the year, for example, where daylight savings time applies, or where the offset from UTC has changed after some time.

import datetime

min6 = datetime.timezone(datetime.timedelta(hours=-6))
plus6 = datetime.timezone(datetime.timedelta(hours=6))
d = datetime.datetime.now(min6)

print(min6, ':', d)
print(datetime.timezone.utc, ':',
      d.astimezone(datetime.timezone.utc))
print(plus6, ':', d.astimezone(plus6))

d_system = d.astimezone()
print(d_system.tzinfo, '      :', d_system)