Introduction
Date and time manipulation is handled in Python through it's datetime module. The module is built into Python so you only have to import it to begin using it. 
import datetime
There are a number of classes that ship with the datetime module. We will focus on the most commonly used ones:
- datetime.datewith attributes- year,- month, and- day.
- datetime.timewith attributes- hour,- minute,- second,- microsecondand- tzinfo.
- datetime.datetimea combination of- dateand- timewith attributes- year,- month,- day,- hour,- minute,- second,- microsecondand- tzinfo
- datetime.timedeltaa duration between two instances of- date,- time, or- datetimeup to a microsecond resolution.
The module also ships with two additional classes for working with timezones; datetime.timezone and datetime.tzinfo which we won't cover in this tutorial.
In practise you will most likely work with the datetime.datetime class most often as it incorporates both the date and time classes into a single class.
Working with the Date Class
The date object represents a date (year, month,day) using the current Gregorian calendar indefinitely extended in both directions. 
datetime.date(year,month,day)
You can specify a particular date in the past and future or use the today() method to return the local current date.
import datetime
past = datetime.date(year=1992,month=2,day=23)
print(f"A date in the past was {past}")
today = datetime.date.today()
print(f"Today's date is {today}")
Output
'A date in the past was 1992-02-23'
"Today's date is 2022-06-09"
Working with the Time Class
The datetime.time class has the following attributes.
datetime.time(hour,minute,second,microsecond)
from datetime import time
atime = time(hour=12,minute=34,second=15)
print(str(atime))
Output
'12:34:15'
You can also call the attributes of the specified time:
print(f"The hour is {atime.hour}.")
print(f"The microsecond is {atime.microsecond}.")
Output
'The hour is 12.'
'The microsecond is 0.' # not specified so defaults to 0
Working with the Datetime Class
The datetime.datetime class encompasses both the functionality of the datetime.date and datetime.time class. Its principle attributes are shown below.
datetime.datetime(year,month,day,hour=0,minute=0,second=0,microsecond=0,tzinfo=None)
The year, month and day attributes are required, and the rest are optional.
The Current Date and Time
The current date and time can be accessed either by the now() method or the today() method. The difference between the two is that today() will always return the local datetime with tzinfo=None while now() can be used with or without a timezone parameter.
The function now() is the preferred method for extracting the current datetime. 
import datetime
# Print the current datetime
now = datetime.datetime.now()
print(str(now))
print(type(now))
'2022-06-08 18:44:29.404011'
<class 'datetime.datetime'>
The attributes of the datetime.datetime object can be called in the same way as the datetime.date and datetime.time classes.
Formatting the Datetime with .strftime
The date, time, and datetime classes all support a strftime(format) object which creates a string from a datetime object in the format specified by the developer. This allows a user to explicitly define how they would like the date or time to be represented when printed or written out.
import datetime
now = datetime.datetime.now()
now.strftime(format) # format code is a string %letter
# Refer to tables below for a selection of formats
Refer to the official Python documentation for a list of all the format codes that can be applied. The tables below shows some of the more popular formats which you may want to use.
Date Related Format Codes
| Format Code | Description | Example | 
|---|---|---|
| %Y | Year (YYYY) | 2022 | 
| %y | Year (YY) | 22 | 
| %m | Month (00 - 12) | 10 | 
| %B | Month, Full Name | August | 
| %b | Month, Abbreviated Name | Aug | 
| %d | Day of the Month | 03 | 
| %A | Weekday Spelled Out | Tuesday | 
| %a | Weekday, Abbreviated Name | Tues | 
| %w | Weekday as a Number (Sun 0 - Sat 6) | 2 | 
| %W | Week Number of the Year (Mon 1st day of week) | 24 | 
| %U | Week Number of the Year (Mon 1st day of week) | 24 | 
Time Related Format Codes
| Format Code | Description | Example | 
|---|---|---|
| %H | Hour (00 - 23) | 08 | 
| %I | Hour (00 -12) | 05 | 
| %p | AM/PM | AM | 
| %M | Minute (00 - 59) | 42 | 
| %S | Second (00 - 59) | 12 | 
| %f | Microsecond (1e6 in 1 sec) | 526412 | 
Preformatted Date and Time Codes
| Format Code | Description | Example | 
|---|---|---|
| %x | Local Version of the Date (MM/DD/YYYY) | 12/25/2022 | 
| %X | Local Version of the Time (hh:mm:ss) | 15:23:45 | 
| %c | Local Version of Date and Time | Wed Jun 8 18:58:46 2022 | 
String Formatting Examples
Some examples using the strftime() object are shown below. 
>>> import datetime
>> now = datetime.datetime.now()
>>> print(now.strftime('%c'))
"Wed Jun  8 18:44:29 2022"
# Can combine format codes to arrive at the desired result
>>> written_date = now.strftime('%d %B %Y')
>>> print(f"The date today is {written_date}.")
"The date today is 08 June 2022."
>>> print(f"Another way to represent the date is: {now.strftime('%x')}.")
"Another way to represent the date is: 06/08/22."
>>> print(f"Some prefer the date convention (dd/mm/yyyy): {now.strftime('%d/%m/%Y')}.")
"Some prefer the date convention (dd/mm/yyyy): 08/06/2022."
>>> print(f"The current time is {now.strftime('%X')}")
"The current time is 18:58:46"
Convert from a String to a Datetime
In the previous examples we started with a datetime object and converted it to a string of our chosen format using strftime. We can also go the other way and convert a string to a  datetime object. This is very useful for example if you are reading a text file into Python with dates represented as strings, and wish to perform some date or time operations on the imported string.
To convert from a string to a datetime object we make use of the strptime(format) method. The format tables applicable to strftime are also applicable to strptime.
strptime(date_string,format)
# date_string is the input string to convert into a datetime object.
# format specifies how to convert the string.
The example below shows strptime in action.
>>> my_date_str = '2022-05-23' # a string in your own format
# now use strptime(format) to match the format of your string
# and convert the string into a datetime object.
>>> date_obj = datetime.datetime.strptime(my_date_str, "%Y-%m-%d")
>>> print(str(type(date_obj)))
"<class 'datetime.datetime'>" # datetime object results
>>> print(date_obj.strftime('%c'))
'Mon May 23 00:00:00 2022'
Time and Date Calculations with timedelta
A timedelta object represents a duration, that is the difference between two dates or times expressed in days or time units.
datetime.timedelta(days=0,seconds=0,microseconds=0,milliseconds=0,minutes=0,hours=0)
# all arguments are optional and default to 0.
# arguments may be positive or negative.
# arguments may be integers or floats.
- Only days, seconds, and microseconds are stored internally in the object. All other arguments are converted into those units as follows.
	- A millisecond is converted to 1000 microseconds.
- A minute is converted to 60 seconds.
- An hour is converted to 3600 seconds.
- A week is converted to 7 days.
 
You are most likely to use timedelta in the context of a mathematical operation to determine the duration between two dates or times. Our example below illustrates this use case.
# duration calculations involving dates and times use the timedelta object.
now = datetime.datetime.now()
past = datetime.datetime(2020,6,12,15,25,45)
diff = now-past # timedelta invoked when mathematical operation used.
Output
print(str(diff))
'726 days, 1:16:37.828801'
print(repr(diff))
datetime.timedelta(days=726, seconds=4597, microseconds=828801)
# timedelta attributes can be called directly 
print(f"There are {diff.days} days between the dates.")
'There are 726 days between the dates.'
print(f"There are {diff.days} days and {diff.seconds} seconds between the dates.")
'There are 726 days and 4597 seconds between the dates.'