[xsl] Timezone concept broken in XPath 2.0?

Subject: [xsl] Timezone concept broken in XPath 2.0?
From: Michael Ludwig <mlu@xxxxxxxxxxxxx>
Date: Wed, 05 Nov 2008 19:37:29 +0100
I'm bringing this here because it isn't processor specific.

On the Saxon mailing list, I read the following statement by Michael Kay
in reply to someone who had relied on XPath 2.0 timezone adjustment
functions and found them to yield incorrect results after a recent
change back from DST:

adjust-dateTime-to-timezone() with one argument adjusts the time to
the "implicit timezone", which is initialized to the time zone offset
of the machine (or more precisely, the current user's locale). Whereas
you want to adjust it to whatever the civil timezone was at the time
in question. That requires knowledge that Saxon doesn't have available
- daylight savings time starts/ends in different parts of the world on
different dates, and Saxon doesn't know which part of the world you
are in.


Forgive the DPH who is posting Perl, and consider how timezone and DST
intricacies are automatically and reliably handled by standard library
functions, which are, dare I say, timezone-aware:

MiLu@FLUNDER:~ > cat sommerzeit.pl
use strict;
use warnings;
$ENV{TZ} = shift || 'Europe/Berlin';    # timezone parameter
my $ts_switch = 1224980000;             # 26.10.2008 00:13:20 UTC
my $sec_delta = shift || 0;             # delta in seconds
my $ts_start = $ts_switch + $sec_delta;
my $ts_end   = $ts_start + 5000;
while ( $ts_start < $ts_end ) {
    print $ts_start, "\t", scalar localtime $ts_start, "\n";
    $ts_start += 1000;

MiLu@FLUNDER:~ > perl sommerzeit.pl
1224980000      Sun Oct 26 02:13:20 2008
1224981000      Sun Oct 26 02:30:00 2008
1224982000      Sun Oct 26 02:46:40 2008
1224983000      Sun Oct 26 02:03:20 2008    <<--<< switch back
1224984000      Sun Oct 26 02:20:00 2008

MiLu@FLUNDER:~ > perl sommerzeit.pl Europe/Moscow -7200
1224972800      Sun Oct 26 02:13:20 2008
1224973800      Sun Oct 26 02:30:00 2008
1224974800      Sun Oct 26 02:46:40 2008
1224975800      Sun Oct 26 02:03:20 2008    <<--<< switch back
1224976800      Sun Oct 26 02:20:00 2008

As you can see, the C library Perl uses (more precisely, the timezone
database) knows that we switched back from summertime to wintertime in
Germany ten days ago. If I pretend I was in Moscow, I can see that they
did the same thing in that part of Russia two hours earlier (with
respect to UTC). So the C library knows its way around the intricacies
of the world's timezones. There is no need for the user of this library
to keep track of this data - everything is taken care of. This is really
nice to use.

This is possible because the concept of a timezone is not simplistically
reduced to a timezone offset, as it seems to happen in XPath 2.0.
Instead, timezone-awareness is built into functions like localtime().

In Saxon, this shortcoming of XPath 2.0 can be overcome by resorting to
saxon:in-summer-time() and then manually adjusting the time by one hour.

Still, this sounds like a bit of a hack to me. There should be no need
to manually specify how many seconds are reeled in or out when making
the DST switch. Generally, it's a full hour, but who knows? Consider
that Kabul deviates from UTC by +4:30 and Kathmandu by +5:45, so someone
might also have DST deviate by partial hours, and then duration
arithmetics using +/-PT1H falls flat on its nose.

Consider this temporal input data, XSL and output:

# more /t2 timezone.xml
  <!-- Epoch seconds and W3C dates are equivalent here.
  $ date -u -d @1224980000 # using the UNIX date utility
  Sun Oct 26 00:13:20 UTC 2008 -->
  <W3CDate epoch="1224980000">2008-10-26T00:13:20Z</W3CDate>
  <W3CDate epoch="1224981000">2008-10-26T00:30:00Z</W3CDate>
  <W3CDate epoch="1224982000">2008-10-26T00:46:40Z</W3CDate>
  <W3CDate epoch="1224983000">2008-10-26T01:03:20Z</W3CDate>
  <W3CDate epoch="1224984000">2008-10-26T01:20:00Z</W3CDate>

# more /t2 timezone.xsl
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
<xsl:output method="text"/>
<xsl:template match="text()"/>
<xsl:template match="W3CDate">
<xsl:variable name="localized" select="adjust-dateTime-to-timezone(.)"/>
<xsl:variable name="picture" select="'[Y]-[M]-[D] [H]:[m]:[s] [Z]'"/>
<xsl:value-of select="format-dateTime( . , $picture)"/>
<xsl:value-of select="format-dateTime( $localized , $picture)"/>

# saxon timezone.xml timezone.xsl
2008-10-26 0:13:20 Z    2008-10-26 1:13:20 +01:00
2008-10-26 0:30:00 Z    2008-10-26 1:30:00 +01:00
2008-10-26 0:46:40 Z    2008-10-26 1:46:40 +01:00
2008-10-26 1:03:20 Z    2008-10-26 2:03:20 +01:00
2008-10-26 1:20:00 Z    2008-10-26 2:20:00 +01:00

I'm using the implicit timezone, which is Europe/Berlin, which right now
(all of today, 05.11.2008, Gregorian calendar) happens to materialize as
UTC+01:00. But this is now - two weeks ago it was UTC+02:00. That's why
the first three lines of the output are wrong. DST was still in effect.

There are three functions in XPath 2.0 which promise timezone handling:

* adjust-date-to-timezone
* adjust-dateTime-to-timezone
* adjust-time-to-timezone

These functions don't hold what their names promise. The failure is in
shortcutting the timezone to a xs:dayTimeDuration. An xs:dayTimeDuration
is insufficient for this task.

A timezone database should be used, as saxon:in-summer-time() does.
Every platform has one, I guess. Why aren't the functions specified to
use a timezone database and then work like timezone-aware localtime() in
Perl and C?

And shouldn't that be considered for the next edition of XPath?

There are about 25 functions related to dates and times in the XPath 2.0
function library, but none of them seems to address this very common
requirement of localizing temporal data. Instead, what XPath 2.0 sells
as timezone adjustment is more like performing addition and substraction
with temporal data, and in many parts of the world, this looks broken
twice a year. Is it?

Michael Ludwig

Current Thread