Skip to main content

Lib/datetime.py

cpython 3.14 @ ab2d84fe1023/Lib/datetime.py

The pure-Python implementation of Python's date/time library. At the bottom of the file the module attempts to replace itself with the accelerated C module _datetime; under a normal CPython build the C module wins and this file is only consulted for reference or when the C extension is unavailable.

The six public classes are timedelta, date, time, datetime, timezone, and tzinfo. datetime is a subclass of date. All instances are immutable. Arithmetic between datetime objects returns a timedelta; adding a timedelta to a date or datetime returns a new object of the same type.

The module also covers the fold attribute (PEP 495) for disambiguating wall-clock times during DST transitions, and the aware/naive distinction: an instance is "aware" when its tzinfo attribute is not None and tzinfo.utcoffset() returns a non-None value.

Map

LinesSymbolRolegopy
1-200module constants, helper functionsMAXYEAR, MINYEAR, _MAXORDINAL; pure-Python helpers _cmp, _days_in_month, _days_before_month, _days_before_year, _ymd2ord, _ord2ymd.module/datetime/module.go
201-500timedeltaStores normalised _days, _seconds, _microseconds; arithmetic, comparisons, total_seconds(), __repr__, __str__.module/datetime/module.go
501-900dateGregorian date from year/month/day; toordinal/fromordinal; today; ISO calendar; strftime; arithmetic with timedelta.module/datetime/module.go
901-1100tzinfo, timezoneAbstract tzinfo base class; timezone is the concrete fixed-offset subclass with the utc singleton.module/datetime/module.go
1101-1500timeTime-of-day with optional tzinfo and fold; utcoffset, dst, tzname; isoformat; aware/naive comparisons.module/datetime/module.go
1501-2500datetimeCombines date and time; now(tz), utcnow, fromtimestamp, utcfromtimestamp, combine, fromisoformat, strptime; full arithmetic.module/datetime/module.go
2501-2600_wrap_strftime, _strptime_datetimestrftime percent-code expansion; strptime delegates to _strptime._strptime_datetime.module/datetime/module.go
2601-2700C-module replacement blocktry: from _datetime import * overwrites all names with C implementations when available.module/datetime/module.go

Reading

timedelta normalisation (lines 201 to 500)

cpython 3.14 @ ab2d84fe1023/Lib/datetime.py#L201-500

def __new__(cls, days=0, seconds=0, microseconds=0,
milliseconds=0, minutes=0, hours=0, weeks=0):
# Normalise to (days, seconds, microseconds)
d = s = us = 0
days += weeks * 7
seconds += minutes * 60 + hours * 3600
microseconds += milliseconds * 1000

# us -> seconds carry
s, us = divmod(microseconds, 1000000)
seconds += s
# seconds -> days carry
d, s = divmod(seconds, 86400)
days += d
# Invariants: 0 <= s < 86400, 0 <= us < 1000000
if not (-999999999 <= days <= 999999999):
raise OverflowError("timedelta # of days is too large")
...
self._days = days
self._seconds = s
self._microseconds = us
return self

The constructor reduces every keyword argument to three fields with strict invariants: 0 <= _seconds < 86400 and 0 <= _microseconds < 1000000. Negative durations are stored as negative _days combined with positive _seconds/_microseconds offsets, following the same sign convention that Python's divmod uses for negative dividends. total_seconds() reconstructs the float by (self._days * 86400 + self._seconds) * 1e6 + self._microseconds) / 1e6.

date.toordinal and fromordinal (lines 501 to 900)

cpython 3.14 @ ab2d84fe1023/Lib/datetime.py#L501-900

def _ymd2ord(year, month, day):
assert 1 <= month <= 12
dim = _days_in_month(year, month)
assert 1 <= day <= dim
return (_days_before_year(year)
+ _days_before_month(year, month)
+ day)

def _days_before_year(year):
y = year - 1
return y*365 + y//4 - y//100 + y//400

def _ord2ymd(n):
# n is the ordinal (days since year 1, Jan 1)
...

The proleptic Gregorian calendar formula counts days before a year as y*365 + y//4 - y//100 + y//400 where y = year - 1. This matches the ISO 8601 extrapolated Gregorian calendar. _ord2ymd inverts the computation using integer arithmetic and a 400-year cycle. toordinal calls _ymd2ord; fromordinal calls _ord2ymd and constructs a new date.

Aware vs naive datetime arithmetic (lines 1501 to 2500)

cpython 3.14 @ ab2d84fe1023/Lib/datetime.py#L1501-2500

def __sub__(self, other):
"Subtract two datetimes, or a datetime and a timedelta."
if isinstance(other, datetime):
delta = self._getstate() ...
# For aware datetimes, convert to UTC first
mytz = self.tzinfo
ottz = other.tzinfo
myoff = self.utcoffset()
otoff = other.utcoffset()
if myoff == otoff:
base_compare = True
else:
base_compare = myoff is None and otoff is None
if base_compare:
return timedelta(days=(self.toordinal() - other.toordinal()),
seconds=..., microseconds=...)
if myoff is None or otoff is None:
raise TypeError("cannot mix naive and aware datetime objects")
# Both aware: subtract UTC offsets before differencing
...
elif isinstance(other, timedelta):
return self + timedelta(-other.days, ...)

Subtracting two datetime objects returns a timedelta. When both instances are naive (no tzinfo) the subtraction is purely arithmetic. When both are aware, their UTC offsets are subtracted out before differencing so the result is always an absolute duration. Mixing naive and aware datetimes raises TypeError. The fold attribute is consulted only when a tzinfo.fromutc implementation uses it to resolve ambiguous wall-clock times; the subtraction operator itself does not touch fold.