Time zone Ambiguities

I was asked recently to organize a meeting with someone in the "UTC-7" time zone (US mid west). This is a common way to represent a time zone, but is not definitive, as can be seen in the highlighted zone below.

Notice the different colors which vary depending on time of year. and particular location within the band. In particular, yellow (Arizona) is always UTC-7 as it doesn't observe DST, but New Mexico or California are only in UTC-7 for (opposite) half of the year. It gets even more complicated in the Navajo nation within Arizona, which does observe DST, but the Hopi reservation within that doesn't!

POSIX time zone ambiguities

In addition to the general confusion above, POSIX adds its own layer of obtuseness, as one refers to the "UTC-7" zone as "UTC+7".
$ TZ=UTC+7 date +%z
-0700
The '+' offset format specified by POSIX is reversed from common sense and from all other formats, including all `date` output formats as can be seen above. Note the "UTC" above is not significant¹, and only used to identify the zone in any output, so the offset "indicates the value added to the local time to arrive at Coordinated Universal Time.". See man timezone for details of the $TZ formats.

I guess as an extension to POSIX, libc could treat "UTC" or "GMT" in $TZ specially, and reverse the sense of any offset. But that would add another inconsistency and also one would have to use the offset like "-0700" above, in the output, rather than "UTC".

¹ Note the "UTC" portion above can be significant, which is another reason to avoid these abbreviated timezone specifications. Consider the following (some of which match entries in /usr/share/zoneinfo/) confusing output:

$ TZ=CEST date       # No match
Tue Sep 27 18:03:05 CEST 2011
$ TZ=CET date        # Match and change
Tue Sep 27 20:03:09 CEST 2011
$ TZ=GB-Eire+1 date  # No zone reported
Tue Sep 27 22:02:49  2011
$ TZ=CET+1 date      # Zone reported (no offset)
Tue Sep 27 20:03:16 CET 2011
$ TZ=Japan+1 date    # Match and no change
Tue Sep 27 21:02:54 Japan 2011
$ TZ=Japan date      # Match and change
Wed Sep 28 07:03:53 JST 2011
$ TZ=Asia/Japan date # No match (Tokyo required)
Wed Jun 20 07:53:15 Asia 2012

Note `date` doesn't warn about unmatched zones because there is no portable API to know if a TZ is not matched. This applies to the following TZ format too.

Location based zones

So due to the ambiguity of the "offset based" time zones described above, it's better to use a "location based" zone, which are not specified by POSIX but are available on GNU/Linux. For example one can specify TZ=America/Los_Angeles which will take daylight savings time into account (which happens at different times in various places, if at all). To select from the available locations, use the tzselect utility. The following example uses this format to show the local time in Phoenix corresponding to 7PM in Dublin.
$ TZ="America/Phoenix" date -R --date='TZ="Europe/Dublin" 19:00 tomorrow'
Tue, Jun  7 11:00:00 -0700

Other time zone gotchas

Even when not directly specifying TZ, one must still be aware of its effects. For example, consider a script that needs to exit unless it's the last day of the month:
[ $(date -d tomorrow +%d) = '01' ] || exit
The above is susceptible to DST fuzzing, with the behavior, on the day of the DST transition, depending on when the script is run. To make it independent of when the script is run, you might think you could use:
[ $(date -d '12:00 +1 day' +%d) = '01' ] || exit
However the +1 is taken as a timezone associated with the time. Thus what you really want is:
[ $(date -d '12:00 today +1 day' +%d) = '01' ] || exit
Also note that time zone peculiarities can be introduced by higher level components in the system. For example see this excellent article on Dealing with Timezones in Python.
© Jul 12 2011