As I work to fix timezone logic in upsheet, I've become somewhat frustrated with date and time handling in Python. Coming from a Java background, I know this stuff is awfully difficult to get right. Python has a large community driven by sophisticated users, however, so I expected things to be pretty smooth. I was wrong:
- datetime vs. time Two modules with overlapping functionality and no clear separation of scope.
- datetime.datetime A class with the same name as its module. This is probably common in the Python library but it makes for weird expressions such as datetime.datetime.strptime(...). Kind of nudges you to drop the module prefix with from datetime import *.
- timezone handling in datetime is a red-headed stepchild The datetime module tactfully avoids the gritty details of coming up with the local system's current timezone. You can supply a timezone but you have to provide your own tzinfo subclass to do it.
- timezone handling in time is a red-headed stepchild You get timezone for the non-DST system timezone, altzone for the DST system timezone, daylight to see if altzone is even applicable and localtime().tm_isdst to decide (provided altzone is applicable) whether timezone or altzone is the local system's current timezone. Whew! I mean, it's wonderfully precise and flexible but kind of all over the place.
- timezone offsets are represented inconsistently While datetime.tzinfo.utcoffset() must return its result in minutes, both time.timezone and time.altzone provide the offset in seconds. Trivial yet irritating.
- datetime.time vs. time A class in one module with the same name as a related module. When you use datetime without the module prefix, this forces you to either also use time without the prefix - further polluting the naming scope - or alias it using import time as sillytime.
- inconsistent timezone offsets While time.timezone and time.altzone specify timezone offsets in seconds west of UTC, tzinfo.utcoffset() must return minutes east of UTC.
- mischievous documentation One example: the description of struct_time.tm_isdst says "0, 1 or -1; see below". Of course, there's no further mention of tm_isdst anywhere in the document. A sentence buried in the next paragraph does say "A -1 argument as the daylight savings flag, passed to mktime() will usually result in the correct daylight savings state to be filled in.". Which means the -1 is utterly irrelevant except for one specific use-case.
Having figured all of this out, I can now construct a datetime.datetime instance equipped with the correct tzinfo for the current local timezone. This goes to show how hard it is to design a really good API.
I do hope it all works the same way under Windows...