views:

95

answers:

1

Ever since British Summer Time ended in the UK last week my application has been seeing a very interesting bug. Here's an isolated Perl script which demonstrates the issue:

#!/usr/bin/perl

use strict; use warnings;

use DateTime::Format::W3CDTF;
use DateTime::Format::ISO8601;

my $tz = 'Europe/London';

sub print_formatted_date {
  my $date = shift;

  my $tz_date = DateTime::Format::ISO8601->new->parse_datetime( $date );
  $tz_date->set_time_zone( $tz );

  print "tz_date:  $tz_date\n";
  $tz_date->set_formatter( DateTime::Format::W3CDTF->new );

  print "tz_date with W3C formatter: $tz_date\n";
}


print_formatted_date( '2009-10-25' );
print "\n";
print_formatted_date( '2009-10-26' );

The output of this is:

tz_date:  2009-10-25T00:00:00
tz_date with W3C formatter: 2009-10-25T00:00:00+01:00

tz_date:  2009-10-26T00:00:00
tz_date with W3C formatter: 0

Notice that for dates which fall outside of BST the W3C formatter is rendering them as '0'.

This is an issue for me because a third party library which we use is using DateTime::Format::W3CDTF to format paramters during a SOAP call. Because the formatting is failing the calls are failing.

Anyone have any clues? I'm no Perl guru so any help would be really appreciated. Could this be a bug in the DateTime::Format::W3CDTF library?

+3  A: 

Looking at the implementation of W3CDTF I think this might actually be a bug in the library:

sub format_datetime
{
    my ( $self, $dt ) = @_;

    my $base = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d',
     $dt->year, $dt->month, $dt->day,
        $dt->hour, $dt->minute, $dt->second );


    my $tz = $dt->time_zone;

    return $base if $tz->is_floating;

    return $base . 'Z' if $tz->is_utc;

    if (my $offset = $dt->offset()) {
     return $base . offset_as_string($offset );
    }
}

Note that if $tz->is_utc is false, but $dt->offset() is 0 then there is neither return codepath is being hit, which I guess in Perl means that a nil is implicitly returned. I think that scenario is what my sample script is hitting - 'Europe/London' is not technically UTC, but it does still have an offset of 0.

UPDATE

After a bit more research I found that this same bug has already been reported (2 years ago!). The bug report includes a patch which appears to resolve the issue (although I haven't tested that personally).

UPDATE 2

A fix for this has been released

Pete Hodgson
The return value of a perl sub is the value of the last expression evaluated, which in this case is `$offset` (from `if(my $offset = $dt->offset())`), or `0`.
Ether