PythonMastery
reference 5 min read · lesson 11 of 25 in Reference

Python Datetime Format Cheat Sheet

1 · The lesson

read

The datetime module has three types you will use all the time. A date is a day. A datetime is a day plus a clock time. A timedelta is a length of time: subtract two datetimes and you get one. This page shows how to create them, turn them into text and back, do date maths and work with time zones. For the full tutorial, see Datetime.

The examples use one datetime, d, which is a Saturday:

python
from datetime import date, time, datetime, timedelta, timezone

d = datetime(2026, 3, 14, 9, 30)
print(d)  # 2026-03-14 09:30:00

1. Create

python
print(repr(date(2026, 3, 14)))    # datetime.date(2026, 3, 14)
print(repr(time(9, 30)))          # datetime.time(9, 30)
print(d.date())                   # 2026-03-14
print(datetime.combine(date(2026, 3, 14), time(18)))  # 2026-03-14 18:00:00
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def date(*_a, **_kw):
    print('-> date() called')
    return _AutoMock('date()')
def time(*_a, **_kw):
    print('-> time() called')
    return _AutoMock('time()')
d = _AutoMock('d')
datetime = _AutoMock('datetime')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

A date has no time and a time has no day. d.date() keeps only the date part, and datetime.combine() joins a date and a time.

For the current moment:

CallWhat you get
datetime.now()Now, on the local clock, with no time zone attached
datetime.now(timezone.utc)Now in UTC (world time): the safe choice for logs and databases
date.today()Today's date

2. Parts

python
print(d.year, d.month, d.day)     # 2026 3 14
print(d.hour, d.minute)           # 9 30
print(d.weekday())                # 5
print(d.isoweekday())             # 6
print(d.isocalendar().week)       # 11
print(d.replace(day=1).date())    # 2026-03-01
+ setup added so this can run · re-runs 2 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

d = _AutoMock('d')
def date(*_a, **_kw):
    print('-> date() called')
    return _AutoMock('date()')
def time(*_a, **_kw):
    print('-> time() called')
    return _AutoMock('time()')
datetime = _AutoMock('datetime')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
    'print(repr(date(2026, 3, 14)))    # datetime.date(2026, 3, 14)\nprint(repr(time(9, 30)))          # datetime.time(9, 30)\nprint(d.date())                   # 2026-03-14\nprint(datetime.combine(date(2026, 3, 14), time(18)))  # 2026-03-14 18:00:00',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

weekday() counts Monday as 0 and Sunday as 6. isoweekday() counts Monday as 1 and Sunday as 7. isocalendar().week is the ISO week number. replace() gives a copy with one part changed. Here it gives the first day of the month.

3. Date to text

strftime() ("string format time") turns a date into text. You describe the text with format codes such as %Y for the year (see section 4).

python
print(d.strftime("%Y-%m-%d"))        # 2026-03-14
print(d.strftime("%d/%m/%Y"))        # 14/03/2026
print(d.strftime("%a %d %b %Y"))     # Sat 14 Mar 2026
print(d.strftime("%A, %B %d"))       # Saturday, March 14
print(d.strftime("%I:%M %p"))        # 09:30 AM
print(f"{d:%d %b, %H:%M}")           # 14 Mar, 09:30
print(d.isoformat())                 # 2026-03-14T09:30:00
print(d.isoformat(" ", "minutes"))   # 2026-03-14 09:30
+ setup added so this can run · re-runs 3 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

d = _AutoMock('d')
def date(*_a, **_kw):
    print('-> date() called')
    return _AutoMock('date()')
def time(*_a, **_kw):
    print('-> time() called')
    return _AutoMock('time()')
