File Coverage

blib/lib/DateTime/Format/ISO8601.pm
Criterion Covered Total %
statement 139 141 98.5
branch 40 42 95.2
condition 8 15 53.3
subroutine 33 34 97.0
pod 11 11 100.0
total 231 243 95.0


line stmt bran cond sub pod time code
1             # Copyright (C) 2003-2012 Joshua Hoblitt
2             package DateTime::Format::ISO8601;
3              
4 8     8   2559819 use strict;
  8         114  
  8         237  
5 8     8   42 use warnings;
  8         15  
  8         223  
6 8     8   2697 use namespace::autoclean;
  8         101654  
  8         46  
7              
8             our $VERSION = '0.15';
9              
10 8     8   655 use Carp qw( croak );
  8         16  
  8         433  
11 8     8   5112 use DateTime 1.45;
  8         2677915  
  8         386  
12 8     8   4886 use DateTime::Format::Builder 0.77;
  8         522944  
  8         59  
13 8     8   4573 use DateTime::Format::ISO8601::Types;
  8         27  
  8         52  
14 8     8   89901 use Params::ValidationCompiler 0.26 qw( validation_for );
  8         217  
  8         41043  
15              
16             {
17             my $validator = validation_for(
18             name => 'DefaultLegacyYear',
19             name_is_optional => 1,
20             params => [ { type => t('Bool') } ],
21             );
22              
23             my $default_legacy_year;
24              
25             sub DefaultLegacyYear {
26 481     481 1 17475 shift;
27 481 100       1681 ($default_legacy_year) = $validator->(@_)
28             if @_;
29              
30 475         1300 return $default_legacy_year;
31             }
32             }
33              
34             __PACKAGE__->DefaultLegacyYear(1);
35              
36             {
37             my $validator = validation_for(
38             name => 'DefaultCutOffYear',
39             name_is_optional => 1,
40             params => [ { type => t('CutOffYear') } ],
41             );
42              
43             my $default_cut_off_year;
44              
45             sub DefaultCutOffYear {
46 10889     10889 1 194439 shift;
47 10889 100       27693 ($default_cut_off_year) = $validator->(@_)
48             if @_;
49              
50 10883         21409 return $default_cut_off_year;
51             }
52             }
53              
54             # the same default value as DT::F::Mail
55             __PACKAGE__->DefaultCutOffYear(49);
56              
57             {
58             my $validator = validation_for(
59             name => '_check_new_params',
60             name_is_optional => 1,
61             params => {
62             base_datetime => {
63             type => t('DateTimeIsh'),
64             optional => 1,
65             },
66             legacy_year => {
67             type => t('Bool'),
68             optional => 1,
69             },
70             cut_off_year => {
71             type => t('CutOffYear'),
72             optional => 1,
73             },
74             },
75             );
76              
77             sub new {
78 574     574 1 429734 my ($class) = shift;
79 574         12482 my %args = $validator->(@_);
80              
81             $args{legacy_year} = $class->DefaultLegacyYear
82 562 100       15778 unless exists $args{legacy_year};
83             $args{cut_off_year} = $class->DefaultCutOffYear
84 562 100       1704 unless exists $args{cut_off_year};
85              
86 562   33     2149 $class = ref($class) || $class;
87              
88 562         1165 my $self = bless( \%args, $class );
89              
90 562 100       1453 if ( $args{base_datetime} ) {
91 122         752 $self->set_base_datetime( object => $args{base_datetime} );
92             }
93              
94 556         1445 return ($self);
95             }
96             }
97              
98             # lifted from DateTime
99 0     0 1 0 sub clone { bless { %{ $_[0] } }, ref $_[0] }
  0         0  
