File Coverage

blib/lib/DateTime/Format/W3CDTF.pm
Criterion Covered Total %
statement 49 55 89.0
branch 18 24 75.0
condition n/a
subroutine 9 10 90.0
pod 4 4 100.0
total 80 93 86.0


line stmt bran cond sub pod time code
1             package DateTime::Format::W3CDTF;
2              
3 4     4   430059 use strict;
  4         7  
  4         111  
4 4     4   14 use warnings;
  4         5  
  4         132  
5              
6 4     4   21 use vars qw ($VERSION);
  4         6  
  4         202  
7              
8             $VERSION = '0.07_01';
9              
10 4     4   2395 use DateTime;
  4         975038  
  4         148  
11 4     4   31 use DateTime::TimeZone;
  4         6  
  4         1972  
12              
13             sub new {
14 1     1 1 118 my $class = shift;
15              
16 1         3 return bless {}, $class;
17             }
18              
19             sub parse_datetime {
20 17     17 1 5525 my ( $self, $date ) = @_;
21              
22 17         41 my @fields = qw/ year month day hour minute second fraction time_zone /;
23 17 100       159 my @values =
24             ( $date =~ /^(\d\d\d\d) # Year
25             (?:-(\d\d) # -Month
26             (?:-(\d\d) # -Day
27             (?:T
28             (\d\d):(\d\d) # Hour:Minute
29             (?:
30             :(\d\d) # :Second
31             (\.\d+)? # .Fractional_Second
32             )?
33             ( Z # UTC
34             | [+-]\d\d:\d\d # Hour:Minute TZ offset
35             (?::\d\d)? # :Second TZ offset
36             )?)?)?)?$/x )
37             or die "Invalid W3CDTF datetime string ($date)";
38 9         13 my %p;
39 9         20 for ( my $i=0; $i < @values; $i++ ) { # Oh how I wish Perl had zip
40 72 100       104 next unless defined $values[$i];
41 46         78 $p{$fields[$i]} = $values[$i];
42             }
43            
44             ### support for YYYY-MM-DDT24:00:00 as a syntactic form for 00:00:00 on the day following YYYY-MM-DD
45             ### this is allowed in xsd dateTime syntactic forms, but not W3CDTF.
46             # my $next_day = 0;
47             # if (defined($p{hour}) and defined($p{minute}) and defined($p{second})) {
48             # if ($p{hour} eq '24') {
49             # if ($p{second} eq '00' and $p{minute} eq '00') {
50             # $p{hour} = '00';
51             # $next_day++;
52             # } else {
53             # die "Cannot use hour value '24' with non-zero minutes and seconds\n";
54             # }
55             # }
56             # }
57            
58 9 100       24 if ( !$p{time_zone} ) {
    100          
59 5         7 $p{time_zone} = 'floating';
60             } elsif ( $p{time_zone} eq 'Z' ) {
61 1         2 $p{time_zone} = 'UTC';
62             }
63              
64 9 100       15 if ( $p{fraction} ) {
65 1         5 $p{nanosecond} = $p{fraction} * 1_000_000_000;
66             delete $p{fraction}
67 1         2 }
68              
69 9         35 my $dt = DateTime->new( %p );
70             # if ($next_day) {
71             # $dt->add( day => 1 );
72             # }
73 9         2523 return $dt;
74             }
75              
76             sub format_datetime {
77 5     5 1 18726 my ( $self, $dt ) = @_;
78              
79 5         13 my $base = sprintf(
80             '%04d-%02d-%02dT%02d:%02d:%02d',
81             $dt->year, $dt->month, $dt->day,
82             $dt->hour, $dt->minute, $dt->second
83             );
84              
85 5 50       84 if ( $dt->nanosecond ) {
86 0         0 my $secs = sprintf "%f", $dt->nanosecond / 1_000_000_000;
87 0         0 $secs =~ s/^0//;
88 0         0 $base .= $secs;
89             }
90              
91 5         24 my $tz = $dt->time_zone;
92              
93 5 50       19 return $base if $tz->is_floating;
94              
95 5 50       16 return $base . 'Z' if $tz->is_utc;
96              
97 5         16 my $offset = $dt->offset();
98              
99 5 50       195 return $base unless defined $offset;
100              
101 5         10 return $base . _offset_as_string($offset)
102             }
103              
104             sub format_date {
105 0     0 1 0 my ( $self, $dt ) = @_;
106              
107 0         0 my $base = sprintf( '%04d-%02d-%02d', $dt->year, $dt->month, $dt->day );
108 0         0 return $base;
109             }
110              
111             # minor offset_as_string variant w/ :
112             #
113             sub _offset_as_string {
114 5     5   5 my $offset = shift;
115              
116 5 50       11 return undef unless defined $offset;
117              
118 5 100       8 my $sign = $offset < 0 ? '-' : '+';
119              
120 5         6 my $hours = $offset / ( 60 * 60 );
121 5         6 $hours = abs($hours) % 24;
122              
123 5         5 my $mins = ( $offset % ( 60 * 60 ) ) / 60;
124              
125 5         4 my $secs = $offset % 60;
126              
127             return (
128 5 50       33 $secs
129             ? sprintf( '%s%02d:%02d:%02d', $sign, $hours, $mins, $secs )
130             : sprintf( '%s%02d:%02d', $sign, $hours, $mins )
131             );
132             }
133              
134             1;
135              
136             __END__
137              
138             =head1 NAME
139              
140             DateTime::Format::W3CDTF - Parse and format W3CDTF datetime strings
141              
142             =head1 SYNOPSIS
143              
144             use DateTime::Format::W3CDTF;
145              
146             my $w3c = DateTime::Format::W3CDTF->new;
147             my $dt = $w3c->parse_datetime( '2003-02-15T13:50:05-05:00' );
148              
149             # 2003-02-15T13:50:05-05:00
150             $w3c->format_datetime($dt);
151              
152             =head1 DESCRIPTION
153              
154             This module understands the W3CDTF date/time format, an ISO 8601 profile,
155             defined at http://www.w3.org/TR/NOTE-datetime. This format as the native
156             date format of RSS 1.0.
157              
158             It can be used to parse these formats in order to create the appropriate
159             objects.
160              
161             =head1 METHODS
162              
163             This API is currently experimental and may change in the future.
164              
165             =over 4
166              
167             =item * new()
168              
169             Returns a new W3CDTF parser object.
170              
171             =item * parse_datetime($string)
172              
173             Given a W3CDTF datetime string, this method will return a new
174             C<DateTime> object.
175              
176             If given an improperly formatted string, this method may die.
177              
178             =item * format_datetime($datetime)
179              
180             Given a C<DateTime> object, this methods returns a W3CDTF datetime
181             string.
182              
183             NOTE: As of version 0.4, format_datetime no longer attempts to truncate
184             datetimes without a time component. This is due to the fact that C<DateTime>
185             doesn't distinguish between a date with no time component, and midnight.
186              
187             =item * format_date($datetime)
188              
189             Given a C<DateTime> object, return a W3CDTF datetime string without the time component.
190              
191             =back
192              
193             =head1 SUPPORT
194              
195             Support for this module is provided via the datetime@perl.org email
196             list. See http://datetime.perl.org/?MailingList for details.
197              
198             Please submit bugs to the CPAN RT system at
199             http://rt.cpan.org/NoAuth/ReportBug.html?Queue=datetime-format-w3cdtf or via
200             email at bug-datetime-format-w3cdtf@rt.cpan.org.
201              
202             =head1 AUTHOR
203              
204             Dave Rolsky E<lt>autarch@urth.orgE<gt>
205              
206             =head1 CREDITS
207              
208             This module is maintained by Gregory Todd Williams E<lt>gwilliams@cpan.orgE<gt>.
209             It was originally created by Kellan Elliott-McCrea E<lt>kellan@protest.netE<gt>.
210              
211             This module was inspired by L<DateTime::Format::ICal>
212              
213             =head1 COPYRIGHT
214              
215             Copyright (c) 2009 David Rolsky. All rights reserved. This
216             program is free software; you can redistribute it and/or modify it
217             under the same terms as Perl itself.
218              
219             Copyright (c) 2003 Kellan Elliott-McCrea
220              
221             Portions of the code in this distribution are derived from other
222             works. Please see the CREDITS file for more details.
223              
224             The full text of the license can be found in the LICENSE file included
225             with this module.
226              
227             =head1 SEE ALSO
228              
229             datetime@perl.org mailing list
230              
231             http://datetime.perl.org/
232              
233             =cut