Chapter 10 Date, Time and Timezone

10.1 Library Import

All below are built-in libraries. There is a popular library named pytz not covered in this chapter as it is being deprecated. The replacement of pytz is ZoneInfo.

from datetime import date, datetime, timedelta, time
from zoneinfo import ZoneInfo

10.2 ZoneInfo

ZoneInfo is the recommended built-in library to deal with timezone for datetime object.

10.2.1 Constructor

Initialize with zone name as key: ZoneInfo(key='<zone_name'>).

MY = ZoneInfo('Asia/Kuala_Lumpur')
NY = ZoneInfo('America/New_York')
UTC= ZoneInfo('UTC')

MY
NY
UTC
## zoneinfo.ZoneInfo(key='Asia/Kuala_Lumpur')
## zoneinfo.ZoneInfo(key='America/New_York')
## zoneinfo.ZoneInfo(key='UTC')

10.2.2 Instance Attributes

MY.key
## 'Asia/Kuala_Lumpur'

10.3 Date and Datetime

This is a built-in library by Python. There is no need to install this library.

date and datetime are both standard built-in python library.

datetime library contain four object classes:

  • date: (year,month,day)
  • time: (hour,minute,second)
  • datetime: (year,month,day,hour,minute,second)
  • timedelta: duration between two datetime or date object

10.3.1 ISO8601

Refer ISO8601 in Wikipedia to understand the common ISO on date/datetime formatting.

UTC:    "2007-04-05T14:30Z"      #notice Z
GMT+8:  "2007-04-05T12:30+08:00  #notice +08:00
GMT+8:  "2007-04-05T12:30+0800   #notice +0800
GMT+8:  "2007-04-05T12:30+08     #notice +08```

### Date
2019-02-04 #notice no timezone available

10.3.2 Constructor

Use constructor to create datetime / date object. You can make it timezone aware with tzinfo parameter, which is an ZoneInfo object.

## Date Only
date      (2000,1,1)
type(date (2000,1,1))

## Datetime - TImezone Naive
datetime  (2000,1,1,23,15,55)
datetime  (year=2000,month=1,day=1,hour=23,minute=15,second=55)
type(datetime(2000,1,1,0,0,0))
## datetime.date(2000, 1, 1)
## <class 'datetime.date'>
## datetime.datetime(2000, 1, 1, 23, 15, 55)
## datetime.datetime(2000, 1, 1, 23, 15, 55)
## <class 'datetime.datetime'>

Timezone Integration

Datetime supports timezone through built-in library (refer ZoneInfo chapter). Initialize datetime with tzinfo parameter to have a timezone aware datetime.

MY = ZoneInfo('Asia/Kuala_Lumpur')
NY = ZoneInfo('America/New_York')
UTC= ZoneInfo('UTC')

## Timezone Aware
datetime  (2000,1,1,23,15,55, tzinfo=NY)
## datetime.datetime(2000, 1, 1, 23, 15, 55, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))

10.3.3 Class Methods