100              
101 1000     1000 1 328602 sub base_datetime { $_[0]->{base_datetime} }
102              
103             {
104             my $validator = validation_for(
105             name => 'set_base_datetime',
106             name_is_optional => 1,
107             params => {
108             object => { type => t('DateTimeIsh') },
109             },
110             );
111              
112             sub set_base_datetime {
113 135     135 1 226 my $self = shift;
114              
115 135         2681 my %args = $validator->(@_);
116              
117             # ISO8601 only allows years 0 to 9999
118             # this implementation ignores the needs of expanded formats
119 135         4291 my $dt = DateTime->from_object( object => $args{object} );
120 135         69732 my $lower_bound = DateTime->new( year => 0 );
121 135         36911 my $upper_bound = DateTime->new( year => 10000 );
122              
123 135 100       36159 if ( $dt < $lower_bound ) {
124 6         359 croak 'base_datetime must be greater then or equal to ',
125             $lower_bound->iso8601;
126             }
127 129 100       9562 if ( $dt >= $upper_bound ) {
128 6         327 croak 'base_datetime must be less then ', $upper_bound->iso8601;
129             }
130              
131 123         7777 $self->{base_datetime} = $dt;
132              
133 123         727 return $self;
134             }
135             }
136              
137 10     10 1 1398 sub legacy_year { $_[0]->{legacy_year} }
138              
139             {
140             my $validator = validation_for(
141             name => 'set_legacy_year',
142             name_is_optional => 1,
143             params => [ { type => t('Bool') } ],
144             );
145              
146             sub set_legacy_year {
147 9     9 1 17 my $self = shift;
148              
149 9         171 ( $self->{legacy_year} ) = $validator->(@_);
150              
151 3         41 return $self;
152             }
153             }
154              
155 501     501 1 109863 sub cut_off_year { $_[0]->{cut_off_year} }
156              
157             {
158             my $validator = validation_for(
159             name => 'set_cut_off_year',
160             name_is_optional => 1,
161             params => [ { type => t('CutOffYear') } ],
162             );
163              
164             sub set_cut_off_year {
165 106     106 1 128 my $self = shift;
166              
167 106         1630 ( $self->{cut_off_year} ) = $validator->(@_);
168              
169 100         884 return $self;
170             }
171             }
172              
173             {
174             my $validator = validation_for(
175             name => 'format_datetime',
176             name_is_optional => 1,
177             params => [ { type => t('DateTime') } ],
178             );
179              
180             sub format_datetime {
181 5     5 1 30889 my $self = shift;
182 5         122 my ($dt) = $validator->(@_);
183              
184 5 100       88 my $cldr
    100          
185             = $dt->nanosecond % 1000000 ? 'yyyy-MM-ddTHH:mm:ss.SSSSSSSSS'
186             : $dt->nanosecond ? 'yyyy-MM-ddTHH:mm:ss.SSS'
187             : 'yyyy-MM-ddTHH:mm:ss';
188              
189 5         54 my $tz;
190 5 100       17 if ( $dt->time_zone->is_utc ) {
191 2         19 $tz = 'Z';
192             }
193             else {
194 3         26 $tz = q{};
195 3         8 $cldr .= 'ZZZZZ';
196             }
197              
198 5         23 return $dt->format_cldr($cldr) . $tz;
199             }
200             }
201              
202             DateTime::Format::Builder->create_class(
203             parsers => {
204             parse_datetime => [
205             {
206             #YYYYMMDD 19850412
207             length => 8,
208             regex => qr/^ (\d{4}) (\d\d) (\d\d) $/x,
209             params => [qw( year month day )],
210             },
211             {
212             # uncombined with above because
213             #regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d) $/x,
214             # was matching 152746-05
215              
216             #YYYY-MM-DD 1985-04-12
217             length => 10,
218             regex => qr/^ (\d{4}) - (\d\d) - (\d\d) $/x,
219             params => [qw( year month day )],
220             },
221             {
222             #YYYY-MM 1985-04
223             length => 7,
224             regex => qr/^ (\d{4}) - (\d\d) $/x,
225             params => [qw( year month )],
226             },
227             {
228             #YYYY 1985
229             length => 4,
230             regex => qr/^ (\d{4}) $/x,
231             params => [qw( year )],
232             },
233             {
234             #YY 19 (century)
235             length => 2,
236             regex => qr/^ (\d\d) $/x,
237             params => [qw( year )],
238             postprocess => \&_normalize_century,
239             },
240             {
241             #YYMMDD 850412
242             #YY-MM-DD 85-04-12
243             length => [qw( 6 8 )],
244             regex => qr/^ (\d\d) -?? (\d\d) -?? (\d\d) $/x,
245             params => [qw( year month day )],
246             postprocess => \&_fix_2_digit_year,
247             },
248             {
249             #-YYMM -8504
250             #-YY-MM -85-04
251             length => [qw( 5 6 )],
252             regex => qr/^ - (\d\d) -?? (\d\d) $/x,
253             params => [qw( year month )],
254             postprocess => \&_fix_2_digit_year,
255             },
256             {
257             #-YY -85
258             length => 3,
259             regex => qr/^ - (\d\d) $/x,
260             params => [qw( year )],
261             postprocess => \&_fix_2_digit_year,
262             },
263             {
264             #--MMDD --0412
265             #--MM-DD --04-12
266             length => [qw( 6 7 )],
267             regex => qr/^ -- (\d\d) -?? (\d\d) $/x,
268             params => [qw( month day )],
269             postprocess => \&_add_year,
270             },
271             {
272             #--MM --04
273             length => 4,
274             regex => qr/^ -- (\d\d) $/x,
275             params => [qw( month )],
276             postprocess => \&_add_year,
277             },
278             {
279             #---DD ---12
280             length => 5,
281             regex => qr/^ --- (\d\d) $/x,
282             params => [qw( day )],
283             postprocess => [ \&_add_year, \&_add_month ],
284             },
285             {
286             #+[YY]YYYYMMDD +0019850412
287             #+[YY]YYYY-MM-DD +001985-04-12
288             length => [qw( 11 13 )],
289             regex => qr/^ \+ (\d{6}) -?? (\d\d) -?? (\d\d) $/x,
290             params => [qw( year month day )],
291             },
292             {
293             #+[YY]YYYY-MM +001985-04
294             length => 10,
295             regex => qr/^ \+ (\d{6}) - (\d\d) $/x,
296             params => [qw( year month )],
297             },
298             {
299             #+[YY]YYYY +001985
300             length => 7,
301             regex => qr/^ \+ (\d{6}) $/x,
302             params => [qw( year )],
303             },
304             {
305             #+[YY]YY +0019 (century)
306             length => 5,
307             regex => qr/^ \+ (\d{4}) $/x,
308             params => [qw( year )],
309             postprocess => \&_normalize_century,
310             },
311             {
312             #YYYYDDD 1985102
313             #YYYY-DDD 1985-102
314             length => [qw( 7 8 )],
315             regex => qr/^ (\d{4}) -?? (\d{3}) $/x,
316             params => [qw( year day_of_year )],
317             constructor => [ 'DateTime', 'from_day_of_year' ],
318             },
319             {
320             #YYDDD 85102
321             #YY-DDD 85-102
322             length => [qw( 5 6 )],
323             regex => qr/^ (\d\d) -?? (\d{3}) $/x,
324             params => [qw( year day_of_year )],
325             postprocess => [ \&_fix_2_digit_year ],
326             constructor => [ 'DateTime', 'from_day_of_year' ],
327             },
328             {
329             #-DDD -102
330             length => 4,
331             regex => qr/^ - (\d{3}) $/x,
332             params => [qw( day_of_year )],
333             postprocess => [ \&_add_year ],
334             constructor => [ 'DateTime', 'from_day_of_year' ],
335             },
336             {
337             #+[YY]YYYYDDD +001985102
338             #+[YY]YYYY-DDD +001985-102
339             length => [qw( 10 11 )],
340             regex => qr/^ \+ (\d{6}) -?? (\d{3}) $/x,
341             params => [qw( year day_of_year )],
342             constructor => [ 'DateTime', 'from_day_of_year' ],
343             },
344             {
345             #YYYYWwwD 1985W155
346             #YYYY-Www-D 1985-W15-5
347             length => [qw( 8 10 )],
348             regex => qr/^ (\d{4}) -?? W (\d\d) -?? (\d) $/x,
349             params => [qw( year week day_of_week )],
350             postprocess => [ \&_normalize_week ],
351             constructor => [ 'DateTime', 'from_day_of_year' ],
352             },
353             {
354             #YYYYWww 1985W15
355             #YYYY-Www 1985-W15
356             length => [qw( 7 8 )],
357             regex => qr/^ (\d{4}) -?? W (\d\d) $/x,
358             params => [qw( year week )],
359             postprocess => [ \&_normalize_week ],
360             constructor => [ 'DateTime', 'from_day_of_year' ],
361             },
362             {
363             #YYWwwD 85W155
364             #YY-Www-D 85-W15-5
365             length => [qw( 6 8 )],
366             regex => qr/^ (\d\d) -?? W (\d\d) -?? (\d) $/x,
367             params => [qw( year week day_of_week )],
368             postprocess => [ \&_fix_2_digit_year, \&_normalize_week ],
369             constructor => [ 'DateTime', 'from_day_of_year' ],
370             },
371             {
372             #YYWww 85W15
373             #YY-Www 85-W15
374             length => [qw( 5 6 )],
375             regex => qr/^ (\d\d) -?? W (\d\d) $/x,
376             params => [qw( year week )],
377             postprocess => [ \&_fix_2_digit_year, \&_normalize_week ],
378             constructor => [ 'DateTime', 'from_day_of_year' ],
379             },
380             {
381             #-YWwwD -5W155
382             #-Y-Www-D -5-W15-5
383             length => [qw( 6 8 )],
384             regex => qr/^ - (\d) -?? W (\d\d) -?? (\d) $/x,
385             params => [qw( year week day_of_week )],
386             postprocess => [ \&_fix_1_digit_year, \&_normalize_week ],
387             constructor => [ 'DateTime', 'from_day_of_year' ],
388             },
389             {
390             #-YWww -5W15
391             #-Y-Www -5-W15
392             length => [qw( 5 6 )],
393             regex => qr/^ - (\d) -?? W (\d\d) $/x,
394             params => [qw( year week )],
395             postprocess => [ \&_fix_1_digit_year, \&_normalize_week ],
396             constructor => [ 'DateTime', 'from_day_of_year' ],
397             },
398             {
399             #-WwwD -W155
400             #-Www-D -W15-5
401             length => [qw( 5 6 )],
402             regex => qr/^ - W (\d\d) -?? (\d) $/x,
403             params => [qw( week day_of_week )],
404             postprocess => [ \&_add_year, \&_normalize_week ],
405             constructor => [ 'DateTime', 'from_day_of_year' ],
406             },
407             {
408             #-Www -W15
409             length => 4,
410             regex => qr/^ - W (\d\d) $/x,
411             params => [qw( week )],
412             postprocess => [ \&_add_year, \&_normalize_week ],
413             constructor => [ 'DateTime', 'from_day_of_year' ],
414             },
415             {
416             #-W-D -W-5
417             length => 4,
418             regex => qr/^ - W - (\d) $/x,
419             params => [qw( day_of_week )],
420             postprocess => [
421             \&_add_year,
422             \&_add_week,
423             \&_normalize_week,
424             ],
425             constructor => [ 'DateTime', 'from_day_of_year' ],
426             },
427             {
428             #+[YY]YYYYWwwD +001985W155
429             #+[YY]YYYY-Www-D +001985-W15-5
430             length => [qw( 11 13 )],
431             regex => qr/^ \+ (\d{6}) -?? W (\d\d) -?? (\d) $/x,
432             params => [qw( year week day_of_week )],
433             postprocess => [ \&_normalize_week ],
434             constructor => [ 'DateTime', 'from_day_of_year' ],
435             },
436             {
437             #+[YY]YYYYWww +001985W15
438             #+[YY]YYYY-Www +001985-W15
439             length => [qw( 10 11 )],
440             regex => qr/^ \+ (\d{6}) -?? W (\d\d) $/x,
441             params => [qw( year week )],
442             postprocess => [ \&_normalize_week ],
443             constructor => [ 'DateTime', 'from_day_of_year' ],
444             },
445             {
446             #hhmmss 232050 - skipped
447             #hh:mm:ss 23:20:50
448             length => [qw( 8 9 )],
449             regex => qr/^ T?? (\d\d) : (\d\d) : (\d\d) $/x,
450             params => [qw( hour minute second)],
451             postprocess => [
452             \&_add_year,
453             \&_add_month,
454             \&_add_day
455             ],
456             },
457              
458             #hhmm 2320 - skipped
459             #hh 23 -skipped
460             {
461             #hh:mm 23:20
462             length => [qw( 4 5 6 )],
463             regex => qr/^ T?? (\d\d) :?? (\d\d) $/x,
464             params => [qw( hour minute )],
465             postprocess => [
466             \&_add_year,
467             \&_add_month,
468             \&_add_day
469             ],
470             },
471             {
472             #hhmmss,ss 232050,5
473             #hh:mm:ss,ss 23:20:50,5
474             regex =>
475             qr/^ T?? (\d\d) :?? (\d\d) :?? (\d\d) [\.,] (\d+) $/x,
476             params => [qw( hour minute second nanosecond)],
477             postprocess => [
478             \&_add_year,
479             \&_add_month,
480             \&_add_day,
481             \&_fractional_second
482             ],
483             },
484             {
485             #hhmm,mm 2320,8
486             #hh:mm,mm 23:20,8
487             regex => qr/^ T?? (\d\d) :?? (\d\d) [\.,] (\d+) $/x,
488             params => [qw( hour minute second )],
489             postprocess => [
490             \&_add_year,
491             \&_add_month,
492             \&_add_day,
493             \&_fractional_minute
494             ],
495             },
496             {
497             #hh,hh 23,3
498             regex => qr/^ T?? (\d\d) [\.,] (\d+) $/x,
499             params => [qw( hour minute )],
500             postprocess => [
501             \&_add_year,
502             \&_add_month,
503             \&_add_day,
504             \&_fractional_hour
505             ],
506             },
507             {
508             #-mmss -2050 - skipped
509             #-mm:ss -20:50
510             length => 6,
511             regex => qr/^ - (\d\d) : (\d\d) $/x,
512             params => [qw( minute second )],
513             postprocess => [
514             \&_add_year,
515             \&_add_month,
516             \&_add_day,
517             \&_add_hour
518             ],
519             },
520              
521             #-mm -20 - skipped
522             #--ss --50 - skipped
523             {
524             #-mmss,s -2050,5
525             #-mm:ss,s -20:50,5
526             regex => qr/^ - (\d\d) :?? (\d\d) [\.,] (\d+) $/x,
527             params => [qw( minute second nanosecond )],
528             postprocess => [
529             \&_add_year,
530             \&_add_month,
531             \&_add_day,
532             \&_add_hour,
533             \&_fractional_second
534             ],
535             },
536             {
537             #-mm,m -20,8
538             regex => qr/^ - (\d\d) [\.,] (\d+) $/x,
539             params => [qw( minute second )],
540             postprocess => [
541             \&_add_year,
542             \&_add_month,
543             \&_add_day,
544             \&_add_hour,
545             \&_fractional_minute
546             ],
547             },
548             {
549             #--ss,s --50,5
550             regex => qr/^ -- (\d\d) [\.,] (\d+) $/x,
551             params => [qw( second nanosecond)],
552             postprocess => [
553             \&_add_year,
554             \&_add_month,
555             \&_add_day,
556             \&_add_hour,
557             \&_add_minute,
558             \&_fractional_second,
559             ],
560             },
561             {
562             #hhmmssZ 232030Z
563             #hh:mm:ssZ 23:20:30Z
564             length => [qw( 7 8 9 10 )],
565             regex => qr/^ T?? (\d\d) :?? (\d\d) :?? (\d\d) Z $/x,
566             params => [qw( hour minute second )],
567             extra => { time_zone => 'UTC' },
568             postprocess => [
569             \&_add_year,
570             \&_add_month,
571             \&_add_day,
572             ],
573             },
574              
575             {
576             #hhmmss.ssZ 232030.5Z
577             #hh:mm:ss.ssZ 23:20:30.5Z
578             regex =>
579             qr/^ T?? (\d\d) :?? (\d\d) :?? (\d\d) [\.,] (\d+) Z $/x,
580             params => [qw( hour minute second nanosecond)],
581             extra => { time_zone => 'UTC' },
582             postprocess => [
583             \&_add_year,
584             \&_add_month,
585             \&_add_day,
586             \&_fractional_second
587             ],
588             },
589              
590             {
591             #hhmmZ 2320Z
592             #hh:mmZ 23:20Z
593             length => [qw( 5 6 7 )],
594             regex => qr/^ T?? (\d\d) :?? (\d\d) Z $/x,
595             params => [qw( hour minute )],
596             extra => { time_zone => 'UTC' },
597             postprocess => [
598             \&_add_year,
599             \&_add_month,
600             \&_add_day,
601             ],
602             },
603             {
604             #hhZ 23Z
605             length => [qw( 3 4 )],
606             regex => qr/^ T?? (\d\d) Z $/x,
607             params => [qw( hour )],
608             extra => { time_zone => 'UTC' },
609             postprocess => [
610             \&_add_year,
611             \&_add_month,
612             \&_add_day,
613             ],
614             },
615             {
616             #hhmmss[+-]hhmm 152746+0100 152746-0500
617             #hh:mm:ss[+-]hh:mm 15:27:46+01:00 15:27:46-05:00
618             length => [qw( 11 12 14 15 )],
619             regex => qr/^ T?? (\d\d) :?? (\d\d) :?? (\d\d)
620             ([+-] \d\d :?? \d\d) $/x,
621             params => [qw( hour minute second time_zone )],
622             postprocess => [
623             \&_add_year,
624             \&_add_month,
625             \&_add_day,
626             \&_normalize_offset,
627             ],
628             },
629             {
630             #hhmmss.ss[+-]hhmm 152746.5+0100 152746.5-0500
631             #hh:mm:ss.ss[+-]hh:mm 15:27:46.5+01:00 15:27:46.5-05:00
632             regex => qr/^ T?? (\d\d) :?? (\d\d) :?? (\d\d) [\.,] (\d+)
633             ([+-] \d\d :?? \d\d) $/x,
634             params => [qw( hour minute second nanosecond time_zone )],
635             postprocess => [
636             \&_add_year,
637             \&_add_month,
638             \&_add_day,
639             \&_fractional_second,
640             \&_normalize_offset,
641             ],
642             },
643              
644             {
645             #hhmmss[+-]hh 152746+01 152746-05
646             #hh:mm:ss[+-]hh 15:27:46+01 15:27:46-05
647             length => [qw( 9 10 11 12 )],
648             regex => qr/^ T?? (\d\d) :?? (\d\d) :?? (\d\d)
649             ([+-] \d\d) $/x,
650             params => [qw( hour minute second time_zone )],
651             postprocess => [
652             \&_add_year,
653             \&_add_month,
654             \&_add_day,
655             \&_normalize_offset,
656             ],
657             },
658             {
659             #YYYYMMDDThhmmss 19850412T101530
660             #YYYY-MM-DDThh:mm:ss 1985-04-12T10:15:30
661             length => [qw( 15 19 )],
662             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
663             T (\d\d) :?? (\d\d) :?? (\d\d) $/x,
664             params => [qw( year month day hour minute second )],
665             extra => { time_zone => 'floating' },
666             },
667             {
668             #YYYYMMDDThhmmss.ss 19850412T101530.123
669             #YYYY-MM-DDThh:mm:ss.ss 1985-04-12T10:15:30.123
670             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
671             T (\d\d) :?? (\d\d) :?? (\d\d) [\.,] (\d+) $/x,
672             params =>
673             [qw( year month day hour minute second nanosecond )],
674             extra => { time_zone => 'floating' },
675             postprocess => [
676             \&_fractional_second,
677             ],
678             },
679             {
680             #YYYYMMDDThhmmssZ 19850412T101530Z
681             #YYYY-MM-DDThh:mm:ssZ 1985-04-12T10:15:30Z
682             length => [qw( 16 20 )],
683             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
684             T (\d\d) :?? (\d\d) :?? (\d\d) Z $/x,
685             params => [qw( year month day hour minute second )],
686             extra => { time_zone => 'UTC' },
687             },
688             {
689             #YYYYMMDDThhmmss.ssZ 19850412T101530.5Z 20041020T101530.5Z
690             #YYYY-MM-DDThh:mm:ss.ssZ 1985-04-12T10:15:30.5Z 1985-04-12T10:15:30.5Z
691             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
692             T?? (\d\d) :?? (\d\d) :?? (\d\d) [\.,] (\d+)
693             Z$/x,
694             params =>
695             [qw( year month day hour minute second nanosecond )],
696             extra => { time_zone => 'UTC' },
697             postprocess => [
698             \&_fractional_second,
699             ],
700             },
701              
702             {
703             #YYYYMMDDThhmmss[+-]hhmm 19850412T101530+0400
704             #YYYY-MM-DDThh:mm:ss[+-]hh:mm 1985-04-12T10:15:30+04:00
705             length => [qw( 20 24 25 )],
706             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
707             T (\d\d) :?? (\d\d) :?? (\d\d) ([+-] \d\d :?? \d\d) $/x,
708             params => [qw( year month day hour minute second time_zone )],
709             postprocess => \&_normalize_offset,
710             },
711             {
712             #YYYYMMDDThhmmss.ss[+-]hhmm 19850412T101530.5+0100 20041020T101530.5-0500
713             regex => qr/^ (\d{4}) (\d\d) (\d\d)
714             T?? (\d\d) (\d\d) (\d\d) [\.,] (\d+)
715             ([+-] \d\d \d\d) $/x,
716             params => [
717             qw( year month day hour minute second nanosecond time_zone )
718             ],
719             postprocess => [
720             \&_fractional_second,
721             \&_normalize_offset,
722             ],
723             },
724             {
725             #YYYY-MM-DDThh:mm:ss.ss[+-]hh 1985-04-12T10:15:30.5+01 1985-04-12T10:15:30.5-05
726             regex => qr/^ (\d{4}) - (\d\d) - (\d\d)
727             T?? (\d\d) : (\d\d) : (\d\d) [\.,] (\d+)
728             ([+-] \d\d ) $/x,
729             params => [
730             qw( year month day hour minute second nanosecond time_zone )
731             ],
732             postprocess => [
733             \&_fractional_second,
734             \&_normalize_offset,
735             ],
736             },
737             {
738             #YYYYMMDDThhmmss.ss[+-]hh 19850412T101530.5+01 20041020T101530.5-05
739             regex => qr/^ (\d{4}) (\d\d) (\d\d)
740             T?? (\d\d) (\d\d) (\d\d) [\.,] (\d+)
741             ([+-] \d\d ) $/x,
742             params => [
743             qw( year month day hour minute second nanosecond time_zone )
744             ],
745             postprocess => [
746             \&_fractional_second,
747             \&_normalize_offset,
748             ],
749             },
750             {
751             #YYYY-MM-DDThh:mm:ss.ss[+-]hh:mm 1985-04-12T10:15:30.5+01:00 1985-04-12T10:15:30.5-05:00
752             regex => qr/^ (\d{4}) - (\d\d) - (\d\d)
753             T?? (\d\d) : (\d\d) : (\d\d) [\.,] (\d+)
754             ([+-] \d\d : \d\d) $/x,
755             params => [
756             qw( year month day hour minute second nanosecond time_zone )
757             ],
758             postprocess => [
759             \&_fractional_second,
760             \&_normalize_offset,
761             ],
762             },
763              
764             {
765             #YYYYMMDDThhmmss[+-]hh 19850412T101530+04
766             #YYYY-MM-DDThh:mm:ss[+-]hh 1985-04-12T10:15:30+04
767             length => [qw( 18 22 )],
768             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
769             T (\d\d) :?? (\d\d) :?? (\d\d) ([+-] \d\d) $/x,
770             params => [qw( year month day hour minute second time_zone )],
771             postprocess => \&_normalize_offset,
772             },
773             {
774             #YYYYMMDDThhmm 19850412T1015
775             #YYYY-MM-DDThh:mm 1985-04-12T10:15
776             length => [qw( 13 16 )],
777             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
778             T (\d\d) :?? (\d\d) $/x,
779             params => [qw( year month day hour minute )],
780             extra => { time_zone => 'floating' },
781             },
782             {
783             #YYYYMMDDThhmmZ 19850412T1015
784             #YYYY-MM-DDThh:mmZ 1985-04-12T10:15
785             length => [qw( 14 17 )],
786             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
787             T (\d\d) :?? (\d\d) Z $/x,
788             params => [qw( year month day hour minute )],
789             extra => { time_zone => 'UTC' },
790             },
791             {
792             #YYYYDDDThhmm 1985102T1015
793             #YYYY-DDDThh:mm 1985-102T10:15
794             length => [qw( 12 14 )],
795             regex => qr/^ (\d{4}) -?? (\d{3}) T
796             (\d\d) :?? (\d\d) $/x,
797             params => [qw( year day_of_year hour minute )],
798             extra => { time_zone => 'floating' },
799             constructor => [ 'DateTime', 'from_day_of_year' ],
800             },
801             {
802             #YYYYDDDThhmmZ 1985102T1015Z
803             #YYYY-DDDThh:mmZ 1985-102T10:15Z
804             length => [qw( 13 15 )],
805             regex => qr/^ (\d{4}) -?? (\d{3}) T
806             (\d\d) :?? (\d\d) Z $/x,
807             params => [qw( year day_of_year hour minute )],
808             extra => { time_zone => 'UTC' },
809             constructor => [ 'DateTime', 'from_day_of_year' ],
810              
811             },
812             {
813             #YYYYWwwDThhmm[+-]hhmm 1985W155T1015+0400
814             #YYYY-Www-DThh:mm[+-]hh 1985-W15-5T10:15+04
815             length => [qw( 18 19 )],
816             regex => qr/^ (\d{4}) -?? W (\d\d) -?? (\d)
817             T (\d\d) :?? (\d\d) ([+-] \d{2,4}) $/x,
818             params => [qw( year week day_of_week hour minute time_zone)],
819             postprocess => [ \&_normalize_week, \&_normalize_offset ],
820             constructor => [ 'DateTime', 'from_day_of_year' ],
821             },
822             ],
823             parse_time => [
824             {
825             #hhmmss 232050
826             length => [qw( 6 7 )],
827             regex => qr/^ T?? (\d\d) (\d\d) (\d\d) $/x,
828             params => [qw( hour minute second )],
829             postprocess => [
830             \&_add_year,
831             \&_add_month,
832             \&_add_day,
833             ],
834             },
835             {
836             #hhmm 2320
837             length => [qw( 4 5 )],
838             regex => qr/^ T?? (\d\d) (\d\d) $/x,
839             params => [qw( hour minute )],
840             postprocess => [
841             \&_add_year,
842             \&_add_month,
843             \&_add_day,
844             ],
845             },
846             {
847             #hh 23
848             length => [qw( 2 3 )],
849             regex => qr/^ T?? (\d\d) $/x,
850             params => [qw( hour )],
851             postprocess => [
852             \&_add_year,
853             \&_add_month,
854             \&_add_day,
855             ],
856             },
857             {
858             #-mmss -2050
859             length => 5,
860             regex => qr/^ - (\d\d) (\d\d) $/x,
861             params => [qw( minute second )],
862             postprocess => [
863             \&_add_year,
864             \&_add_month,
865             \&_add_day,
866             \&_add_hour,
867             ],
868             },
869             {
870             #-mm -20
871             length => 3,
872             regex => qr/^ - (\d\d) $/x,
873             params => [qw( minute )],
874             postprocess => [
875             \&_add_year,
876             \&_add_month,
877             \&_add_day,
878             \&_add_hour,
879             ],
880             },
881             {
882             #--ss --50
883             length => 4,
884             regex => qr/^ -- (\d\d) $/x,
885             params => [qw( second )],
886             postprocess => [
887             \&_add_year,
888             \&_add_month,
889             \&_add_day,
890             \&_add_hour,
891             \&_add_minute,
892             ],
893             },
894             ],
895             }
896             );
897              
898             sub _fix_1_digit_year {
899 12     12   26093 my %p = @_;
900              
901 12         43 my $year = _base_dt( $p{self} )->year;
902              
903 12         2481 $year =~ s/.$//;
904 12         39 $p{parsed}{year} = $year . $p{parsed}{year};
905              
906 12         36 return 1;
907             }
908              
909             sub _fix_2_digit_year {
910 21033     21033   15958228 my %p = @_;
911              
912             # this is a mess because of the need to support parse_* being called
913             # as a class method
914 21033 100 66     95529 if ( ref $p{self} && exists $p{self}{legacy_year} ) {
915 11022 100       25584 if ( $p{self}{legacy_year} ) {
916             my $cutoff
917             = exists $p{self}{cut_off_year}
918             ? $p{self}{cut_off_year}
919 10022 50       23014 : $p{self}->DefaultCutOffYear;
920 10022 100       35352 $p{parsed}{year} += $p{parsed}{year} > $cutoff ? 1900 : 2000;
921             }
922             else {
923 1000   33     3839 my $century = ( $p{self}{base_datetime} || DateTime->now )
924             ->strftime('%C');
925 1000         34957 $p{parsed}{year} += $century * 100;
926             }
927             }
928             else {
929             my $cutoff
930             = ref $p{self} && exists $p{self}{cut_off_year}
931             ? $p{self}{cut_off_year}
932 10011 50 33     31779 : $p{self}->DefaultCutOffYear;
933 10011 100       33171 $p{parsed}{year} += $p{parsed}{year} > $cutoff ? 1900 : 2000;
934             }
935              
936 21033         65456 return 1;
937             }
938              
939             sub _add_minute {
940 11     11   101 my %p = @_;
941              
942 11         40 $p{parsed}{minute} = _base_dt( $p{self} )->minute;
943              
944 11         1173 return 1;
945             }
946              
947             sub _add_hour {
948 29     29   283 my %p = @_;
949              
950 29         67 $p{parsed}{hour} = _base_dt( $p{self} )->hour;
951              
952 29         4321 return 1;
953             }
954              
955             sub _add_day {
956 116     116   992 my %p = @_;
957              
958 116         247 $p{parsed}{day} = _base_dt( $p{self} )->day;
959              
960 116         20144 return 1;
961             }
962              
963             sub _add_week {
964 8     8   66 my %p = @_;
965              
966 8         21 $p{parsed}{week} = _base_dt( $p{self} )->week;
967              
968 8         847 return 1;
969             }
970              
971             sub _add_month {
972 119     119   1129 my %p = @_;
973              
974 119         282 $p{parsed}{month} = _base_dt( $p{self} )->month;
975              
976 119         20758 return 1;
977             }
978              
979             sub _add_year {
980 142     142   354528 my %p = @_;
981              
982 142         419 $p{parsed}{year} = _base_dt( $p{self} )->year;
983              
984 142         26539 return 1;
985             }
986              
987             sub _base_dt {
988 437 100 100 437   1970 return $_[0]{base_datetime} if ref $_[0] && $_[0]{base_datetime};
989 268         849 return DateTime->now;
990             }
991              
992             sub _fractional_second {
993 62     62   113615 my %p = @_;
994              
995             ## no critic (ValuesAndExpressions::ProhibitMismatchedOperators)
996 62         331 $p{parsed}{nanosecond} = ".$p{ parsed }{ nanosecond }" * 10**9;
997              
998 62         180 return 1;
999             }
1000              
1001             sub _fractional_minute {
1002 9     9   86 my %p = @_;
1003              
1004             ## no critic (ValuesAndExpressions::ProhibitMismatchedOperators)
1005 9         47 $p{parsed}{second} = ".$p{ parsed }{ second }" * 60;
1006              
1007 9         28 return 1;
1008             }
1009              
1010             sub _fractional_hour {
1011 3     3   29 my %p = @_;
1012              
1013             ## no critic (ValuesAndExpressions::ProhibitMismatchedOperators)
1014 3         19 $p{parsed}{minute} = ".$p{ parsed }{ minute }" * 60;
1015              
1016 3         10 return 1;
1017             }
1018              
1019             sub _normalize_offset {
1020 66     66   36941 my %p = @_;
1021              
1022 66         217 $p{parsed}{time_zone} =~ s/://;
1023              
1024 66 100       224 if ( length $p{parsed}{time_zone} == 3 ) {
1025 27         64 $p{parsed}{time_zone} .= '00';
1026             }
1027              
1028 66         177 return 1;
1029             }
1030              
1031             sub _normalize_week {
1032 3980     3980   15497024 my %p = @_;
1033              
1034             # See
1035             # https://en.wikipedia.org/wiki/ISO_week_date#Calculating_an_ordinal_or_month_date_from_a_week_date
1036             # for the algorithm we're using here.
1037 3980         13700 my $od = $p{parsed}{week} * 7;
1038 3980 100       12524 $od += ( exists $p{parsed}{day_of_week} ? $p{parsed}{day_of_week} : 1 );
1039             $od -= DateTime->new(
1040             year => $p{parsed}{year},
1041 3980         15327 month => 1,
1042             day => 4,
1043             )->day_of_week + 3;
1044              
1045 3980         1221395 my ( $year, $day_of_year );
1046 3980 100       10344 if ( $od < 1 ) {
1047 20         61 $year = $p{parsed}{year} - 1;
1048 20         60 $day_of_year = DateTime->new( year => $year )->year_length + $od;
1049             }
1050             else {
1051             my $year_length
1052 3960         12105 = DateTime->new( year => $p{parsed}{year} )->year_length;
1053 3960 100       1174872 if ( $od > $year_length ) {
1054 6         19 $year = $p{parsed}{year} + 1;
1055 6         17 $day_of_year = $od - $year_length;
1056             }
1057             else {
1058 3954         9436 $year = $p{parsed}{year};
1059 3954         7072 $day_of_year = $od;
1060             }
1061             }
1062              
1063             # We need to leave the references in $p{parsed} as is. We cannot create a
1064             # new reference.
1065 3980         14355 $p{parsed}{year} = $year;
1066 3980         8182 $p{parsed}{day_of_year} = $day_of_year;
1067              
1068 3980         7917 delete $p{parsed}{week};
1069 3980         6633 delete $p{parsed}{day_of_week};
1070              
1071 3980         12865 return 1;
1072             }
1073              
1074             sub _normalize_century {
1075 6     6   73781 my %p = @_;
1076              
1077 6         21 $p{parsed}{year} .= '01';
1078              
1079 6         23 return 1;
1080             }
1081              
1082             1;
1083              
1084             # ABSTRACT: Parses ISO8601 formats
1085              
1086             __END__
1087              
1088             =pod
1089              
1090             =encoding UTF-8
1091              
1092             =head1 NAME
1093              
1094             DateTime::Format::ISO8601 - Parses ISO8601 formats
1095              
1096             =head1 VERSION
1097              
1098             version 0.15
1099              
1100             =head1 SYNOPSIS
1101              
1102             use DateTime::Format::ISO8601;
1103              
1104             my $datetime_str = '2020-07-25T11:32:31';
1105             my $dt = DateTime::Format::ISO8601->parse_datetime($datetime_str);
1106             say $dt;
1107              
1108             # This format is ambiguous and could be either a date or time, so use the
1109             # parse_time method.
1110             my $time_str = '113231';
1111             $dt = DateTime::Format::ISO8601->parse_time($time_str);
1112             say $dt;
1113              
1114             # or
1115              
1116             my $iso8601 = DateTime::Format::ISO8601->new;
1117             $dt = $iso8601->parse_datetime($datetime_str);
1118             say $dt;
1119              
1120             $dt = $iso8601->parse_time($time_str);
1121             say $dt;
1122              
1123             say DateTime::Format::ISO8601->format_datetime($dt);
1124              
1125             =head1 DESCRIPTION
1126              
1127             Parses almost all ISO8601 date and time formats. ISO8601 time-intervals will
1128             be supported in a later release.
1129              
1130             =head1 METHODS
1131              
1132             This class provides the following methods:
1133              
1134             =head2 Constructors
1135              
1136             =head3 DateTime::Format::ISO8601->new( ... )
1137              
1138             Accepts an optional hash.
1139              
1140             my $iso8601 = DateTime::Format::ISO8601->new(
1141             base_datetime => $dt,
1142             cut_off_year => 42,
1143             legacy_year => 1,
1144             );
1145              
1146             =over 4
1147              
1148             =item * base_datetime
1149              
1150             A C<DateTime> object that will be used to fill in missing information from
1151             incomplete date/time formats.
1152              
1153             This key is optional.
1154              
1155             =item * cut_off_year
1156              
1157             A integer representing the cut-off point between interpreting 2-digits years
1158             as 19xx or 20xx.
1159              
1160             2-digit years < cut_off_year will be interpreted as 20xx
1161             2-digit years >= cut_off_year will be untreated as 19xx
1162              
1163             This key defaults to the value of C<DefaultCutOffYear>.
1164              
1165             =item * legacy_year
1166              
1167             A boolean value controlling if a 2-digit year is interpreted as being in the
1168             current century (unless a C<base_datetime> is set) or if C<cut_off_year>
1169             should be used to place the year in either 20xx or 19xx.
1170              
1171             If this is true, then the C<cut_off_year> is used. If this is false, then the
1172             year is always interpreted as being in the current century.
1173              
1174             This key defaults to the value of C<DefaultLegacyYear>.
1175              
1176             =back
1177              
1178             =head3 $iso8601->clone
1179              
1180             Returns a replica of the given object.
1181              
1182             =head2 Object Methods
1183              
1184             =head3 $iso8601->base_datetime
1185              
1186             Returns a C<DateTime> object if a C<base_datetime> has been set.
1187              
1188             =head3 $iso8601->set_base_datetime( object => $object )
1189              
1190             Accepts a C<DateTime> object that will be used to fill in missing information
1191             from incomplete date/time formats.
1192              
1193             =head3 $iso8601->cut_off_year
1194              
1195             Returns a integer representing the cut-off point between interpreting
1196             2-digits years as 19xx or 20xx.
1197              
1198             =head3 $iso8601->set_cut_off_year($int)
1199              
1200             Accepts a integer representing the cut-off point between interpreting
1201             2-digits years as 19xx or 20xx.
1202              
1203             2-digit years < legacy_year will be interpreted as 20xx
1204             2-digit years >= legacy_year will be interpreted as 19xx
1205              
1206             =head3 $iso8601->legacy_year
1207              
1208             Returns a boolean value indicating the 2-digit year handling behavior.
1209              
1210             =head3 $iso8601->set_legacy_year($bool)
1211              
1212             Accepts a boolean value controlling if a 2-digit year is interpreted as being
1213             in the current century (unless a C<base_datetime> is set) or if
1214             C<cut_off_year> should be used to place the year in either 20xx or 19xx.
1215              
1216             =head2 Class Methods
1217              
1218             =head3 DateTime::Format::ISO8601->DefaultCutOffYear($int)
1219              
1220             Accepts a integer representing the cut-off point for 2-digit years when
1221             calling C<parse_*> as class methods and the default value for C<cut_off_year>
1222             when creating objects. If called with no parameters this method will return
1223             the default value for C<cut_off_year>.
1224              
1225             =head3 DateTime::Format::ISO8601->DefaultLegacyYear($bool)
1226              
1227             Accepts a boolean value controlling the legacy year behavior when calling
1228             C<parse_*> as class methods and the default value for C<legacy_year> when
1229             creating objects. If called with no parameters this method will return the
1230             default value for C<legacy_year>.
1231              
1232             =head2 Parser(s)
1233              
1234             These methods may be called as either class or object methods.
1235              
1236             =head3 parse_datetime
1237              
1238             =head3 parse_time
1239              
1240             Please see the L</FORMATS> section.
1241              
1242             =head2 Formatter
1243              
1244             This may be called as either class or object method.
1245              
1246             =head3 format_datetime($dt)
1247              
1248             Formats the datetime in an ISO8601-compatible format. This differs from
1249             L<DateTime/iso8601> by including nanoseconds/milliseconds and the correct
1250             timezone offset.
1251              
1252             =head1 FORMATS
1253              
1254             There are 6 strings that can match against date only or time only formats.
1255             The C<parse_datetime> method will attempt to match these ambiguous strings
1256             against date only formats. If you want to match against the time only formats
1257             use the C<parse_time> method.
1258              
1259             =head2 Conventions
1260              
1261             =over 4
1262              
1263             =item * Expanded ISO8601
1264              
1265             These formats are supported with exactly 6 digits for the year.
1266             Support for a variable number of digits will be in a later release.
1267              
1268             =item * Precision
1269              
1270             If a format doesn't include a year all larger time unit up to and including
1271             the year are filled in using the current date/time or [if set] the
1272             C<base_datetime> object.
1273              
1274             =item * Fractional time
1275              
1276             There is no limit on the expressed precision.
1277              
1278             =back
1279              
1280             =head2 Supported via parse_datetime
1281              
1282             The supported formats are listed by the section of ISO 8601:2000(E) in
1283             which they appear.
1284              
1285             =head3 5.2 Dates
1286              
1287             =over 4
1288              
1289             =item * 5.2.1.1
1290              
1291             =over 8
1292              
1293             =item YYYYMMDD
1294              
1295             =item YYYY-MM-DD
1296              
1297             =back
1298              
1299             =item * 5.2.1.2
1300              
1301             =over 8
1302              
1303             =item YYYY-MM
1304              
1305             =item YYYY
1306              
1307             =item YY
1308              
1309             =back
1310              
1311             =item * 5.2.1.3
1312              
1313             =over 8
1314              
1315             =item YYMMDD
1316              
1317             =item YY-MM-DD
1318              
1319             =item -YYMM
1320              
1321             =item -YY-MM
1322              
1323             =item -YY
1324              
1325             =item --MMDD
1326              
1327             =item --MM-DD
1328              
1329             =item --MM
1330              
1331             =item ---DD
1332              
1333             =back
1334              
1335             =item * 5.2.1.4
1336              
1337             =over 8
1338              
1339             =item +[YY]YYYYMMDD
1340              
1341             =item +[YY]YYYY-MM-DD
1342              
1343             =item +[YY]YYYY-MM
1344              
1345             =item +[YY]YYYY
1346              
1347             =item +[YY]YY
1348              
1349             =back
1350              
1351             =item * 5.2.2.1
1352              
1353             =over 8
1354              
1355             =item YYYYDDD
1356              
1357             =item YYYY-DDD
1358              
1359             =back
1360              
1361             =item * 5.2.2.2
1362              
1363             =over 8
1364              
1365             =item YYDDD
1366              
1367             =item YY-DDD
1368              
1369             =item -DDD
1370              
1371             =back
1372              
1373             =item * 5.2.2.3
1374              
1375             =over 8
1376              
1377             =item +[YY]YYYYDDD
1378              
1379             =item +[YY]YYYY-DDD
1380              
1381             =back
1382              
1383             =item * 5.2.3.1
1384              
1385             =over 8
1386              
1387             =item YYYYWwwD
1388              
1389             =item YYYY-Www-D
1390              
1391             =back
1392              
1393             =item * 5.2.3.2
1394              
1395             =over 8
1396              
1397             =item YYYYWww
1398              
1399             =item YYYY-Www
1400              
1401             =item YYWwwD
1402              
1403             =item YY-Www-D
1404              
1405             =item YYWww
1406              
1407             =item YY-Www
1408              
1409             =item -YWwwD
1410              
1411             =item -Y-Www-D
1412              
1413             =item -YWww
1414              
1415             =item -Y-Www
1416              
1417             =item -WwwD
1418              
1419             =item -Www-D
1420              
1421             =item -Www
1422              
1423             =item -W-D
1424              
1425             =back
1426              
1427             =item * 5.2.3.4
1428              
1429             =over 8
1430              
1431             =item +[YY]YYYYWwwD
1432              
1433             =item +[YY]YYYY-Www-D
1434              
1435             =item +[YY]YYYYWww
1436              
1437             =item +[YY]YYYY-Www
1438              
1439             =back
1440              
1441             =back
1442              
1443             =head3 5.3 Time of Day
1444              
1445             =over 4
1446              
1447             =item * 5.3.1.1 - 5.3.1.3
1448              
1449             Values can optionally be prefixed with 'T'.
1450              
1451             =item * 5.3.1.1
1452              
1453             =over 8
1454              
1455             =item hh:mm:ss
1456              
1457             =back
1458              
1459             =item * 5.3.1.2
1460              
1461             =over 8
1462              
1463             =item hh:mm
1464              
1465             =back
1466              
1467             =item * 5.3.1.3 - 5.3.1.4
1468              
1469             fractional (decimal) separator maybe either ',' or '.'
1470              
1471             =item * 5.3.1.3
1472              
1473             =over 8
1474              
1475             =item hhmmss,ss
1476              
1477             =item hh:mm:ss,ss
1478              
1479             =item hhmm,mm
1480              
1481             =item hh:mm,mm
1482              
1483             =item hh,hh
1484              
1485             =back
1486              
1487             =item * 5.3.1.4
1488              
1489             =over 8
1490              
1491             =item -mm:ss
1492              
1493             =item -mmss,s
1494              
1495             =item -mm:ss,s
1496              
1497             =item -mm,m
1498              
1499             =item --ss,s
1500              
1501             =back
1502              
1503             =item * 5.3.3 - 5.3.4.2
1504              
1505             Values can optionally be prefixed with 'T'.
1506              
1507             =item * 5.3.3
1508              
1509             =over 8
1510              
1511             =item hhmmssZ
1512              
1513             =item hh:mm:ssZ
1514              
1515             =item hhmmZ
1516              
1517             =item hh:mmZ
1518              
1519             =item hhZ
1520              
1521             =item hhmmss.ssZ
1522              
1523             =item hh:mm:ss.ssZ
1524              
1525             =back
1526              
1527             =item * 5.3.4.2
1528              
1529             =over 8
1530              
1531             =item hhmmss[+-]hhmm
1532              
1533             =item hh:mm:ss[+-]hh:mm
1534              
1535             =item hhmmss[+-]hh
1536              
1537             =item hh:mm:ss[+-]hh
1538              
1539             =item hhmmss.ss[+-]hhmm
1540              
1541             =item hh:mm:ss.ss[+-]hh:mm
1542              
1543             =back
1544              
1545             =back
1546              
1547             =head3 5.4 Combinations of date and time of day
1548              
1549             =over 4
1550              
1551             =item * 5.4.1
1552              
1553             =over 8
1554              
1555             =item YYYYMMDDThhmmss
1556              
1557             =item YYYY-MM-DDThh:mm:ss
1558              
1559             =item YYYYMMDDThhmmssZ
1560              
1561             =item YYYY-MM-DDThh:mm:ssZ
1562              
1563             =item YYYYMMDDThhmmss[+-]hhmm
1564              
1565             =item YYYY-MM-DDThh:mm:ss[+-]hh:mm
1566              
1567             =item YYYYMMDDThhmmss[+-]hh
1568              
1569             =item YYYY-MM-DDThh:mm:ss[+-]hh
1570              
1571             =back
1572              
1573             =item * 5.4.2
1574              
1575             =over 8
1576              
1577             =item YYYYMMDDThhmmss.ss
1578              
1579             =item YYYY-MM-DDThh:mm:ss.ss
1580              
1581             =item YYYYMMDDThhmmss.ss[+-]hh
1582              
1583             =item YYYY-MM-DDThh:mm:ss.ss[+-]hh
1584              
1585             =item YYYYMMDDThhmmss.ss[+-]hhmm
1586              
1587             =item YYYY-MM-DDThh:mm:ss.ss[+-]hh:mm
1588              
1589             =back
1590              
1591             =item * 5.4.3
1592              
1593             Support for this section is not complete.
1594              
1595             =over 8
1596              
1597             =item YYYYMMDDThhmm
1598              
1599             =item YYYY-MM-DDThh:mm
1600              
1601             =item YYYYMMDDThhmmZ
1602              
1603             =item YYYY-MM-DDThh:mmZ
1604              
1605             =item YYYYDDDThhmm
1606              
1607             =item YYYY-DDDThh:mm
1608              
1609             =item YYYYDDDThhmmZ
1610              
1611             =item YYYY-DDDThh:mmZ
1612              
1613             =item YYYYWwwDThhmm[+-]hhmm
1614              
1615             =item YYYY-Www-DThh:mm[+-]hh
1616              
1617             =back
1618              
1619             =back
1620              
1621             =head3 5.5 Time-Intervals
1622              
1623             These are not currently supported
1624              
1625             =head2 Supported via parse_time
1626              
1627             =head3 5.3.1.1 - 5.3.1.3
1628              
1629             Values can optionally be prefixed with 'T'.
1630              
1631             =over 4
1632              
1633             =item * 5.3.1.1
1634              
1635             =over 8
1636              
1637             =item hhmmss
1638              
1639             =back
1640              
1641             =item * 5.3.1.2
1642              
1643             =over 8
1644              
1645             =item hhmm
1646              
1647             =item hh
1648              
1649             =back
1650              
1651             =item * 5.3.1.4
1652              
1653             =over 8
1654              
1655             =item -mmss
1656              
1657             =item -mm
1658              
1659             =item --ss
1660              
1661             =back
1662              
1663             =back
1664              
1665             =head1 STANDARDS DOCUMENT
1666              
1667             =head2 Title
1668              
1669             ISO8601:2000(E)
1670             Data elements and interchange formats - information exchange -
1671             Representation of dates and times
1672             Second edition 2000-12-15
1673              
1674             =head2 Reference Number
1675              
1676             ISO/TC 154 N 362
1677              
1678             =head1 CREDITS
1679              
1680             Iain 'Spoon' Truskett (SPOON) who wrote L<DateTime::Format::Builder>.
1681             That has grown into I<The Vacuum Energy Powered C<Swiss Army> Katana>
1682             of date and time parsing. This module was inspired by and conceived
1683             in honor of Iain's work.
1684              
1685             Tom Phoenix (PHOENIX) and PDX.pm for helping me solve the ISO week conversion
1686             bug. Not by fixing the code but motivation me to fix it so I could
1687             participate in a game of C<Zendo>.
1688              
1689             Jonathan Leffler (JOHNL) for reporting a test bug.
1690              
1691             Kelly McCauley for a patch to add 8 missing formats.
1692              
1693             Alasdair Allan (AALLAN) for complaining about excessive test execution time.
1694              
1695             Everyone at the DateTime C<Asylum>.
1696              
1697             =head1 SEE ALSO
1698              
1699             =over 4
1700              
1701             =item *
1702              
1703             L<DateTime>
1704              
1705             =item *
1706              
1707             L<DateTime::Format::Builder>
1708              
1709             =back
1710              
1711             =head1 SUPPORT
1712              
1713             Bugs may be submitted at L<https://github.com/houseabsolute/DateTime-Format-ISO8601/issues>.
1714              
1715             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
1716              
1717             =head1 SOURCE
1718              
1719             The source code repository for DateTime-Format-ISO8601 can be found at L<https://github.com/houseabsolute/DateTime-Format-ISO8601>.
1720              
1721             =head1 AUTHORS
1722              
1723             =over 4
1724              
1725             =item *
1726              
1727             Joshua Hoblitt <josh@hoblitt.com>
1728              
1729             =item *
1730              
1731             Dave Rolsky <autarch@urth.org>
1732              
1733             =back
1734              
1735             =head1 CONTRIBUTORS
1736              
1737             =for stopwords Doug Bell joe Thomas Klausner
1738              
1739             =over 4
1740              
1741             =item *
1742              
1743             Doug Bell <doug@preaction.me>
1744              
1745             =item *
1746              
1747             joe <draxil@gmail.com>
1748              
1749             =item *
1750              
1751             Thomas Klausner <domm@plix.at>
1752              
1753             =back
1754              
1755             =head1 COPYRIGHT AND LICENSE
1756              
1757             This software is copyright (c) 2020 by Joshua Hoblitt.
1758              
1759             This is free software; you can redistribute it and/or modify it under
1760             the same terms as the Perl 5 programming language system itself.
1761              
1762             The full text of the license can be found in the
1763             F<LICENSE> file included with this distribution.
1764              
1765             =cut