--- hide-toc: true myst: html_meta: description: >- Overview of the sharp edges in Python's standard datetime module, and how whenever addresses each of them. --- (datetime-pitfalls)= # 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. ``` ```{eval-rst} .. grid:: 1 2 2 3 :margin: 4 4 0 0 :gutter: 2 .. grid-item-card:: :octicon:`north-star` One type, two meanings :link: naive-aware :link-type: doc :shadow: md One class for two incompatible concepts, so annotations can't tell them apart .. grid-item-card:: :octicon:`sun` operators ignore DST :link: dst-ignored :link-type: doc :shadow: md Eight hours after 10pm isn't always 6am, but ``+`` thinks it is .. grid-item-card:: :octicon:`stack` "Naive" is overloaded :link: naive-meaning :link-type: doc :shadow: md Sometimes the system timezone, sometimes UTC, sometimes neither .. grid-item-card:: :octicon:`mute` Silent ambiguity :link: silent-ambiguity :link-type: doc :shadow: md Times that happen twice—or never—are resolved without a word .. grid-item-card:: :octicon:`pulse` equality ignores ``fold`` :link: broken-equality :link-type: doc :shadow: md Identical moments can compare unequal, and distinct ones equal .. grid-item-card:: :octicon:`stop` ``timezone`` isn't a time zone :link: timezone-classes :link-type: doc :shadow: md Several timezone classes to choose from; the obvious one is wrong .. grid-item-card:: :octicon:`location` Quietly uses the system zone :link: system-timezone :link-type: doc :shadow: md The result depends on your machine's configuration .. grid-item-card:: :octicon:`flame` Broken ``date`` inheritance :link: date-inheritance :link-type: doc :shadow: md A subclass that can't be compared with its own base class .. grid-item-card:: :octicon:`alert` ``timedelta.seconds`` footgun :link: timedelta-seconds :link-type: doc :shadow: md A remainder that looks like a total—right up until it doesn't .. toctree:: :maxdepth: 1 :hidden: One type, two meanings dst-ignored "Naive" is overloaded Silent ambiguity broken-equality timezone-classes Quietly uses the system zone Broken date inheritance timedelta-seconds ```