Now and Today

  • Both now() and today() return current system local datetime, which is timezone naive.
  • utcnow() returns UTC datetime, which is also timezone naive.
  • Only now() supports tz parameter to convert the time to desired timezone.
  • today() / utcnow()** does not supports **tz``` parameter.
datetime.now()         ## local datetime
datetime.today()       ## local datetime, same as above
datetime.utcnow()      ## UTC datetime
datetime.now(tz=NY)    ## convert to New York timezone
datetime.today(tz=NY)  ## Error
datetime.utcnow(tz=NY) ## Error
## datetime.datetime(2022, 12, 31, 12, 0, 9, 625769)
## datetime.datetime(2022, 12, 31, 12, 0, 9, 636771)
## datetime.datetime(2022, 12, 31, 4, 0, 9, 645774)
## datetime.datetime(2022, 12, 30, 23, 0, 9, 652001, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))
## Error in py_call_impl(callable, dots$args, dots$keywords): TypeError: datetime.today() takes no keyword arguments
## Error in py_call_impl(callable, dots$args, dots$keywords): TypeError: datetime.utcnow() takes no keyword arguments

Combine Data and Time (combine)

Apply datetime.combine() class method on both date and time objects to get datetime

now = datetime.now()
datetime.combine(now.date(), now.time())
## datetime.datetime(2022, 12, 31, 12, 0, 9, 875439)

Convert from String (strptime)

Use strptime to convert string into datetime object. Refer this cheatsheet for complete directives.

%I : 12-hour
%H : 24-hour
%M : Minute
%p : AM/PM
%y : 18
%Y : 2018
%b : Mar
%m : month (1 to 12)
%d : day
datetime.strptime('2011-02-25', '%Y-%m-%d')
datetime.strptime('9-01-18',    '%d-%m-%y')
datetime.strptime('09-Mar-2018','%d-%b-%Y')
datetime.strptime('2/5/2018 4:49 PM', '%m/%d/%Y %I:%M %p')
## datetime.datetime(2011, 2, 25, 0, 0)
## datetime.datetime(2018, 1, 9, 0, 0)
## datetime.datetime(2018, 3, 9, 0, 0)
## datetime.datetime(2018, 2, 5, 16, 49)

Convert from ISO (fromisoformat)

  • fromisoformat() is the reverse of isoformat().
  • It actually not ISO compliance: when Z or +8 is included at the end of the string, Error is triggered.
datetime.now().isoformat()                         ## checkout the iso format
datetime.fromisoformat("2019-02-05T10:22:33")      ## Good
datetime.fromisoformat("2019-02-05T10:22:33Z")     ## Error due to trailing 'Z'
datetime.fromisoformat("2019-02-05T10:22:33+0800") ## Errordue to trailing '+0800'
## '2022-12-31T12:00:10.354497'
## datetime.datetime(2019, 2, 5, 10, 22, 33)
## Error in py_call_impl(callable, dots$args, dots$keywords): ValueError: Invalid isoformat string: '2019-02-05T10:22:33Z'
## Error in py_call_impl(callable, dots$args, dots$keywords): ValueError: Invalid isoformat string: '2019-02-05T10:22:33+0800'

10.3.4 Instance Attributes

now = datetime.now(tz=MY)  ## initialize the instance
now.month
now.day
now.hour
now.minute
now.tzinfo                   ## this would be null if TZ parameter not specified
## 12
## 31
## 12
## 0
## zoneinfo.ZoneInfo(key='Asia/Kuala_Lumpur')

10.3.5 Instance Methods

Update Data(.replace)

Use replace() to change any parameters of the date/datetime during initialization.

birthday = datetime(1997,3,1,10,35,21)   ## replace for datetime
birthday
birthday.replace(tzinfo=NY, year=1998, month=5)

holiday = date(1999,8,31)                ## replace for date
holiday
holiday.replace(year=2000)
## datetime.datetime(1997, 3, 1, 10, 35, 21)
## datetime.datetime(1998, 5, 1, 10, 35, 21, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))
## datetime.date(1999, 8, 31)
## datetime.date(2000, 8, 31)

Timezone Conversion (astimezone)

  • Use astimezone(<zoneinfo_timezone>) to convert a datetime to the desired timezone
  • When applying this to a timezone naive datetime, the datetime is assumed to be local timezone for conversion. Observe the example below.
birthday = datetime(1997,3,1,10,35,21)  ## timezone naive, assumed to be local timezone for conversion
birthday.astimezone(NY)                 ## convert to NY time

birthday = datetime(1997,3,1,10,35,21, tzinfo=UTC)  ## timezone aware
birthday.astimezone(NY)                  ## convert to NY time
## datetime.datetime(1997, 2, 28, 21, 35, 21, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))
## datetime.datetime(1997, 3, 1, 5, 35, 21, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))

Convert to Time (.time)

datetime.now().time()
## datetime.time(12, 0, 11, 364837)

Convert to Date (.date)

datetime.now().date()
## datetime.date(2022, 12, 31)

Convert to String (strftime, isoformat)

Refer this cheatsheet for complete directives.

str( datetime.now() )
datetime.now().strftime('%d-%b-%Y')
datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ')  ## ISO 8601 UTC
datetime.utcnow().isoformat()
## '2022-12-31 12:00:11.743655'
## '31-Dec-2022'
## '2022-12-31T04:00:11.759281Z'
## '2022-12-31T04:00:11.759281'

Weekday

now = datetime.now()
now.weekday()
## 5

10.3.6 Instance Attributes

datetime.now().year
datetime.now().month
datetime.now().day
datetime.now().hour
datetime.now().minute
## 2022
## 12
## 31
## 12
## 0

10.3.7 Operators

Difference (-)

Use - to get the difference between two dates as timedelta object.

dt1 = datetime(2000,1,1,10,25,35)
dt2 = datetime(2000,1,2,14,30,55)

dt2 - dt1
dt1 - dt2
## datetime.timedelta(days=1, seconds=14720)
## datetime.timedelta(days=-2, seconds=71680)

Addition (+)

Adding timedelta to a datetime to get an adjusted datetime. Note that there can be negative adjustment to any parameters of timedelta.

delta = timedelta(days=5, hours=-5, minutes=30, seconds=10)
dt1   = datetime(2000,1,1,10,25,35)
delta  ## check the delta object
dt1    ## original datetime
dt1 + timedelta(days=5, hours=-5, minutes=30, seconds=10)  ## adjusted datetime
## datetime.timedelta(days=4, seconds=70210)
## datetime.datetime(2000, 1, 1, 10, 25, 35)
## datetime.datetime(2000, 1, 6, 5, 55, 45)

10.4 Time

10.4.1 Constructor

print( time(2) )    ## specify hour only, minutes and seconds default to 0
print( time(2,15) ) ## seconds default to 0
print( time(hour=2,minute=15,second=30) )
## 02:00:00
## 02:15:00
## 02:15:30

10.4.2 Instance Methods

t1 = time(2,15,30)
t2 = t1.replace(hour=5, minute=30, second=55)
t1.isoformat()
t2.isoformat()
## '02:15:30'
## '05:30:55'

10.4.3 Instance Attributes

t = time(2,15,30)
t.hour
t.minute
t.second
## 2
## 15
## 30

10.5 Timedelta

10.5.1 Constructor

  • Constructor does not support years argument.
delt = timedelta(days=365,minutes=33,seconds=15)

10.5.2 Using With Datetime

  • timedelta can be applied on on datetime object.
  • Timedelta Cannot be applied on time object , because it potentially go beyond single day (24H).
dt1  = datetime.now()
delt = timedelta(days=365,minutes=33,seconds=15)

delt
dt1
now + delt
## datetime.timedelta(days=365, seconds=1995)
## datetime.datetime(2022, 12, 31, 12, 0, 14, 748657)
## datetime.datetime(2023, 12, 31, 12, 33, 27, 30434)