Precision, ranges and leap seconds

datetime · Expert · 9 min

High-frequency data, scientific logs and interop with other systems hit these limits.

datetime and timedelta store microseconds. Anything finer, such as nanosecond timestamps from Kafka, databases or time.time_ns(), is truncated or rounded when converted. Keep integer nanoseconds (or use NumPy's datetime64[ns] or pandas Timestamp) when you need them.

Ranges: datetime.min is year 1 and datetime.max year 9999. Dates use the proleptic Gregorian calendar, extending today's calendar backwards, which doesn't match historical Julian dates. timedelta spans about ±999,999,999 days.

Leap seconds don't exist in Python's model: every day has 86,400 seconds, matching POSIX time. A string like 23:59:60 raises ValueError in both fromisoformat and datetime.strptime; only the lower-level time.strptime and time.struct_time accept seconds of 60 and 61. Systems that log leap seconds need special handling.

Float timestamps have about 15 to 16 significant digits; for current epoch values, that leaves sub-microsecond precision that rounds unpredictably. dt.timestamp() and fromtimestamp round-trip microseconds correctly, but arithmetic on floats can drift. Prefer datetime arithmetic, or integers like time.time_ns() and time.monotonic_ns().

fromtimestamp raises OverflowError or OSError for values out of the platform's range, and on Windows negative timestamps aren't supported.

Example

import time
from datetime import UTC, datetime, timedelta

ns = 1_740_830_400_123_456_789            # nanoseconds since the epoch
dt = datetime.fromtimestamp(ns / 1e9, tz=UTC)
print(dt.microsecond)                     # 123457  (float rounding)
exact = datetime(1970, 1, 1, tzinfo=UTC) + timedelta(microseconds=ns // 1000)
print(exact.microsecond)                  # 123456  (integer math)

print(datetime.min.year, datetime.max.year)   # 1 9999
print(timedelta(microseconds=1) / 2)      # 0:00:00  (rounds to whole µs)

try:
    datetime.fromisoformat("2016-12-31T23:59:60")
except ValueError as e:
    print("leap second rejected")         # leap second rejected
print(type(time.monotonic_ns()).__name__) # int

Remember

  • datetime resolution is one microsecond; keep integer nanoseconds yourself
  • No leap seconds: every day is 86,400 seconds
  • Prefer integer or datetime arithmetic over float timestamps

Watch out for

  • Converting nanosecond timestamps through floats and getting off-by-one microsecond values.

Check yourself

  1. What is the finest resolution a datetime.datetime can store?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/datetime.html#datetime.datetime.min

Builds on

Unlocks

Nothing builds on this yet.