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.

Saturday, April 18th, 2009 Asides 5 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:

  1. master branch is same code as currently in upstream.
  2. Working to resolve issue #1234 pertaining to “Performance for gizmo”, I work on a branch 1234-gizmo-performance.
  3. I mail a peer, John, with this information, as well as my repository location.
  4. John adds my repository as a remote, lutzky. Then he branches review1 (or review2 if that is taken, and so on) at lutzky/1234-gizmo-performance.
  5. 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.
  6. John tags his final review commit (or, if he had no comments – lutzky/1234-gizmo-performance) with a “reviewed1” (or reviewed2, etc.) annotated tag. Since the annotated tag includes all the necessary information (who tagged, when, and what), the number doesn’t really matter.
  7. 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.

Saturday, April 4th, 2009 Asides 1 Comment