datetime = _AutoMock('datetime')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
    'print(repr(date(2026, 3, 14)))    # datetime.date(2026, 3, 14)\nprint(repr(time(9, 30)))          # datetime.time(9, 30)\nprint(d.date())                   # 2026-03-14\nprint(datetime.combine(date(2026, 3, 14), time(18)))  # 2026-03-14 18:00:00',
    'print(d.year, d.month, d.day)     # 2026 3 14\nprint(d.hour, d.minute)           # 9 30\nprint(d.weekday())                # 5\nprint(d.isoweekday())             # 6\nprint(d.isocalendar().week)       # 11\nprint(d.replace(day=1).date())    # 2026-03-01',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

The same codes work inside an f-string after a colon. isoformat() gives the standard ISO 8601 form (year-month-day, then the time), which is the best form for files and APIs.

4. Format codes

CodeMeaningFor d
%Y / %yYear, 4 digits / 2 digits2026 / 26
%mMonth as a number, 2 digits03
%B / %bMonth name, full / shortMarch / Mar
%dDay of the month, 2 digits14
%jDay of the year073
%A / %aWeekday name, full / shortSaturday / Sat
%HHour, 24-hour clock09
%IHour, 12-hour clock09
%pAM or PMAM
%MMinute30
%SSecond00
%fMicrosecond, 6 digits000000
%z / %ZUTC offset / zone name (only when the datetime has a zone)+0530 / IST
%%A literal % sign%

Note the capitals: %m is the month, %M is the minute.

5. Text to date

strptime() ("string parse time") reads text into a datetime. The format must match the text exactly.

python
print(repr(datetime.strptime("14/03/2026", "%d/%m/%Y")))
# datetime.datetime(2026, 3, 14, 0, 0)
print(datetime.strptime("9:05 PM", "%I:%M %p").hour)    # 21
print(date.fromisoformat("2026-03-14"))                 # 2026-03-14
print(datetime.fromisoformat("2026-03-14T09:30Z").tzinfo)  # UTC
+ setup added so this can run · re-runs 2 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

date = _AutoMock('date')
datetime = _AutoMock('datetime')
def time(*_a, **_kw):
    print('-> time() called')
    return _AutoMock('time()')
d = _AutoMock('d')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
    'print(repr(date(2026, 3, 14)))    # datetime.date(2026, 3, 14)\nprint(repr(time(9, 30)))          # datetime.time(9, 30)\nprint(d.date())                   # 2026-03-14\nprint(datetime.combine(date(2026, 3, 14), time(18)))  # 2026-03-14 18:00:00',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

%p reads AM or PM. fromisoformat() reads ISO text. Since Python 3.11 it also accepts a Z or an offset like +05:30 at the end.

Bad text, and dates that do not exist, raise ValueError:

python
for text in ["14/03/2026", "31/02/2026", "next week"]:
    try:
        print(datetime.strptime(text, "%d/%m/%Y").date())
    except ValueError:
        print("not a date:", text)
+ setup added so this can run · re-runs 3 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

datetime = _AutoMock('datetime')
def date(*_a, **_kw):
    print('-> date() called')
    return _AutoMock('date()')
def time(*_a, **_kw):
    print('-> time() called')
    return _AutoMock('time()')
d = _AutoMock('d')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
    'print(repr(date(2026, 3, 14)))    # datetime.date(2026, 3, 14)\nprint(repr(time(9, 30)))          # datetime.time(9, 30)\nprint(d.date())                   # 2026-03-14\nprint(datetime.combine(date(2026, 3, 14), time(18)))  # 2026-03-14 18:00:00',
    'print(repr(datetime.strptime("14/03/2026", "%d/%m/%Y")))\n# datetime.datetime(2026, 3, 14, 0, 0)\nprint(datetime.strptime("9:05 PM", "%I:%M %p").hour)    # 21\nprint(date.fromisoformat("2026-03-14"))                 # 2026-03-14\nprint(datetime.fromisoformat("2026-03-14T09:30Z").tzinfo)  # UTC',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier
python
2026-03-14
not a date: 31/02/2026
not a date: next week

6. Date maths

Add or subtract a timedelta to move a date. Subtract two dates to get the gap between them.

