Perl DateTime objects, time zones, and ISO8601
Here's what I want to do. DateTime objects conveniently stringify to their ISO8601 date representation. But last week the clocks changed, and all my code broke, because this representation doesn't include the time zone. I want a DateTime object that will stringify to an ISO8601 date including a timezone. Shouldn't be too hard.
Let's look at the perldoc for DateTime::Format::ISO8601. There's an awful lot of things that can be called an ISO8601 date string. 20060101T120000+0100
is a valid date. 2006-01-01T12:00:00+01:00
is also a valid date string. Personally, I prefer the latter. And fortunately, the W3C have looked at this mess and decided 'It's too complicated'. And they agree with me. So this is the string that I want to produce.
DateTime.pm provides a formatter
method - you can define a formatter class that will stringify the object. Horay. But using DateTime::Formatter::ISO8601
doesn't produce a string with a time zone. My next plan is to use DateTime::Format::Strptime
and provide a strptime spec that will produce the right string. To the DateTime / strftime docs! Apparently, %FT%T
produces a nice 2006-01-01T12:00:00
, so all we need is the time zone. And again, here is where we fail. There's %z
, but that produces +0100
, and I need +01:00
.
At this point, I write a hack and get back to work. DateTime lets you use the magic format specifier %{ method_name }
to call a method of the DateTime object. So I push a method into the DateTime namespace that produces the string that I want.
sub DateTime::iso8601_with_tz {
my $self = shift;
my $val = $self->strftime('%FT%T%z');
$val =~ s/(dd)$/:$1/;
return $val;
}
Then I can create a formatter that will format DateTime objects the way I want them.
use DateTime::Format::Strptime;
my $formatter = DateTime::Format::Strptime->new( pattern => "%{iso8601_with_tz}" )
Finally, I can use this as my formatter.
$my_datetime->formatter( $formatter );
Nasty.