Archive for April, 2009
Timezones are fickle
I’ve been trying to work out a system to be able to cleanly switch between IST (Israel Standard Time, GMT+2:00) and IDT (Israel Daylight savings Time, GMT+3:00) on command. The logical way to do this, in my opinion, is to have two separate files in /usr/share/zoneinfo, say IsraelIST and IsraelIDT, and copy (or link) the relevant one as /etc/localtime. The trick is creating the IsraelIDT file:
My first guess was the following zic source-file:
# Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL] Zone IsraelIDT 2:00 01:00 IDT
Now, this almost works. The problem is that both is_dst is set and timezone = -10800 (3 hours – should be 2, as it should represent local standard time), so some software double-compensates here for a grand total of GMT+4:00. After some research (walking through __tzfile_read gave the biggest hint), it turns out that timezone is set according to the minimal local time type which is transitioned into. So I came up with this file:
# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule ZionIDT min 1939 - Jan 1 00:00 1:00 D Rule ZionIDT 1939 only - Jan 1 00:00 0:00 S Rule ZionIDT 1940 max - Jan 1 00:00 1:00 D # Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL] Zone IsraelIDT 2:00 ZionIDT I%sT
Sounds about right, nay? Even my handy little pyzdump confirms that it looks about how I want it to:
./pyzdump.py /usr/share/zoneinfo/IsraelIDT Transitions: ['At Sat Dec 31 23:00:00 1938, switch to IST', 'At Sun Dec 31 22:00:00 1939, switch to IDT'] Types: [<TZType IDT: UTC+10800 dst=True>, <TZType IST: UTC+7200 dst=False>]
(pardon the lack of syntax highlighting – my prettyprinter fails me)
However, it still doesn’t work. A test program:
int main() {
tzset();
time_t t = time(NULL);
printf("Timezone name is %s, timezone=%ld\n", __tzname[1], timezone);
printf("The time is %s", ctime(&t));
printf("Timezone name is %s, timezone=%ld\n", __tzname[1], timezone);
return 0;
}
And its results, as run at 14:42:17 UTC, which is 19:42:17 IDT:
Timezone name is IDT, timezone=-7200 The time is Sat Apr 18 14:42:17 2009 Timezone name is UTC, timezone=0
Or, as I described it to a friend:
Me: Hi computer, do you know what timezone are we in?
Computer: Yeah, it’s Israel Daylight Savings time, GMT+2:00 for standard time.
Me: OK, and what time is it?
Computer: 14:42
Me: No, that’s 3 hours late. What timezone are we in?
Computer: Umm… UTC?
Me: You just said IDT.
Computer: Nuh-uh.
I’ll get to the bottom of this eventually :/
Addendum: It seems that the problem is even more complicated. For the following timezone file, C programs seem to work fine:
# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule ZionIDT min 1939 - Jan 1 00:00 1:00 D Rule ZionIDT 1939 only - Jan 1 00:00 0:00 S Rule ZionIDT 1940 2030 - Jan 1 00:00 1:00 D Rule ZionIDT 2030 max - Jan 1 00:00 0:00 S # Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL] Zone IsraelIDT 2:00 ZionIDT I%sT
However, Python programs still show timezone = -10800. Examining Python’s code, I found this:
if( janzone < julyzone ) {
/* DST is reversed in the southern hemisphere */
PyModule_AddIntConstant(m, "timezone", julyzone);
PyModule_AddIntConstant(m, "altzone", janzone);
PyModule_AddIntConstant(m, "daylight",
janzone != julyzone);
PyModule_AddObject(m, "tzname",
Py_BuildValue("(zz)",
julyname, janname));
} else {
PyModule_AddIntConstant(m, "timezone", janzone);
PyModule_AddIntConstant(m, "altzone", julyzone);
PyModule_AddIntConstant(m, "daylight",
janzone != julyzone);
PyModule_AddObject(m, "tzname",
Py_BuildValue("(zz)",
janname, julyname));
}
And since June and July have the same timezone in our case, there's a good chance that this is what's going wrong.
The moral of the story seems to be this - I should go with the first, simplest "always-DST" solution. Programs should ignore the timezone variable, as in our context it isn't reliable. In general, all internal time handling should be done in UTC; when reading times from the outside world, if they are in local time - use mktime. If they are in a specified timezone, use timegm and compensate manually. I'd love to hear better ideas in the comments.
Using git for code review
At my workplace, I’ve recently been using git for code review purposes. I work on code in my own git clone, and ask a peer to review it. It works somewhat like this:
masterbranch is same code as currently in upstream.- Working to resolve issue #1234 pertaining to “Performance for gizmo”, I work on a branch
1234-gizmo-performance. - I mail a peer, John, with this information, as well as my repository location.
- John adds my repository as a remote, lutzky. Then he branches
review1(orreview2if that is taken, and so on) atlutzky/1234-gizmo-performance. - John adds comments with nice big “
FIXME” tags, which are highlighted in any decent editor. He commits this, the commit-message stating that it was code review. - John tags his final review commit (or, if he had no comments –
lutzky/1234-gizmo-performance) with a “reviewed1” (orreviewed2, etc.) annotated tag. Since the annotated tag includes all the necessary information (who tagged, when, and what), the number doesn’t really matter. - I merge
john/review1, incorporate the changes (or reject them) and remove the comments. If no further review is necessary, I submit this – and once submitted, I merge this back into master.
It’s a nice system. I wonder what other methods there are of doing this.