python
print(d + timedelta(days=30))                       # 2026-04-13 09:30:00
print(d - timedelta(hours=10))                      # 2026-03-13 23:30:00
print((date(2026, 12, 25) - d.date()).days)         # 286
print(abs(date(2026, 1, 1) - d.date()).days)        # 72
print(d.replace(hour=0, minute=0))                  # 2026-03-14 00:00:00
print(date(2026, 3, 1) - timedelta(days=1))         # 2026-02-28
print(d < d + timedelta(seconds=1))                 # True
+ setup added so this can run · re-runs 5 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

d = _AutoMock('d')
def timedelta(*_a, **_kw):
    print('-> timedelta() called')
    return _AutoMock('timedelta()')
def date(*_a, **_kw):
    print('-> date() called')
    return _AutoMock('date()')
def time(*_a, **_kw):
    print('-> time() called')
    return _AutoMock('time()')
datetime = _AutoMock('datetime')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
    'print(repr(date(2026, 3, 14)))    # datetime.date(2026, 3, 14)\nprint(repr(time(9, 30)))          # datetime.time(9, 30)\nprint(d.date())                   # 2026-03-14\nprint(datetime.combine(date(2026, 3, 14), time(18)))  # 2026-03-14 18:00:00',
    'print(d.year, d.month, d.day)     # 2026 3 14\nprint(d.hour, d.minute)           # 9 30\nprint(d.weekday())                # 5\nprint(d.isoweekday())             # 6\nprint(d.isocalendar().week)       # 11\nprint(d.replace(day=1).date())    # 2026-03-01',
    'print(d.strftime("%Y-%m-%d"))        # 2026-03-14\nprint(d.strftime("%d/%m/%Y"))        # 14/03/2026\nprint(d.strftime("%a %d %b %Y"))     # Sat 14 Mar 2026\nprint(d.strftime("%A, %B %d"))       # Saturday, March 14\nprint(d.strftime("%I:%M %p"))        # 09:30 AM\nprint(f"{d:%d %b, %H:%M}")           # 14 Mar, 09:30\nprint(d.isoformat())                 # 2026-03-14T09:30:00\nprint(d.isoformat(" ", "minutes"))   # 2026-03-14 09:30',
    'print(repr(datetime.strptime("14/03/2026", "%d/%m/%Y")))\n# datetime.datetime(2026, 3, 14, 0, 0)\nprint(datetime.strptime("9:05 PM", "%I:%M %p").hour)    # 21\nprint(date.fromisoformat("2026-03-14"))                 # 2026-03-14\nprint(datetime.fromisoformat("2026-03-14T09:30Z").tzinfo)  # UTC',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

Use abs() when you do not know which date is later. Datetimes can be compared and sorted like numbers.

A timedelta stores only days, seconds and microseconds:

