How time and timezones are supposed to work in 32-bit OS/2.

Many people thought that OS/2 kept time and understood timezones just like MS/PC/DR-DOS and DOS+Windows did before it: one global timezone, and lots of frantic gyrations whenever a daylight savings time switch occurs. But in fact that isn't actually true of 32-bit OS/2. Used properly, as it was clearly originally designed to be used, OS/2 is in fact capable of having multiple timezones active at once, all automatically switching daylight savings time on and off with no fuss whatsoever — just like one can on Unix and Linux.

Unsurprisingly, the 32-bit OS/2 mechanism was designed to work pretty much along the same lines as the equivalent Unix and Linux mechanisms: The kernel keeps a single clock, running in International Atomic Time (TAI), and all kernel date and time operations, including filesystem operations, operate in TAI. Applications convert to and from local time, for user interface purposes, via application-mode library code, which uses per-process settings to work out exactly how the conversion should occur and which applies daylight savings corrections without reference to any form of global system-wide state.

The single kernel system clock in 32-bit OS/2 is accessible via the DosQuerySysInfo() system API function. It provides a 64-bit count of the SI seconds since 1970-01-01 00:00:00 GMT. (This can potentially last up to the year 584 thousand million. The OS/2 kernel has no Year 2038 problem in this area.)

The hardware RTC

OS/2 sets its 64-bit kernel clock from the PC hardware real-time clock (RTC). It requires that the RTC be set to TAI. The hardware RTC must not be set to local time, and daylight savings time changes must not be applied to it.

(Many OS/2 users have set the hardware RTC to local time in the past. Very few of those people seemed to notice that the 32-bit OS/2 system API function gives the wrong value in such circumstances. It is plain from this alone how 32-bit OS/2 was originally intended to work.)

Nor must it be set to Coördinated Universal Time (UTC), although in common use it often is, since it is easier to obtain a UTC clock reference from a (local time) wall clock or other source than it is to obtain TAI. The hardware RTC only ticks exactly 60 seconds per minute and, strictly speaking, this matches TAI not UTC. Similarly, the OS/2 kernel's clock ticks once per SI second, counting seconds since a fixed Epoch, and again this is only strictly correct if leap seconds are included in the count, which they would not be with UTC.

There are various OS/2 utilities available to synchronise the hardware RTC with time sources available via radio broadcasts or dial-up links in various countries, and across the Internet. If used, these must be configured to maintain the hardware RTC in TAI, or at least in UTC, not in local time.

The TZ environment variable

Applications programs calculate the local time from the TAI time by (strictly speaking) deleting leap seconds to convert to UTC and then applying the timezone offsets and daylight savings time rules supplied in the TZ environment variable. Since each process has its own TZ variable, in theory every single process on the machine could be configured to run in its own individual timezone.

The TZ environment variable determines the names for standard and daylight sayings time in the current timezone, the offsets from UTC for each, and the rules to apply to determine when each is in effect.

The contents of the TZ environment variable are in the standard POSIX 1003.1 (ISO/IEC 9445-1:1990) format:

sss[offset]ddd[offset][,start[/time][,end[/time]]]

This breaks down as follows:

sss[offset]ddd[offset] sss[]ddd[hh[:mm]]

This part of the variable determines the names for standard and daylight savings time, and their offset from UTC in hours and (optionally) minutes west of the Prime Meridian (i.e. the amount to subtract from UTC to obtain local standard/daylight savings time). If no offset is given for daylight savings time, the default is for it to be exactly one hour east of standard time.

The offset encodes hours and minutes in the form:

[-]hh[:mm]

[,start[/time][,end[/time]]]

This part of the variable gives rules for calculating the exact time for the start and end of daylight savings time, specifying the day of the year, and optionally the hour, minute, and second (in standard time), at which daylight savings time starts and ends. The start and end portions specify the day of the year, and can be in one of three forms:

Mm.w.d
The letter 'M' followed by three numbers, separated by dots, indicates the first day of week 'w' of month 'm', where weeks are considered to start on day 'd' (Sunday = 0).
Jn
The letter 'J' followed by a number, indicating the Nth day of the year (1-365), where February the 29th has no number.
n
A number, indicating the Nth day of the year (1-366).

If the start day is earlier in the year than the end day, as it will be for the Northern Hemisphere, daylight savings time will apply between those days, and standard time otherwise. Otherwise, standard time will apply between the two days, and daylight savings time otherwise.

The time portion specifies the hour, minute, and second, standard time, at which the change occurs. If it is omitted, the default is 02:00:00. Its syntax is:

hh[:mm[:ss]]

The default start and end rules mandated by ISO/IEC 9445-1:1990, if none are supplied, are ",M4.1.0/02:00:00,M10.5.0/02:00:00", which is the rule that applied in (most of) the United States of America from 1987 for a couple of decades. ("First Sunday in April, at 02:00:00 standard time, and last Sunday in October, at 02:00:00 standard time.").

This default is not so useful. The United States rule changed. And the U.S. rule was not uniformly the rule for Europe and other parts of the world in the first place. (Historical note: The ISO/IEC 9445-1:1990 encoding was never capable of correctly encoding the DST rules for the U.K. as set out in the Summer Time Act 1972. Luckily, the U.K. is part of the EC, and EC harmonisation directives override the Summer Time Act each year with simpler daylight savings time rules that can be encoded.)

Some examples:

GMT0BST1,M3.5.0/01:00:00,M10.5.0/01:00:00
The full string for the U.K. including the correct rule for the years 1998 to 2001 (as specified in Statutory Instrument 1997/2982).
GMT0BST,M3.5/1,M10.5/1
A shorter, but functionally identical, form of the above.
CET-1CEST,M3.5.0,M10.5.0
The string for Germany and other central European countries. Notice that the time just happens, because 01:00:00 GMT is 02:00:00 CET, to match the POSIX default, and so can be omitted entirely.
EST-10EDT,M10.5.0,M3.5.0
Australian Eastern Standard/Daylight time.
CST-9:30CDT,M10.5.0,M3.5.0
Australian Central Standard/Daylight time.
EST5EDT,M3.2,M11.1
U.S. Eastern Standard/Daylight time, with the 2007 rule.
PST8PDT,M3.2,M11.1
U.S. Pacific Standard/Daylight time, with the 2007 rule.
NST3:30NDT1:30,M3.2,M11.1
Newfoundland Standard/Daylight time, with the 2007 rule.

© Copyright 2001,2009 Jonathan de Boyne Pollard. "Moral" rights asserted.
Permission is hereby granted to copy and to distribute this web page in its original, unmodified form as long as its last modification datestamp is preserved.