Articles tagged with "upsheet"

Idiosyncrasies in Python's timezone handling

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...

Stuck in a timezone

My timesheet uploader micro-project started off as a spontaneous scratch-your-own-itch effort with very little polish. No error handling, liberal copy-paste, vague structure - you know the story. The code obviously involved some parsing and formatting of dates and times. It wasn't very complicated so I avoided the big guns, sticking to string manipulation and simple arithmetic (figuring out that 0830-1030 means 120 minutes etc.).

Timesheet entries are fed to JIRA as JSON documents via its REST API. JSON doesn't define a standard timestamp format but examples in the documentation use ISO8601 with a timezone suffix - something like 2012-08-14T08:30:00.000+0200. I dutifully slapped on the suffix as a constant to all my outgoing timestamp data.

One of the things I wanted from the script was to detect timesheet entries that were already in JIRA to prevent duplication in the worklogs. This involved downloading the worklog for the relevant issue and comparing it with the entry that was to be uploaded. When parsing timestamps in the worklog I happily ignored the timezone suffix. I figured I would always be in the same timezone as my JIRA server.

After some time I noticed this collision detection was often failing. I had no time to analyze it so I simply took care to upload my timesheet all at once and re-upload only the failed entries if there were errors. It was a pain but still much better than stuffing JIRA manually.

When I got more free time in January I discovered the true nature of the bug. Both me and my JIRA server had effectively switched timezones when the Daylight Savings Time ended in October. The script, however, still had +0200 in its outgoing timestamps. This meant that I uploaded an entry with 08:30:00.000+0200 but then it came back as 09:30:00.000+0100. Ignoring the timezone, the script detected no overlap (as long as duration was under 61 minutes) and proceeded to re-upload the entry, again with 08:30:00.000+0200 in the timestamp.

It turns out that no, I can't ignore timezones in my calculations after all. Besides, the code is already on GitHub and right now collision detection works only for the lucky users who are two hours East from UTC together with their JIRA servers. Good thing I haven't told anyone yet :-)

« Page 1 / 1 »
Proudly powered by Pelican, which takes great advantage of Python.