python
print(repr(timedelta(hours=36)))              # datetime.timedelta(days=1, seconds=43200)
print(timedelta(hours=27).total_seconds())    # 97200.0
print(timedelta(minutes=95))                  # 1:35:00
print(timedelta(weeks=2) // timedelta(days=1))  # 14
print(timedelta(hours=1) / 2)                 # 0:30:00
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def timedelta(*_a, **_kw):
    print('-> timedelta() called')
    return _AutoMock('timedelta()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

total_seconds() gives the whole length in seconds. Dividing one timedelta by another with // tells you how many whole days (or hours) fit. Dividing a timedelta by a number gives a shorter timedelta.

7. Time zones

A datetime with no time zone is called naive. A datetime with a zone is called aware. Use ZoneInfo with a name like "Asia/Kolkata" to attach a zone. On Windows, install the zone data once first:

bash
pip install tzdata
python
from zoneinfo import ZoneInfo

ist = ZoneInfo("Asia/Kolkata")
ny = ZoneInfo("America/New_York")

t = d.replace(tzinfo=ist)                     # same clock time, now aware
print(f"{t:%H:%M %Z}")                        # 09:30 IST
print(f"{t.astimezone(ny):%H:%M %Z}")         # 00:00 EDT
print(t.astimezone(timezone.utc).isoformat()) # 2026-03-14T04:00:00+00:00
print(t.utcoffset())                          # 5:30:00
print(t.timestamp())                          # 1773460800.0
print(datetime.fromtimestamp(0, timezone.utc).year)  # 1970
+ setup added so this can run · re-runs 7 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

d = _AutoMock('d')
datetime = _AutoMock('datetime')
timezone = _AutoMock('timezone')
def date(*_a, **_kw):
    print('-> date() called')
    return _AutoMock('date()')
def time(*_a, **_kw):
    print('-> time() called')
    return _AutoMock('time()')
def timedelta(*_a, **_kw):
    print('-> timedelta() called')
    return _AutoMock('timedelta()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
    'print(repr(date(2026, 3, 14)))    # datetime.date(2026, 3, 14)\nprint(repr(time(9, 30)))          # datetime.time(9, 30)\nprint(d.date())                   # 2026-03-14\nprint(datetime.combine(date(2026, 3, 14), time(18)))  # 2026-03-14 18:00:00',
    'print(d.year, d.month, d.day)     # 2026 3 14\nprint(d.hour, d.minute)           # 9 30\nprint(d.weekday())                # 5\nprint(d.isoweekday())             # 6\nprint(d.isocalendar().week)       # 11\nprint(d.replace(day=1).date())    # 2026-03-01',
    'print(d.strftime("%Y-%m-%d"))        # 2026-03-14\nprint(d.strftime("%d/%m/%Y"))        # 14/03/2026\nprint(d.strftime("%a %d %b %Y"))     # Sat 14 Mar 2026\nprint(d.strftime("%A, %B %d"))       # Saturday, March 14\nprint(d.strftime("%I:%M %p"))        # 09:30 AM\nprint(f"{d:%d %b, %H:%M}")           # 14 Mar, 09:30\nprint(d.isoformat())                 # 2026-03-14T09:30:00\nprint(d.isoformat(" ", "minutes"))   # 2026-03-14 09:30',
    'print(repr(datetime.strptime("14/03/2026", "%d/%m/%Y")))\n# datetime.datetime(2026, 3, 14, 0, 0)\nprint(datetime.strptime("9:05 PM", "%I:%M %p").hour)    # 21\nprint(date.fromisoformat("2026-03-14"))                 # 2026-03-14\nprint(datetime.fromisoformat("2026-03-14T09:30Z").tzinfo)  # UTC',
    'for text in ["14/03/2026", "31/02/2026", "next week"]:\n    try:\n        print(datetime.strptime(text, "%d/%m/%Y").date())\n    except ValueError:\n        print("not a date:", text)',
    'print(d + timedelta(days=30))                       # 2026-04-13 09:30:00\nprint(d - timedelta(hours=10))                      # 2026-03-13 23:30:00\nprint((date(2026, 12, 25) - d.date()).days)         # 286\nprint(abs(date(2026, 1, 1) - d.date()).days)        # 72\nprint(d.replace(hour=0, minute=0))                  # 2026-03-14 00:00:00\nprint(date(2026, 3, 1) - timedelta(days=1))         # 2026-02-28\nprint(d < d + timedelta(seconds=1))                 # True',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

replace(tzinfo=...) attaches a zone without changing the clock time. astimezone() converts: it shows the same moment on another clock. Store and compare times in UTC, and convert to local time only to show them to a person. A timestamp is the number of seconds since 1 January 1970 UTC.

timezone(timedelta(hours=5, minutes=30)) is a fixed offset with no daylight saving rules. Use a ZoneInfo name when the place changes its clocks in summer.

8. Useful recipes

python
import calendar

print(calendar.monthrange(2026, 2)[1])   # 28
print(calendar.isleap(2028))             # True

# Next Monday (0), never today; use 4 for Friday
ahead = (0 - d.weekday()) % 7 or 7
print(d.date() + timedelta(days=ahead))  # 2026-03-16

# Age in whole years: t is today, b is the birthday
t, b = date(2026, 3, 14), date(1990, 6, 1)
age = t.year - b.year - ((t.month, t.day) < (b.month, b.day))
print(age)                               # 35

# ISO text sorts in date order
print(sorted(["2026-03-14", "2025-12-01"]))  # ['2025-12-01', '2026-03-14']
+ setup added so this can run · re-runs 8 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def date(*_a, **_kw):
    print('-> date() called')
    return _AutoMock('date()')
def timedelta(*_a, **_kw):
    print('-> timedelta() called')
    return _AutoMock('timedelta()')
d = _AutoMock('d')
def time(*_a, **_kw):
    print('-> time() called')
    return _AutoMock('time()')
datetime = _AutoMock('datetime')
timezone = _AutoMock('timezone')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
    from zoneinfo import ZoneInfo
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
    'print(repr(date(2026, 3, 14)))    # datetime.date(2026, 3, 14)\nprint(repr(time(9, 30)))          # datetime.time(9, 30)\nprint(d.date())                   # 2026-03-14\nprint(datetime.combine(date(2026, 3, 14), time(18)))  # 2026-03-14 18:00:00',
    'print(d.year, d.month, d.day)     # 2026 3 14\nprint(d.hour, d.minute)           # 9 30\nprint(d.weekday())                # 5\nprint(d.isoweekday())             # 6\nprint(d.isocalendar().week)       # 11\nprint(d.replace(day=1).date())    # 2026-03-01',
    'print(d.strftime("%Y-%m-%d"))        # 2026-03-14\nprint(d.strftime("%d/%m/%Y"))        # 14/03/2026\nprint(d.strftime("%a %d %b %Y"))     # Sat 14 Mar 2026\nprint(d.strftime("%A, %B %d"))       # Saturday, March 14\nprint(d.strftime("%I:%M %p"))        # 09:30 AM\nprint(f"{d:%d %b, %H:%M}")           # 14 Mar, 09:30\nprint(d.isoformat())                 # 2026-03-14T09:30:00\nprint(d.isoformat(" ", "minutes"))   # 2026-03-14 09:30',
    'print(repr(datetime.strptime("14/03/2026", "%d/%m/%Y")))\n# datetime.datetime(2026, 3, 14, 0, 0)\nprint(datetime.strptime("9:05 PM", "%I:%M %p").hour)    # 21\nprint(date.fromisoformat("2026-03-14"))                 # 2026-03-14\nprint(datetime.fromisoformat("2026-03-14T09:30Z").tzinfo)  # UTC',
    'for text in ["14/03/2026", "31/02/2026", "next week"]:\n    try:\n        print(datetime.strptime(text, "%d/%m/%Y").date())\n    except ValueError:\n        print("not a date:", text)',
    'print(d + timedelta(days=30))                       # 2026-04-13 09:30:00\nprint(d - timedelta(hours=10))                      # 2026-03-13 23:30:00\nprint((date(2026, 12, 25) - d.date()).days)         # 286\nprint(abs(date(2026, 1, 1) - d.date()).days)        # 72\nprint(d.replace(hour=0, minute=0))                  # 2026-03-14 00:00:00\nprint(date(2026, 3, 1) - timedelta(days=1))         # 2026-02-28\nprint(d < d + timedelta(seconds=1))                 # True',
    'from zoneinfo import ZoneInfo\n\nist = ZoneInfo("Asia/Kolkata")\nny = ZoneInfo("America/New_York")\n\nt = d.replace(tzinfo=ist)                     # same clock time, now aware\nprint(f"{t:%H:%M %Z}")                        # 09:30 IST\nprint(f"{t.astimezone(ny):%H:%M %Z}")         # 00:00 EDT\nprint(t.astimezone(timezone.utc).isoformat()) # 2026-03-14T04:00:00+00:00\nprint(t.utcoffset())                          # 5:30:00\nprint(t.timestamp())                          # 1773460800.0\nprint(datetime.fromtimestamp(0, timezone.utc).year)  # 1970',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

monthrange() returns the weekday of the first day and the number of days in the month. [1] takes the number of days. In the age recipe, the comparison is True (counted as 1) if the birthday has not come yet this year.

Common mistakes

Comparing naive and aware datetimes. If only one side has a zone, Python raises TypeError:

python
try:
    d < d.replace(tzinfo=timezone.utc)
except TypeError as e:
    print(e)  # can't compare offset-naive and offset-aware datetimes
+ setup added so this can run · re-runs 9 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

d = _AutoMock('d')
timezone = _AutoMock('timezone')
def date(*_a, **_kw):
    print('-> date() called')
    return _AutoMock('date()')
def time(*_a, **_kw):
    print('-> time() called')
    return _AutoMock('time()')
datetime = _AutoMock('datetime')
def timedelta(*_a, **_kw):
    print('-> timedelta() called')
    return _AutoMock('timedelta()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
    from zoneinfo import ZoneInfo
    import calendar
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
    'print(repr(date(2026, 3, 14)))    # datetime.date(2026, 3, 14)\nprint(repr(time(9, 30)))          # datetime.time(9, 30)\nprint(d.date())                   # 2026-03-14\nprint(datetime.combine(date(2026, 3, 14), time(18)))  # 2026-03-14 18:00:00',
    'print(d.year, d.month, d.day)     # 2026 3 14\nprint(d.hour, d.minute)           # 9 30\nprint(d.weekday())                # 5\nprint(d.isoweekday())             # 6\nprint(d.isocalendar().week)       # 11\nprint(d.replace(day=1).date())    # 2026-03-01',
    'print(d.strftime("%Y-%m-%d"))        # 2026-03-14\nprint(d.strftime("%d/%m/%Y"))        # 14/03/2026\nprint(d.strftime("%a %d %b %Y"))     # Sat 14 Mar 2026\nprint(d.strftime("%A, %B %d"))       # Saturday, March 14\nprint(d.strftime("%I:%M %p"))        # 09:30 AM\nprint(f"{d:%d %b, %H:%M}")           # 14 Mar, 09:30\nprint(d.isoformat())                 # 2026-03-14T09:30:00\nprint(d.isoformat(" ", "minutes"))   # 2026-03-14 09:30',
    'print(repr(datetime.strptime("14/03/2026", "%d/%m/%Y")))\n# datetime.datetime(2026, 3, 14, 0, 0)\nprint(datetime.strptime("9:05 PM", "%I:%M %p").hour)    # 21\nprint(date.fromisoformat("2026-03-14"))                 # 2026-03-14\nprint(datetime.fromisoformat("2026-03-14T09:30Z").tzinfo)  # UTC',
    'for text in ["14/03/2026", "31/02/2026", "next week"]:\n    try:\n        print(datetime.strptime(text, "%d/%m/%Y").date())\n    except ValueError:\n        print("not a date:", text)',
    'print(d + timedelta(days=30))                       # 2026-04-13 09:30:00\nprint(d - timedelta(hours=10))                      # 2026-03-13 23:30:00\nprint((date(2026, 12, 25) - d.date()).days)         # 286\nprint(abs(date(2026, 1, 1) - d.date()).days)        # 72\nprint(d.replace(hour=0, minute=0))                  # 2026-03-14 00:00:00\nprint(date(2026, 3, 1) - timedelta(days=1))         # 2026-02-28\nprint(d < d + timedelta(seconds=1))                 # True',
    'from zoneinfo import ZoneInfo\n\nist = ZoneInfo("Asia/Kolkata")\nny = ZoneInfo("America/New_York")\n\nt = d.replace(tzinfo=ist)                     # same clock time, now aware\nprint(f"{t:%H:%M %Z}")                        # 09:30 IST\nprint(f"{t.astimezone(ny):%H:%M %Z}")         # 00:00 EDT\nprint(t.astimezone(timezone.utc).isoformat()) # 2026-03-14T04:00:00+00:00\nprint(t.utcoffset())                          # 5:30:00\nprint(t.timestamp())                          # 1773460800.0\nprint(datetime.fromtimestamp(0, timezone.utc).year)  # 1970',
    'import calendar\n\nprint(calendar.monthrange(2026, 2)[1])   # 28\nprint(calendar.isleap(2028))             # True\n\n# Next Monday (0), never today; use 4 for Friday\nahead = (0 - d.weekday()) % 7 or 7\nprint(d.date() + timedelta(days=ahead))  # 2026-03-16\n\n# Age in whole years: t is today, b is the birthday\nt, b = date(2026, 3, 14), date(1990, 6, 1)\nage = t.year - b.year - ((t.month, t.day) < (b.month, b.day))\nprint(age)                               # 35\n\n# ISO text sorts in date order\nprint(sorted(["2026-03-14", "2025-12-01"]))  # [\'2025-12-01\', \'2026-03-14\']',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

Fix: give both a zone.

%M instead of %m. %M is minutes, so d.strftime("%Y-%M-%d") gives '2026-30-14'. Use %m for the month.

.seconds instead of total_seconds(). .seconds leaves out the days:

python
gap = timedelta(days=1, seconds=5)
print(gap.seconds)          # 5
print(gap.total_seconds())  # 86405.0
+ setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

def timedelta(*_a, **_kw):
    print('-> timedelta() called')
    return _AutoMock('timedelta()')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

Adding "a month" as 30 days. timedelta has no months, and months are not all 30 days long: date(2026, 1, 31) + timedelta(days=30) is 2 March, not the end of February. For calendar months, use replace(month=...), or relativedelta from the dateutil package (install it with pip).

29 February in another year. date(2024, 2, 29).replace(year=2025) raises ValueError, because 2025 has no 29 February.

Comparing a datetime with a date. They are never equal:

python
print(d == date(2026, 3, 14))         # False
print(d.date() == date(2026, 3, 14))  # True
+ setup added so this can run · re-runs 10 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

d = _AutoMock('d')
def date(*_a, **_kw):
    print('-> date() called')
    return _AutoMock('date()')
def time(*_a, **_kw):
    print('-> time() called')
    return _AutoMock('time()')
datetime = _AutoMock('datetime')
def timedelta(*_a, **_kw):
    print('-> timedelta() called')
    return _AutoMock('timedelta()')
timezone = _AutoMock('timezone')

# Earlier blocks on this page that this one builds on, re-run quietly first.
try:
    from datetime import date, time, datetime, timedelta, timezone
    from zoneinfo import ZoneInfo
    import calendar
except Exception:
    pass
_pm_earlier = [
    'from datetime import date, time, datetime, timedelta, timezone\n\nd = datetime(2026, 3, 14, 9, 30)\nprint(d)  # 2026-03-14 09:30:00',
    'print(repr(date(2026, 3, 14)))    # datetime.date(2026, 3, 14)\nprint(repr(time(9, 30)))          # datetime.time(9, 30)\nprint(d.date())                   # 2026-03-14\nprint(datetime.combine(date(2026, 3, 14), time(18)))  # 2026-03-14 18:00:00',
    'print(d.year, d.month, d.day)     # 2026 3 14\nprint(d.hour, d.minute)           # 9 30\nprint(d.weekday())                # 5\nprint(d.isoweekday())             # 6\nprint(d.isocalendar().week)       # 11\nprint(d.replace(day=1).date())    # 2026-03-01',
    'print(d.strftime("%Y-%m-%d"))        # 2026-03-14\nprint(d.strftime("%d/%m/%Y"))        # 14/03/2026\nprint(d.strftime("%a %d %b %Y"))     # Sat 14 Mar 2026\nprint(d.strftime("%A, %B %d"))       # Saturday, March 14\nprint(d.strftime("%I:%M %p"))        # 09:30 AM\nprint(f"{d:%d %b, %H:%M}")           # 14 Mar, 09:30\nprint(d.isoformat())                 # 2026-03-14T09:30:00\nprint(d.isoformat(" ", "minutes"))   # 2026-03-14 09:30',
    'print(repr(datetime.strptime("14/03/2026", "%d/%m/%Y")))\n# datetime.datetime(2026, 3, 14, 0, 0)\nprint(datetime.strptime("9:05 PM", "%I:%M %p").hour)    # 21\nprint(date.fromisoformat("2026-03-14"))                 # 2026-03-14\nprint(datetime.fromisoformat("2026-03-14T09:30Z").tzinfo)  # UTC',
    'for text in ["14/03/2026", "31/02/2026", "next week"]:\n    try:\n        print(datetime.strptime(text, "%d/%m/%Y").date())\n    except ValueError:\n        print("not a date:", text)',
    'print(d + timedelta(days=30))                       # 2026-04-13 09:30:00\nprint(d - timedelta(hours=10))                      # 2026-03-13 23:30:00\nprint((date(2026, 12, 25) - d.date()).days)         # 286\nprint(abs(date(2026, 1, 1) - d.date()).days)        # 72\nprint(d.replace(hour=0, minute=0))                  # 2026-03-14 00:00:00\nprint(date(2026, 3, 1) - timedelta(days=1))         # 2026-02-28\nprint(d < d + timedelta(seconds=1))                 # True',
    'from zoneinfo import ZoneInfo\n\nist = ZoneInfo("Asia/Kolkata")\nny = ZoneInfo("America/New_York")\n\nt = d.replace(tzinfo=ist)                     # same clock time, now aware\nprint(f"{t:%H:%M %Z}")                        # 09:30 IST\nprint(f"{t.astimezone(ny):%H:%M %Z}")         # 00:00 EDT\nprint(t.astimezone(timezone.utc).isoformat()) # 2026-03-14T04:00:00+00:00\nprint(t.utcoffset())                          # 5:30:00\nprint(t.timestamp())                          # 1773460800.0\nprint(datetime.fromtimestamp(0, timezone.utc).year)  # 1970',
    'import calendar\n\nprint(calendar.monthrange(2026, 2)[1])   # 28\nprint(calendar.isleap(2028))             # True\n\n# Next Monday (0), never today; use 4 for Friday\nahead = (0 - d.weekday()) % 7 or 7\nprint(d.date() + timedelta(days=ahead))  # 2026-03-16\n\n# Age in whole years: t is today, b is the birthday\nt, b = date(2026, 3, 14), date(1990, 6, 1)\nage = t.year - b.year - ((t.month, t.day) < (b.month, b.day))\nprint(age)                               # 35\n\n# ISO text sorts in date order\nprint(sorted(["2026-03-14", "2025-12-01"]))  # [\'2025-12-01\', \'2026-03-14\']',
    "try:\n    d < d.replace(tzinfo=timezone.utc)\nexcept TypeError as e:\n    print(e)  # can't compare offset-naive and offset-aware datetimes",
]
def _pm_run_earlier(_srcs, _g):
    import sys as _s, io as _io, warnings as _w
    for _src in _srcs:
        _o, _e = _s.stdout, _s.stderr
        _s.stdout = _s.stderr = _io.StringIO()
        try:
            with _w.catch_warnings():
                _w.simplefilter('ignore')
                exec(compile(_src, '<earlier block>', 'exec'), _g)
        except (Exception, SystemExit):
            pass
        finally:
            _s.stdout, _s.stderr = _o, _e
    if 'matplotlib.pyplot' in _s.modules:
        _s.modules['matplotlib.pyplot'].close('all')   # their plots belong to their own blocks
_pm_run_earlier(_pm_earlier, globals())
del _pm_earlier, _pm_run_earlier

datetime.utcnow(). utcnow() is deprecated, and its result has no zone even though the time is UTC. Use datetime.now(timezone.utc).

See also

Practice this

on practicepython.in

Short exercises that run in your browser and tell you what your code did, as well as whether a test passed.

Found this useful? Share it with a friend who’s learning Python.