The pitfalls of datetime

Python’s datetime module first appeared in Python 2.3, back in 2003. That it has remained usable for over twenty years is remarkable, in a problem domain this tricky: Java and JavaScript both ended up replacing their date-time APIs wholesale, while Python never needed a wholesale replacement.

It does, however, have sharp edges which regularly trip up even experienced developers. Below are the most impactful ones, and what whenever does instead.

Note

None of this is a condemnation of datetime. It has been carefully maintained and adapted over the years—all while preserving backwards compatibility. “Pitfall” is a subjective term: what follows is simply a catalog of the places where the design makes certain mistakes easy to make.

One type, two meanings

One class for two incompatible concepts, so annotations can’t tell them apart

One type for two incompatible concepts
operators ignore DST

Eight hours after 10pm isn’t always 6am, but + thinks it is

+ ignores DST
“Naive” is overloaded

Sometimes the system timezone, sometimes UTC, sometimes neither

“Naive” is interpreted differently in different places
Silent ambiguity

Times that happen twice—or never—are resolved without a word

Ambiguity is resolved without a word
equality ignores fold

Identical moments can compare unequal, and distinct ones equal

== ignores fold
timezone isn’t a time zone

Several timezone classes to choose from; the obvious one is wrong

timezone isn’t a time zone
Quietly uses the system zone

The result depends on your machine’s configuration

The system time zone is used implicitly
Broken date inheritance

A subclass that can’t be compared with its own base class

datetime inherits from date—and it breaks
timedelta.seconds footgun

A remainder that looks like a total—right up until it doesn’t

timedelta.seconds footgun