Skip Menu |

This queue is for tickets about the DateTime-Format-W3CDTF CPAN distribution.

Report information
The Basics
Id: 14179
Status: patched
Priority: 0/
Queue: DateTime-Format-W3CDTF

People
Owner: Nobody in particular
Requestors: gwilliams [...] cpan.org
TURNERA [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: (no value)
Fixed in: (no value)



Subject: Module doesn't parse times that include fractions of a second
As described here: http://www.w3.org/TR/NOTE-datetime Show quoted text
> > Complete date plus hours, minutes, seconds and a decimal fraction of a second > YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00) > where: > s = one or more digits representing a decimal fraction of a second
Here's a patch. (This also changes the handful of tabs located in the source into spaces.)
These do the following: Change all tabs to spaces. Add support for parsing and generating dates with fractional seconds. Add support for parsing time zones with seconds (which the generator was capable of generating). Add a test for parsing dates with fractional seconds. diff -ru DateTime-Format-W3CDTF-0.04/lib/DateTime/Format/W3CDTF.pm DateTime-Format-W3CDTF-Fixed/lib/DateTime/Format/W3CDTF.pm --- DateTime-Format-W3CDTF-0.04/lib/DateTime/Format/W3CDTF.pm 2003-11-23 21:09:56.000000000 -0500 +++ DateTime-Format-W3CDTF-Fixed/lib/DateTime/Format/W3CDTF.pm 2005-08-29 11:08:46.000000000 -0400 @@ -7,6 +7,7 @@ $VERSION = '0.04'; use DateTime; +use DateTime::TimeZone; sub new { @@ -15,92 +16,69 @@ return bless {}, $class; } -# key is string length -my %valid_formats = - ( 19 => - { params => [ qw( year month day hour minute second) ], - regex => qr/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)$/, - zero => {}, - }, - 16 => - { params => [ qw( year month day hour minute) ], - regex => qr/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)$/, - zero => { second => 0 }, - }, - 10 => - { params => [ qw( year month day ) ], - regex => qr/^(\d{4})-(\d\d)-(\d\d)$/, - zero => { hour => 0, minute => 0, second => 0 }, - }, - 7 => - { params => [ qw( year month ) ], - regex => qr/^(\d{4})-(\d\d)$/, - zero => { day => 1, hour => 0, minute => 0, second => 0 }, - }, - 4 => - { params => [ qw( year ) ], - regex => qr/^(\d\d\d\d)$/, - zero => { month => 1, day => 1, hour => 0, minute => 0, second => 0 } - } - ); - sub parse_datetime { my ( $self, $date ) = @_; - # save for error messages - my $original = $date; - - my %p; - if ( $date =~ s/([+-]\d\d:\d\d)$// ) - { - $p{time_zone} = $1; + my @fields = qw/ year month day hour minute second fraction time_zone /; + my @values = + ( $date =~ /^(\d\d\d\d) # Year + (?:-(\d\d) # -Month + (?:-(\d\d) # -Day + (?:T + (\d\d):(\d\d) # Hour:Minute + (?: + :(\d\d) # :Second + (\.\d+)? # .Fractional_Second + )? + ( Z # UTC + | [+-]\d\d:\d\d # Hour:Minute TZ offset + (?::\d\d)? # :Second TZ offset + )?)?)?)?$/x ) + or die "Invalid W3CDTF datetime string ($date)"; + my %p; + for ( my $i=0; $i < @values; $i++ ) { # Oh how I wish Perl had zip + next unless defined $values[$i]; + $p{$fields[$i]} = $values[$i]; } - # Z at end means UTC - elsif ( $date =~ s/Z$// ) - { - $p{time_zone} = 'UTC'; + if ( !$p{time_zone} ) { + $p{time_zone} = 'floating'; } - else + elsif ( $p{time_zone} eq 'Z' ) { - $p{time_zone} = 'floating'; + $p{time_zone} = 'UTC'; } - my $format = $valid_formats{ length $date } - or die "Invalid W3CDTF datetime string ($original)"; - - @p{ @{ $format->{params} } } = $date =~ /$format->{regex}/; + if ( $p{fraction} ) { + $p{nanosecond} = $p{fraction} * 1_000_000_000; + delete $p{fraction} + } - return DateTime->new( %p, %{ $format->{zero} } ); + return DateTime->new( %p ); } sub format_datetime { my ( $self, $dt ) = @_; - # removed in 0.4 as it behaved improperly at midnight - kellan 2003/11/23 - #my $base = - # ( $dt->hour || $dt->min || $dt->sec ? - # sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', - # $dt->year, $dt->month, $dt->day, - # $dt->hour, $dt->minute, $dt->second ) : - # sprintf( '%04d-%02d-%02d', $dt->year, $dt->month, $dt->day ) - # ); - - my $base = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', - $dt->year, $dt->month, $dt->day, + my $base = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', + $dt->year, $dt->month, $dt->day, $dt->hour, $dt->minute, $dt->second ); - + if ( $dt->nanosecond ) { + my $secs = sprintf "%f", $dt->nanosecond / 1_000_000_000; + $secs =~ s/^0//; + $base .= $secs; + } my $tz = $dt->time_zone; return $base if $tz->is_floating; - return $base . 'Z' if $tz->is_utc; + return $base . 'Z' if $tz->is_utc; - if (my $offset = $dt->offset()) { - return $base . offset_as_string($offset ); - } + if (my $offset = $dt->offset()) { + return $base . offset_as_string( $offset ); + } } sub format_date @@ -108,7 +86,7 @@ my ( $self, $dt ) = @_; my $base = sprintf( '%04d-%02d-%02d', $dt->year, $dt->month, $dt->day ); - return $base; + return $base; } # minor offset_as_string variant w/ : diff -ru DateTime-Format-W3CDTF-0.04/t/01parse.t DateTime-Format-W3CDTF-Fixed/t/01parse.t --- DateTime-Format-W3CDTF-0.04/t/01parse.t 2003-11-23 21:09:56.000000000 -0500 +++ DateTime-Format-W3CDTF-Fixed/t/01parse.t 2005-08-29 11:07:37.000000000 -0400 @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -use Test::More tests => 17; +use Test::More tests => 18; use strict; use vars qw( $class ); @@ -9,15 +9,16 @@ } my @tests = ( - '2003-02-10T15:23:45' => '2003-02-10T15:23:45', - '1997-04-11T09:34' => '1997-04-11T09:34:00', - '2002-05-12' => '2002-05-12T00:00:00', - '1985-06' => '1985-06-01T00:00:00', - '1988' => '1988-01-01T00:00:00', - # '2001-02-30' => '2001-03-02T00:00:00', - '2005-03-10T20:14:34+09:30' => '2005-03-10T10:44:34', - '2000-06-12T14:12:33Z' => '2000-06-12T14:12:33', - '1994-11-05T08:15:30-05:00' => '1994-11-05T13:15:30', + '2003-02-10T15:23:45' => '2003-02-10T15:23:45', + '1997-04-11T09:34' => '1997-04-11T09:34:00', + '2002-05-12' => '2002-05-12T00:00:00', + '1985-06' => '1985-06-01T00:00:00', + '1988' => '1988-01-01T00:00:00', + # '2001-02-30' => '2001-03-02T00:00:00', + '2005-03-10T20:14:34+09:30' => '2005-03-10T10:44:34', + '2000-06-12T14:12:33Z' => '2000-06-12T14:12:33', + '1994-11-05T08:15:30-05:00' => '1994-11-05T13:15:30', + '2004-07-01T15:00:13.17-05:00' => '2004-07-01T20:00:13', ); while (@tests) diff -ru DateTime-Format-W3CDTF-0.04/t/02bugs.t DateTime-Format-W3CDTF-Fixed/t/02bugs.t --- DateTime-Format-W3CDTF-0.04/t/02bugs.t 2003-11-23 21:09:56.000000000 -0500 +++ DateTime-Format-W3CDTF-Fixed/t/02bugs.t 2005-08-29 11:08:30.000000000 -0400 @@ -1,7 +1,7 @@ #!/usr/bin/perl -w # test bug 3766 -# http://rt.cpan.org/NoAuth/Bug.html?id=3766 +# http://rt.cpan.org/NoAuth/Bug.html?id=3766 # returns undef when you pass it a DateTime object # whose timezone has been explicitly set. @@ -17,26 +17,26 @@ use DateTime::Format::W3CDTF; my @dates = ( - { date => { year => 1977, month => 11, day => 11, hour => 1, minute => 12, time_zone => 'America/Los_Angeles' }, - w3cdtf => '1977-11-11T01:12:00-08:00', - msg => 'formatter works with explicit timezone', - }, - { date => { year => 1977, month => 4, day => 7, time_zone => 'America/Los_Angeles' }, - w3cdtf => '1977-04-07T00:00:00-08:00', - msg => 'formatter works without timestamp', - }, - { date => { year => 2003, month => 4, day => 7, hour => 2, time_zone => 'America/Los_Angeles' }, - w3cdtf => '2003-04-07T02:00:00-07:00', - msg => 'formatter properly recognizing daylights saving' - }, - { date => { year => 2003, month => 12, day => 25, hour => 0, minute => 00, second => 00, time_zone => 'America/Montreal' }, - w3cdtf => '2003-12-25T00:00:00-05:00', - msg => 'formatter properly formats midnight' - } + { date => { year => 1977, month => 11, day => 11, hour => 1, minute => 12, time_zone => 'America/Los_Angeles' }, + w3cdtf => '1977-11-11T01:12:00-08:00', + msg => 'formatter works with explicit timezone', + }, + { date => { year => 1977, month => 4, day => 7, time_zone => 'America/Los_Angeles' }, + w3cdtf => '1977-04-07T00:00:00-08:00', + msg => 'formatter works without timestamp', + }, + { date => { year => 2003, month => 4, day => 7, hour => 2, time_zone => 'America/Los_Angeles' }, + w3cdtf => '2003-04-07T02:00:00-07:00', + msg => 'formatter properly recognizing daylights saving' + }, + { date => { year => 2003, month => 12, day => 25, hour => 0, minute => 00, second => 00, time_zone => 'America/Montreal' }, + w3cdtf => '2003-12-25T00:00:00-05:00', + msg => 'formatter properly formats midnight' + } ); my $f = DateTime::Format::W3CDTF->new(); foreach my $d ( @dates ) { - my $dt = DateTime->new( %{ $d->{date} } ); - is ( $f->format_datetime($dt), $d->{w3cdtf}, $d->{msg}); -} \ No newline at end of file + my $dt = DateTime->new( %{ $d->{date} } ); + is ( $f->format_datetime($dt), $d->{w3cdtf}, $d->{msg}); +}
That patch is from me.
Subject: parse_datetime fails on valid W3C DTF strings
W3CDTF allows sub-second accuracy with the format YYYY-MM-DDThh:mm:ss.sTZD where, ss = two digits of second (00 through 59) s = one or more digits representing a decimal fraction of a second The DateTime::Format::W3CDTF module seems to base parsing on the fixed length of the string to parse which makes it think valid datetimes such as "1997-07-16T19:20:30.45+01:00", an example used on the W3CDTF page at http://www.w3.org/TR/NOTE-datetime
I'm not really using this module. Would someone like to take it over?
CC: TURNERA [...] cpan.org, gwilliams [...] cpan.org
Subject: Re: [rt.cpan.org #14179] Module doesn't parse times that include fractions of a second
Date: Sat, 16 Oct 2010 16:31:38 -0400
To: bug-DateTime-Format-W3CDTF [...] rt.cpan.org
From: Gregory Williams <greg [...] evilfunhouse.com>
On Oct 16, 2010, at 4:28 PM, Dave Rolsky via RT wrote: Show quoted text
> <URL: https://rt.cpan.org/Ticket/Display.html?id=14179 > > > I'm not really using this module. Would someone like to take it over?
I've got the code patched locally, and intend to release a new version to CPAN as soon as I can find some time to do so. thanks, .greg
Subject: Re: [rt.cpan.org #14179] Module doesn't parse times that include fractions of a second
Date: Sat, 16 Oct 2010 15:47:54 -0500 (CDT)
To: "greg [...] evilfunhouse.com via RT" <bug-DateTime-Format-W3CDTF [...] rt.cpan.org>
From: Dave Rolsky <autarch [...] urth.org>
On Sat, 16 Oct 2010, greg@evilfunhouse.com via RT wrote: Show quoted text
> Queue: DateTime-Format-W3CDTF > Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=14179 > > > On Oct 16, 2010, at 4:28 PM, Dave Rolsky via RT wrote: >
>> <URL: https://rt.cpan.org/Ticket/Display.html?id=14179 > >> >> I'm not really using this module. Would someone like to take it over?
> > I've got the code patched locally, and intend to release a new version to CPAN as soon as I can find some time to do so.
Ah, you already have maint status. I was just going through the bugs that show up for me in RT, and I forgot about this. -dave /*============================================================ http://VegGuide.org http://blog.urth.org Your guide to all that's veg House Absolute(ly Pointless) ============================================================*/
Subject: DateTime-Format-W3CDTF.patch
Index: t/02bugs.t =================================================================== --- t/02bugs.t (revision 4459) +++ t/02bugs.t (working copy) @@ -1,7 +1,7 @@ #!/usr/bin/perl -w # test bug 3766 -# http://rt.cpan.org/NoAuth/Bug.html?id=3766 +# http://rt.cpan.org/NoAuth/Bug.html?id=3766 # returns undef when you pass it a DateTime object # whose timezone has been explicitly set. Index: t/01parse.t =================================================================== --- t/01parse.t (revision 4459) +++ t/01parse.t (working copy) @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -use Test::More tests => 17; +use Test::More tests => 18; use strict; use vars qw( $class ); @@ -17,6 +17,7 @@ '2005-03-10T20:14:34+09:30' => '2005-03-10T10:44:34', '2000-06-12T14:12:33Z' => '2000-06-12T14:12:33', '1994-11-05T08:15:30-05:00' => '1994-11-05T13:15:30', + '2004-07-01T15:00:13.17-05:00' => '2004-07-01T20:00:13', ); while (@tests) { Index: lib/DateTime/Format/W3CDTF.pm =================================================================== --- lib/DateTime/Format/W3CDTF.pm (revision 4459) +++ lib/DateTime/Format/W3CDTF.pm (working copy) @@ -7,6 +7,7 @@ $VERSION = '0.05'; use DateTime; +use DateTime::TimeZone; sub new { my $class = shift; @@ -14,80 +15,65 @@ return bless {}, $class; } -# key is string length -my %valid_formats = ( - 19 => { - params => [qw( year month day hour minute second)], - regex => qr/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)$/, - zero => {}, - }, - 16 => { - params => [qw( year month day hour minute)], - regex => qr/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)$/, - zero => { second => 0 }, - }, - 10 => { - params => [qw( year month day )], - regex => qr/^(\d{4})-(\d\d)-(\d\d)$/, - zero => { hour => 0, minute => 0, second => 0 }, - }, - 7 => { - params => [qw( year month )], - regex => qr/^(\d{4})-(\d\d)$/, - zero => { day => 1, hour => 0, minute => 0, second => 0 }, - }, - 4 => { - params => [qw( year )], - regex => qr/^(\d\d\d\d)$/, - zero => { month => 1, day => 1, hour => 0, minute => 0, second => 0 } - } -); - sub parse_datetime { my ( $self, $date ) = @_; # save for error messages my $original = $date; + my @fields = qw/ year month day hour minute second fraction time_zone /; + my @values = + ( $date =~ /^(\d\d\d\d) # Year + (?:-(\d\d) # -Month + (?:-(\d\d) # -Day + (?:T + (\d\d):(\d\d) # Hour:Minute + (?: + :(\d\d) # :Second + (\.\d+)? # .Fractional_Second + )? + ( Z # UTC + | [+-]\d\d:\d\d # Hour:Minute TZ offset + (?::\d\d)? # :Second TZ offset + )?)?)?)?$/x ) + or die "Invalid W3CDTF datetime string ($date)"; my %p; - if ( $date =~ s/([+-]\d\d:\d\d)$// ) { - $p{time_zone} = $1; + for ( my $i=0; $i < @values; $i++ ) { # Oh how I wish Perl had zip + next unless defined $values[$i]; + $p{$fields[$i]} = $values[$i]; } # Z at end means UTC - elsif ( $date =~ s/Z$// ) { + if ( !$p{time_zone} ) { + $p{time_zone} = 'floating'; + } + elsif ( $p{time_zone} eq 'Z' ) { $p{time_zone} = 'UTC'; } - else { - $p{time_zone} = 'floating'; + + if ( $p{fraction} ) { + $p{nanosecond} = $p{fraction} * 1_000_000_000; + delete $p{fraction} } - my $format = $valid_formats{ length $date } - or die "Invalid W3CDTF datetime string ($original)"; - - @p{ @{ $format->{params} } } = $date =~ /$format->{regex}/; - - return DateTime->new( %p, %{ $format->{zero} } ); + return DateTime->new( %p ); } sub format_datetime { my ( $self, $dt ) = @_; - # removed in 0.4 as it behaved improperly at midnight - kellan 2003/11/23 - #my $base = - # ( $dt->hour || $dt->min || $dt->sec ? - # sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', - # $dt->year, $dt->month, $dt->day, - # $dt->hour, $dt->minute, $dt->second ) : - # sprintf( '%04d-%02d-%02d', $dt->year, $dt->month, $dt->day ) - # ); - my $base = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $dt->year, $dt->month, $dt->day, $dt->hour, $dt->minute, $dt->second ); + if ( $dt->nanosecond ) { + my $secs = sprintf "%f", $dt->nanosecond / 1_000_000_000; + $secs =~ s/^0//; + $base .= $secs; + } + my $tz = $dt->time_zone; return $base if $tz->is_floating;
On Sun Oct 31 10:28:46 2010, SHLOMIF wrote: Show quoted text
> Attached is an up-to-date patch against the trunk: > > https://perl-date-time.svn.sourceforge.net/svnroot/perl-date- > time/modules/DateTime-Format-W3CDTF/trunk
Can you please look into applying this patch? Regards, -- Shlomi Fish
On Sun Jul 29 17:01:06 2012, SHLOMIF wrote: Show quoted text
> Can you please look into applying this patch?
I believe this was applied for version 0.06. However, it seems this ticket was never closed. Can you please confirm, and I'll close the ticket? thanks, .greg