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   2891592 use strict;
  8         76  
  8         299  
5 8     8   48 use warnings;
  8         15  
  8         235  
6 8     8   3028 use namespace::autoclean;
  8         118061  
  8         50  
7              
8             our $VERSION = '0.16';
9              
10 8     8   728 use Carp qw( croak );
  8         21  
  8         515  
11 8     8   6252 use DateTime 1.45;
  8         3001231  
  8         459  
12 8     8   5849 use DateTime::Format::Builder 0.77;
  8         578554  
  8         69  
13 8     8   5201 use DateTime::Format::ISO8601::Types;
  8         32  
  8         67  
14 8     8   98204 use Params::ValidationCompiler 0.26 qw( validation_for );
  8         233  
  8         44856  
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 482     482 1 17291 shift;
27 482 100       1815 ($default_legacy_year) = $validator->(@_)
28             if @_;
29              
30 476         1451 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 10890     10890 1 236565 shift;
47 10890 100       30811 ($default_cut_off_year) = $validator->(@_)
48             if @_;
49              
50 10884         24563 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 575     575 1 2739906 my ($class) = shift;
79 575         14992 my %args = $validator->(@_);
80              
81             $args{legacy_year} = $class->DefaultLegacyYear
82 563 100       17713 unless exists $args{legacy_year};
83             $args{cut_off_year} = $class->DefaultCutOffYear
84 563 100       1923 unless exists $args{cut_off_year};
85              
86 563   33     2557 $class = ref($class) || $class;
87              
88 563         1280 my $self = bless( \%args, $class );
89              
90 563 100       1620 if ( $args{base_datetime} ) {
91 122         792 $self->set_base_datetime( object => $args{base_datetime} );
92             }
93              
94 557         1638 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 326522 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 225 my $self = shift;
114              
115 135         2652 my %args = $validator->(@_);
116              
117             # ISO8601 only allows years 0 to 9999
118             # this implementation ignores the needs of expanded formats
119 135         4659 my $dt = DateTime->from_object( object => $args{object} );
120 135         73547 my $lower_bound = DateTime->new( year => 0 );
121 135         38399 my $upper_bound = DateTime->new( year => 10000 );
122              
123 135 100       37852 if ( $dt < $lower_bound ) {
124 6         452 croak 'base_datetime must be greater then or equal to ',
125             $lower_bound->iso8601;
126             }
127 129 100       10182 if ( $dt >= $upper_bound ) {
128 6         390 croak 'base_datetime must be less then ', $upper_bound->iso8601;
129             }
130              
131 123         7922 $self->{base_datetime} = $dt;
132              
133 123         792 return $self;
134             }
135             }
136              
137 10     10 1 1292 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 14 my $self = shift;
148              
149 9         165 ( $self->{legacy_year} ) = $validator->(@_);
150              
151 3         42 return $self;
152             }
153             }
154              
155 501     501 1 128604 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 158 my $self = shift;
166              
167 106         1950 ( $self->{cut_off_year} ) = $validator->(@_);
168              
169 100         1082 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 30701 my $self = shift;
182 5         124 my ($dt) = $validator->(@_);
183              
184 5 100       83 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         53 my $tz;
190 5 100       16 if ( $dt->time_zone->is_utc ) {
191 2         18 $tz = 'Z';
192             }
193             else {
194 3         25 $tz = q{};
195 3         8 $cldr .= 'ZZZZZ';
196             }
197              
198 5         24 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             #YYYYMMDDThhmm[+-]hhmm 19850412T1015+0400
703             #YYYY-MM-DDThh:mm[+-]hh:mm 1985-04-12T10:15+04:00
704             length => [qw( 18 22 )],
705             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
706             T (\d\d) :?? (\d\d) ([+-] \d\d :?? \d\d) $/x,
707             params => [qw( year month day hour minute time_zone )],
708             postprocess => \&_normalize_offset,
709             },
710             {
711             #YYYYMMDDThhmmss[+-]hhmm 19850412T101530+0400
712             #YYYY-MM-DDThh:mm:ss[+-]hh:mm 1985-04-12T10:15:30+04:00
713             length => [qw( 20 24 25 )],
714             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
715             T (\d\d) :?? (\d\d) :?? (\d\d) ([+-] \d\d :?? \d\d) $/x,
716             params => [qw( year month day hour minute second time_zone )],
717             postprocess => \&_normalize_offset,
718             },
719             {
720             #YYYYMMDDThhmmss.ss[+-]hhmm 19850412T101530.5+0100 20041020T101530.5-0500
721             regex => qr/^ (\d{4}) (\d\d) (\d\d)
722             T?? (\d\d) (\d\d) (\d\d) [\.,] (\d+)
723             ([+-] \d\d \d\d) $/x,
724             params => [
725             qw( year month day hour minute second nanosecond time_zone )
726             ],
727             postprocess => [
728             \&_fractional_second,
729             \&_normalize_offset,
730             ],
731             },
732             {
733             #YYYY-MM-DDThh:mm:ss.ss[+-]hh 1985-04-12T10:15:30.5+01 1985-04-12T10:15:30.5-05
734             regex => qr/^ (\d{4}) - (\d\d) - (\d\d)
735             T?? (\d\d) : (\d\d) : (\d\d) [\.,] (\d+)
736             ([+-] \d\d ) $/x,
737             params => [
738             qw( year month day hour minute second nanosecond time_zone )
739             ],
740             postprocess => [
741             \&_fractional_second,
742             \&_normalize_offset,
743             ],
744             },
745             {
746             #YYYYMMDDThhmmss.ss[+-]hh 19850412T101530.5+01 20041020T101530.5-05
747             regex => qr/^ (\d{4}) (\d\d) (\d\d)
748             T?? (\d\d) (\d\d) (\d\d) [\.,] (\d+)
749             ([+-] \d\d ) $/x,
750             params => [
751             qw( year month day hour minute second nanosecond time_zone )
752             ],
753             postprocess => [
754             \&_fractional_second,
755             \&_normalize_offset,
756             ],
757             },
758             {
759             #YYYY-MM-DDThh:mm:ss.ss[+-]hh:mm 1985-04-12T10:15:30.5+01:00 1985-04-12T10:15:30.5-05:00
760             regex => qr/^ (\d{4}) - (\d\d) - (\d\d)
761             T?? (\d\d) : (\d\d) : (\d\d) [\.,] (\d+)
762             ([+-] \d\d : \d\d) $/x,
763             params => [
764             qw( year month day hour minute second nanosecond time_zone )
765             ],
766             postprocess => [
767             \&_fractional_second,
768             \&_normalize_offset,
769             ],
770             },
771              
772             {
773             #YYYYMMDDThhmmss[+-]hh 19850412T101530+04
774             #YYYY-MM-DDThh:mm:ss[+-]hh 1985-04-12T10:15:30+04
775             length => [qw( 18 22 )],
776             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
777             T (\d\d) :?? (\d\d) :?? (\d\d) ([+-] \d\d) $/x,
778             params => [qw( year month day hour minute second time_zone )],
779             postprocess => \&_normalize_offset,
780             },
781             {
782             #YYYYMMDDThhmm 19850412T1015
783             #YYYY-MM-DDThh:mm 1985-04-12T10:15
784             length => [qw( 13 16 )],
785             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
786             T (\d\d) :?? (\d\d) $/x,
787             params => [qw( year month day hour minute )],
788             extra => { time_zone => 'floating' },
789             },
790             {
791             #YYYYMMDDThhmmZ 19850412T1015
792             #YYYY-MM-DDThh:mmZ 1985-04-12T10:15
793             length => [qw( 14 17 )],
794             regex => qr/^ (\d{4}) -?? (\d\d) -?? (\d\d)
795             T (\d\d) :?? (\d\d) Z $/x,
796             params => [qw( year month day hour minute )],
797             extra => { time_zone => 'UTC' },
798             },
799             {
800             #YYYYDDDThhmm 1985102T1015
801             #YYYY-DDDThh:mm 1985-102T10:15
802             length => [qw( 12 14 )],
803             regex => qr/^ (\d{4}) -?? (\d{3}) T
804             (\d\d) :?? (\d\d) $/x,
805             params => [qw( year day_of_year hour minute )],
806             extra => { time_zone => 'floating' },
807             constructor => [ 'DateTime', 'from_day_of_year' ],
808             },
809             {
810             #YYYYDDDThhmmZ 1985102T1015Z
811             #YYYY-DDDThh:mmZ 1985-102T10:15Z
812             length => [qw( 13 15 )],
813             regex => qr/^ (\d{4}) -?? (\d{3}) T
814             (\d\d) :?? (\d\d) Z $/x,
815             params => [qw( year day_of_year hour minute )],
816             extra => { time_zone => 'UTC' },
817             constructor => [ 'DateTime', 'from_day_of_year' ],
818              
819             },
820             {
821             #YYYYWwwDThhmm[+-]hhmm 1985W155T1015+0400
822             #YYYY-Www-DThh:mm[+-]hh 1985-W15-5T10:15+04
823             length => [qw( 18 19 )],
824             regex => qr/^ (\d{4}) -?? W (\d\d) -?? (\d)
825             T (\d\d) :?? (\d\d) ([+-] \d{2,4}) $/x,
826             params => [qw( year week day_of_week hour minute time_zone)],
827             postprocess => [ \&_normalize_week, \&_normalize_offset ],
828             constructor => [ 'DateTime', 'from_day_of_year' ],
829             },
830             ],
831             parse_time => [
832             {
833             #hhmmss 232050
834             length => [qw( 6 7 )],
835             regex => qr/^ T?? (\d\d) (\d\d) (\d\d) $/x,
836             params => [qw( hour minute second )],
837             postprocess => [
838             \&_add_year,
839             \&_add_month,
840             \&_add_day,
841             ],
842             },
843             {
844             #hhmm 2320
845             length => [qw( 4 5 )],
846             regex => qr/^ T?? (\d\d) (\d\d) $/x,
847             params => [qw( hour minute )],
848             postprocess => [
849             \&_add_year,
850             \&_add_month,
851             \&_add_day,
852             ],
853             },
854             {
855             #hh 23
856             length => [qw( 2 3 )],
857             regex => qr/^ T?? (\d\d) $/x,
858             params => [qw( hour )],
859             postprocess => [
860             \&_add_year,
861             \&_add_month,
862             \&_add_day,
863             ],
864             },
865             {
866             #-mmss -2050
867             length => 5,
868             regex => qr/^ - (\d\d) (\d\d) $/x,
869             params => [qw( minute second )],
870             postprocess => [
871             \&_add_year,
872             \&_add_month,
873             \&_add_day,
874             \&_add_hour,
875             ],
876             },
877             {
878             #-mm -20
879             length => 3,
880             regex => qr/^ - (\d\d) $/x,
881             params => [qw( minute )],
882             postprocess => [
883             \&_add_year,
884             \&_add_month,
885             \&_add_day,
886             \&_add_hour,
887             ],
888             },
889             {
890             #--ss --50
891             length => 4,
892             regex => qr/^ -- (\d\d) $/x,
893             params => [qw( second )],
894             postprocess => [
895             \&_add_year,
896             \&_add_month,
897             \&_add_day,
898             \&_add_hour,
899             \&_add_minute,
900             ],
901             },
902             ],
903             }
904             );
905              
906             sub _fix_1_digit_year {
907 13     13   26453 my %p = @_;
908              
909 13         46 my $year = _base_dt( $p{self} )->year;
910              
911 13         2794 $year =~ s/.$//;
912 13         45 $p{parsed}{year} = $year . $p{parsed}{year};
913              
914 13         41 return 1;
915             }
916              
917             sub _fix_2_digit_year {
918 21033     21033   16513977 my %p = @_;
919              
920             # this is a mess because of the need to support parse_* being called
921             # as a class method
922 21033 100 66     89086 if ( ref $p{self} && exists $p{self}{legacy_year} ) {
923 11022 100       24948 if ( $p{self}{legacy_year} ) {
924             my $cutoff
925             = exists $p{self}{cut_off_year}
926             ? $p{self}{cut_off_year}
927 10022 50       23278 : $p{self}->DefaultCutOffYear;
928 10022 100       33835 $p{parsed}{year} += $p{parsed}{year} > $cutoff ? 1900 : 2000;
929             }
930             else {
931 1000   33     4008 my $century = ( $p{self}{base_datetime} || DateTime->now )
932             ->strftime('%C');
933 1000         35154 $p{parsed}{year} += $century * 100;
934             }
935             }
936             else {
937             my $cutoff
938             = ref $p{self} && exists $p{self}{cut_off_year}
939             ? $p{self}{cut_off_year}
940 10011 50 33     35492 : $p{self}->DefaultCutOffYear;
941 10011 100       36721 $p{parsed}{year} += $p{parsed}{year} > $cutoff ? 1900 : 2000;
942             }
943              
944 21033         65146 return 1;
945             }
946              
947             sub _add_minute {
948 11     11   104 my %p = @_;
949              
950 11         30 $p{parsed}{minute} = _base_dt( $p{self} )->minute;
951              
952 11         1176 return 1;
953             }
954              
955             sub _add_hour {
956 29     29   256 my %p = @_;
957              
958 29         64 $p{parsed}{hour} = _base_dt( $p{self} )->hour;
959              
960 29         4331 return 1;
961             }
962              
963             sub _add_day {
964 116     116   999 my %p = @_;
965              
966 116         250 $p{parsed}{day} = _base_dt( $p{self} )->day;
967              
968 116         20460 return 1;
969             }
970              
971             sub _add_week {
972 8     8   83 my %p = @_;
973              
974 8         34 $p{parsed}{week} = _base_dt( $p{self} )->week;
975              
976 8         875 return 1;
977             }
978              
979             sub _add_month {
980 119     119   1091 my %p = @_;
981              
982 119         261 $p{parsed}{month} = _base_dt( $p{self} )->month;
983              
984 119         20984 return 1;
985             }
986              
987             sub _add_year {
988 142     142   349623 my %p = @_;
989              
990 142         427 $p{parsed}{year} = _base_dt( $p{self} )->year;
991              
992 142         26313 return 1;
993             }
994              
995             sub _base_dt {
996 438 100 100 438   2007 return $_[0]{base_datetime} if ref $_[0] && $_[0]{base_datetime};
997 269         840 return DateTime->now;
998             }
999              
1000             sub _fractional_second {
1001 62     62   114453 my %p = @_;
1002              
1003             ## no critic (ValuesAndExpressions::ProhibitMismatchedOperators)
1004 62         326 $p{parsed}{nanosecond} = ".$p{ parsed }{ nanosecond }" * 10**9;
1005              
1006 62         171 return 1;
1007             }
1008              
1009             sub _fractional_minute {
1010 9     9   81 my %p = @_;
1011              
1012             ## no critic (ValuesAndExpressions::ProhibitMismatchedOperators)
1013 9         47 $p{parsed}{second} = ".$p{ parsed }{ second }" * 60;
1014              
1015 9         26 return 1;
1016             }
1017              
1018             sub _fractional_hour {
1019 3     3   32 my %p = @_;
1020              
1021             ## no critic (ValuesAndExpressions::ProhibitMismatchedOperators)
1022 3         20 $p{parsed}{minute} = ".$p{ parsed }{ minute }" * 60;
1023              
1024 3         8 return 1;
1025             }
1026              
1027             sub _normalize_offset {
1028 78     78   69041 my %p = @_;
1029              
1030 78         259 $p{parsed}{time_zone} =~ s/://;
1031              
1032 78 100       244 if ( length $p{parsed}{time_zone} == 3 ) {
1033 27         59 $p{parsed}{time_zone} .= '00';
1034             }
1035              
1036 78         200 return 1;
1037             }
1038              
1039             sub _normalize_week {
1040 3981     3981   16146256 my %p = @_;
1041              
1042             # See
1043             # https://en.wikipedia.org/wiki/ISO_week_date#Calculating_an_ordinal_or_month_date_from_a_week_date
1044             # for the algorithm we're using here.
1045 3981         17664 my $od = $p{parsed}{week} * 7;
1046 3981 100       13121 $od += ( exists $p{parsed}{day_of_week} ? $p{parsed}{day_of_week} : 1 );
1047             $od -= DateTime->new(
1048             year => $p{parsed}{year},
1049 3981         16976 month => 1,
1050             day => 4,
1051             )->day_of_week + 3;
1052              
1053 3981         1282013 my ( $year, $day_of_year );
1054 3981 100       12240 if ( $od < 1 ) {
1055 20         63 $year = $p{parsed}{year} - 1;
1056 20         57 $day_of_year = DateTime->new( year => $year )->year_length + $od;
1057             }
1058             else {
1059             my $year_length
1060 3961         13681 = DateTime->new( year => $p{parsed}{year} )->year_length;
1061 3961 100       1184015 if ( $od > $year_length ) {
1062 6         23 $year = $p{parsed}{year} + 1;
1063 6         18 $day_of_year = $od - $year_length;
1064             }
1065             else {
1066 3955         10458 $year = $p{parsed}{year};
1067 3955         7673 $day_of_year = $od;
1068             }
1069             }
1070              
1071             # We need to leave the references in $p{parsed} as is. We cannot create a
1072             # new reference.
1073 3981         14806 $p{parsed}{year} = $year;
1074 3981         8421 $p{parsed}{day_of_year} = $day_of_year;
1075              
1076 3981         9764 delete $p{parsed}{week};
1077 3981         8382 delete $p{parsed}{day_of_week};
1078              
1079 3981         12884 return 1;
1080             }
1081              
1082             sub _normalize_century {
1083 6     6   71988 my %p = @_;
1084              
1085 6         17 $p{parsed}{year} .= '01';
1086              
1087 6         20 return 1;
1088             }
1089              
1090             1;
1091              
1092             # ABSTRACT: Parses ISO8601 formats
1093              
1094             __END__
1095              
1096             =pod
1097              
1098             =encoding UTF-8
1099              
1100             =head1 NAME
1101              
1102             DateTime::Format::ISO8601 - Parses ISO8601 formats
1103              
1104             =head1 VERSION
1105              
1106             version 0.16
1107              
1108             =head1 SYNOPSIS
1109              
1110             use DateTime::Format::ISO8601;
1111              
1112             my $datetime_str = '2020-07-25T11:32:31';
1113             my $dt = DateTime::Format::ISO8601->parse_datetime($datetime_str);
1114             say $dt;
1115              
1116             # This format is ambiguous and could be either a date or time, so use the
1117             # parse_time method.
1118             my $time_str = '113231';
1119             $dt = DateTime::Format::ISO8601->parse_time($time_str);
1120             say $dt;
1121              
1122             # or
1123              
1124             my $iso8601 = DateTime::Format::ISO8601->new;
1125             $dt = $iso8601->parse_datetime($datetime_str);
1126             say $dt;
1127              
1128             $dt = $iso8601->parse_time($time_str);
1129             say $dt;
1130              
1131             say DateTime::Format::ISO8601->format_datetime($dt);
1132              
1133             =head1 DESCRIPTION
1134              
1135             Parses almost all ISO8601 date and time formats. ISO8601 time-intervals will be
1136             supported in a later release.
1137              
1138             =head1 METHODS
1139              
1140             This class provides the following methods:
1141              
1142             =head2 Constructors
1143              
1144             =head3 DateTime::Format::ISO8601->new( ... )
1145              
1146             Accepts an optional hash.
1147              
1148             my $iso8601 = DateTime::Format::ISO8601->new(
1149             base_datetime => $dt,
1150             cut_off_year => 42,
1151             legacy_year => 1,
1152             );
1153              
1154             =over 4
1155              
1156             =item * base_datetime
1157              
1158             A C<DateTime> object that will be used to fill in missing information from
1159             incomplete date/time formats.
1160              
1161             This key is optional.
1162              
1163             =item * cut_off_year
1164              
1165             A integer representing the cut-off point between interpreting 2-digits years as
1166             19xx or 20xx.
1167              
1168             2-digit years < cut_off_year will be interpreted as 20xx
1169             2-digit years >= cut_off_year will be untreated as 19xx
1170              
1171             This key defaults to the value of C<DefaultCutOffYear>.
1172              
1173             =item * legacy_year
1174              
1175             A boolean value controlling if a 2-digit year is interpreted as being in the
1176             current century (unless a C<base_datetime> is set) or if C<cut_off_year> should
1177             be used to place the year in either 20xx or 19xx.
1178              
1179             If this is true, then the C<cut_off_year> is used. If this is false, then the
1180             year is always interpreted as being in the current century.
1181              
1182             This key defaults to the value of C<DefaultLegacyYear>.
1183              
1184             =back
1185              
1186             =head3 $iso8601->clone
1187              
1188             Returns a replica of the given object.
1189              
1190             =head2 Object Methods
1191              
1192             =head3 $iso8601->base_datetime
1193              
1194             Returns a C<DateTime> object if a C<base_datetime> has been set.
1195              
1196             =head3 $iso8601->set_base_datetime( object => $object )
1197              
1198             Accepts a C<DateTime> object that will be used to fill in missing information
1199             from incomplete date/time formats.
1200              
1201             =head3 $iso8601->cut_off_year
1202              
1203             Returns a integer representing the cut-off point between interpreting 2-digits
1204             years as 19xx or 20xx.
1205              
1206             =head3 $iso8601->set_cut_off_year($int)
1207              
1208             Accepts a integer representing the cut-off point between interpreting 2-digits
1209             years as 19xx or 20xx.
1210              
1211             2-digit years < legacy_year will be interpreted as 20xx
1212             2-digit years >= legacy_year will be interpreted as 19xx
1213              
1214             =head3 $iso8601->legacy_year
1215              
1216             Returns a boolean value indicating the 2-digit year handling behavior.
1217              
1218             =head3 $iso8601->set_legacy_year($bool)
1219              
1220             Accepts a boolean value controlling if a 2-digit year is interpreted as being
1221             in the current century (unless a C<base_datetime> is set) or if C<cut_off_year>
1222             should be used to place the year in either 20xx or 19xx.
1223              
1224             =head2 Class Methods
1225              
1226             =head3 DateTime::Format::ISO8601->DefaultCutOffYear($int)
1227              
1228             Accepts a integer representing the cut-off point for 2-digit years when calling
1229             C<parse_*> as class methods and the default value for C<cut_off_year> when
1230             creating objects. If called with no parameters this method will return the
1231             default value for C<cut_off_year>.
1232              
1233             =head3 DateTime::Format::ISO8601->DefaultLegacyYear($bool)
1234              
1235             Accepts a boolean value controlling the legacy year behavior when calling
1236             C<parse_*> as class methods and the default value for C<legacy_year> when
1237             creating objects. If called with no parameters this method will return the
1238             default value for C<legacy_year>.
1239              
1240             =head2 Parser(s)
1241              
1242             These methods may be called as either class or object methods.
1243              
1244             =head3 parse_datetime
1245              
1246             =head3 parse_time
1247              
1248             Please see the L</FORMATS> section.
1249              
1250             =head2 Formatter
1251              
1252             This may be called as either class or object method.
1253              
1254             =head3 format_datetime($dt)
1255              
1256             Formats the datetime in an ISO8601-compatible format. This differs from
1257             L<DateTime/iso8601> by including nanoseconds/milliseconds and the correct
1258             timezone offset.
1259              
1260             =head1 FORMATS
1261              
1262             There are 6 strings that can match against date only or time only formats. The
1263             C<parse_datetime> method will attempt to match these ambiguous strings against
1264             date only formats. If you want to match against the time only formats use the
1265             C<parse_time> method.
1266              
1267             =head2 Conventions
1268              
1269             =over 4
1270              
1271             =item * Expanded ISO8601
1272              
1273             These formats are supported with exactly 6 digits for the year. Support for a
1274             variable number of digits will be in a later release.
1275              
1276             =item * Precision
1277              
1278             If a format doesn't include a year all larger time unit up to and including the
1279             year are filled in using the current date/time or [if set] the C<base_datetime>
1280             object.
1281              
1282             =item * Fractional time
1283              
1284             There is no limit on the expressed precision.
1285              
1286             =back
1287              
1288             =head2 Supported via parse_datetime
1289              
1290             The supported formats are listed by the section of ISO 8601:2000(E) in which
1291             they appear.
1292              
1293             =head3 5.2 Dates
1294              
1295             =over 4
1296              
1297             =item * 5.2.1.1
1298              
1299             =over 8
1300              
1301             =item YYYYMMDD
1302              
1303             =item YYYY-MM-DD
1304              
1305             =back
1306              
1307             =item * 5.2.1.2
1308              
1309             =over 8
1310              
1311             =item YYYY-MM
1312              
1313             =item YYYY
1314              
1315             =item YY
1316              
1317             =back
1318              
1319             =item * 5.2.1.3
1320              
1321             =over 8
1322              
1323             =item YYMMDD
1324              
1325             =item YY-MM-DD
1326              
1327             =item -YYMM
1328              
1329             =item -YY-MM
1330              
1331             =item -YY
1332              
1333             =item --MMDD
1334              
1335             =item --MM-DD
1336              
1337             =item --MM
1338              
1339             =item ---DD
1340              
1341             =back
1342              
1343             =item * 5.2.1.4
1344              
1345             =over 8
1346              
1347             =item +[YY]YYYYMMDD
1348              
1349             =item +[YY]YYYY-MM-DD
1350              
1351             =item +[YY]YYYY-MM
1352              
1353             =item +[YY]YYYY
1354              
1355             =item +[YY]YY
1356              
1357             =back
1358              
1359             =item * 5.2.2.1
1360              
1361             =over 8
1362              
1363             =item YYYYDDD
1364              
1365             =item YYYY-DDD
1366              
1367             =back
1368              
1369             =item * 5.2.2.2
1370              
1371             =over 8
1372              
1373             =item YYDDD
1374              
1375             =item YY-DDD
1376              
1377             =item -DDD
1378              
1379             =back
1380              
1381             =item * 5.2.2.3
1382              
1383             =over 8
1384              
1385             =item +[YY]YYYYDDD
1386              
1387             =item +[YY]YYYY-DDD
1388              
1389             =back
1390              
1391             =item * 5.2.3.1
1392              
1393             =over 8
1394              
1395             =item YYYYWwwD
1396              
1397             =item YYYY-Www-D
1398              
1399             =back
1400              
1401             =item * 5.2.3.2
1402              
1403             =over 8
1404              
1405             =item YYYYWww
1406              
1407             =item YYYY-Www
1408              
1409             =item YYWwwD
1410              
1411             =item YY-Www-D
1412              
1413             =item YYWww
1414              
1415             =item YY-Www
1416              
1417             =item -YWwwD
1418              
1419             =item -Y-Www-D
1420              
1421             =item -YWww
1422              
1423             =item -Y-Www
1424              
1425             =item -WwwD
1426              
1427             =item -Www-D
1428              
1429             =item -Www
1430              
1431             =item -W-D
1432              
1433             =back
1434              
1435             =item * 5.2.3.4
1436              
1437             =over 8
1438              
1439             =item +[YY]YYYYWwwD
1440              
1441             =item +[YY]YYYY-Www-D
1442              
1443             =item +[YY]YYYYWww
1444              
1445             =item +[YY]YYYY-Www
1446              
1447             =back
1448              
1449             =back
1450              
1451             =head3 5.3 Time of Day
1452              
1453             =over 4
1454              
1455             =item * 5.3.1.1 - 5.3.1.3
1456              
1457             Values can optionally be prefixed with 'T'.
1458              
1459             =item * 5.3.1.1
1460              
1461             =over 8
1462              
1463             =item hh:mm:ss
1464              
1465             =back
1466              
1467             =item * 5.3.1.2
1468              
1469             =over 8
1470              
1471             =item hh:mm
1472              
1473             =back
1474              
1475             =item * 5.3.1.3 - 5.3.1.4
1476              
1477             fractional (decimal) separator maybe either ',' or '.'
1478              
1479             =item * 5.3.1.3
1480              
1481             =over 8
1482              
1483             =item hhmmss,ss
1484              
1485             =item hh:mm:ss,ss
1486              
1487             =item hhmm,mm
1488              
1489             =item hh:mm,mm
1490              
1491             =item hh,hh
1492              
1493             =back
1494              
1495             =item * 5.3.1.4
1496              
1497             =over 8
1498              
1499             =item -mm:ss
1500              
1501             =item -mmss,s
1502              
1503             =item -mm:ss,s
1504              
1505             =item -mm,m
1506              
1507             =item --ss,s
1508              
1509             =back
1510              
1511             =item * 5.3.3 - 5.3.4.2
1512              
1513             Values can optionally be prefixed with 'T'.
1514              
1515             =item * 5.3.3
1516              
1517             =over 8
1518              
1519             =item hhmmssZ
1520              
1521             =item hh:mm:ssZ
1522              
1523             =item hhmmZ
1524              
1525             =item hh:mmZ
1526              
1527             =item hhZ
1528              
1529             =item hhmmss.ssZ
1530              
1531             =item hh:mm:ss.ssZ
1532              
1533             =back
1534              
1535             =item * 5.3.4.2
1536              
1537             =over 8
1538              
1539             =item hhmmss[+-]hhmm
1540              
1541             =item hh:mm:ss[+-]hh:mm
1542              
1543             =item hhmmss[+-]hh
1544              
1545             =item hh:mm:ss[+-]hh
1546              
1547             =item hhmmss.ss[+-]hhmm
1548              
1549             =item hh:mm:ss.ss[+-]hh:mm
1550              
1551             =back
1552              
1553             =back
1554              
1555             =head3 5.4 Combinations of date and time of day
1556              
1557             =over 4
1558              
1559             =item * 5.4.1
1560              
1561             =over 8
1562              
1563             =item YYYYMMDDThhmmss
1564              
1565             =item YYYY-MM-DDThh:mm:ss
1566              
1567             =item YYYYMMDDThhmmssZ
1568              
1569             =item YYYY-MM-DDThh:mm:ssZ
1570              
1571             =item YYYYMMDDThhmmss[+-]hhmm
1572              
1573             =item YYYY-MM-DDThh:mm:ss[+-]hh:mm
1574              
1575             =item YYYYMMDDThhmmss[+-]hh
1576              
1577             =item YYYY-MM-DDThh:mm:ss[+-]hh
1578              
1579             =back
1580              
1581             =item * 5.4.2
1582              
1583             =over 8
1584              
1585             =item YYYYMMDDThhmmss.ss
1586              
1587             =item YYYY-MM-DDThh:mm:ss.ss
1588              
1589             =item YYYYMMDDThhmmss.ss[+-]hh
1590              
1591             =item YYYY-MM-DDThh:mm:ss.ss[+-]hh
1592              
1593             =item YYYYMMDDThhmmss.ss[+-]hhmm
1594              
1595             =item YYYY-MM-DDThh:mm:ss.ss[+-]hh:mm
1596              
1597             =back
1598              
1599             =item * 5.4.3
1600              
1601             Support for this section is not complete.
1602              
1603             =over 8
1604              
1605             =item YYYYMMDDThhmm
1606              
1607             =item YYYY-MM-DDThh:mm
1608              
1609             =item YYYYMMDDThhmmZ
1610              
1611             =item YYYY-MM-DDThh:mmZ
1612              
1613             =item YYYYDDDThhmm
1614              
1615             =item YYYY-DDDThh:mm
1616              
1617             =item YYYYDDDThhmmZ
1618              
1619             =item YYYY-DDDThh:mmZ
1620              
1621             =item YYYYWwwDThhmm[+-]hhmm
1622              
1623             =item YYYY-Www-DThh:mm[+-]hh
1624              
1625             =back
1626              
1627             =back
1628              
1629             =head3 5.5 Time-Intervals
1630              
1631             These are not currently supported
1632              
1633             =head2 Supported via parse_time
1634              
1635             =head3 5.3.1.1 - 5.3.1.3
1636              
1637             Values can optionally be prefixed with 'T'.
1638              
1639             =over 4
1640              
1641             =item * 5.3.1.1
1642              
1643             =over 8
1644              
1645             =item hhmmss
1646              
1647             =back
1648              
1649             =item * 5.3.1.2
1650              
1651             =over 8
1652              
1653             =item hhmm
1654              
1655             =item hh
1656              
1657             =back
1658              
1659             =item * 5.3.1.4
1660              
1661             =over 8
1662              
1663             =item -mmss
1664              
1665             =item -mm
1666              
1667             =item --ss
1668              
1669             =back
1670              
1671             =back
1672              
1673             =head1 STANDARDS DOCUMENT
1674              
1675             =head2 Title
1676              
1677             ISO8601:2000(E)
1678             Data elements and interchange formats - information exchange -
1679             Representation of dates and times
1680             Second edition 2000-12-15
1681              
1682             =head2 Reference Number
1683              
1684             ISO/TC 154 N 362
1685              
1686             =head1 CREDITS
1687              
1688             Iain 'Spoon' Truskett (SPOON) who wrote L<DateTime::Format::Builder>. That has
1689             grown into I<The Vacuum Energy Powered C<Swiss Army> Katana> of date and time
1690             parsing. This module was inspired by and conceived in honor of Iain's work.
1691              
1692             Tom Phoenix (PHOENIX) and PDX.pm for helping me solve the ISO week conversion
1693             bug. Not by fixing the code but motivation me to fix it so I could participate
1694             in a game of C<Zendo>.
1695              
1696             Jonathan Leffler (JOHNL) for reporting a test bug.
1697              
1698             Kelly McCauley for a patch to add 8 missing formats.
1699              
1700             Alasdair Allan (AALLAN) for complaining about excessive test execution time.
1701              
1702             Everyone at the DateTime C<Asylum>.
1703              
1704             =head1 SEE ALSO
1705              
1706             =over 4
1707              
1708             =item *
1709              
1710             L<DateTime>
1711              
1712             =item *
1713              
1714             L<DateTime::Format::Builder>
1715              
1716             =back
1717              
1718             =head1 SUPPORT
1719              
1720             Bugs may be submitted at L<https://github.com/houseabsolute/DateTime-Format-ISO8601/issues>.
1721              
1722             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
1723              
1724             =head1 SOURCE
1725              
1726             The source code repository for DateTime-Format-ISO8601 can be found at L<https://github.com/houseabsolute/DateTime-Format-ISO8601>.
1727              
1728             =head1 AUTHORS
1729              
1730             =over 4
1731              
1732             =item *
1733              
1734             Joshua Hoblitt <josh@hoblitt.com>
1735              
1736             =item *
1737              
1738             Dave Rolsky <autarch@urth.org>
1739              
1740             =back
1741              
1742             =head1 CONTRIBUTORS
1743              
1744             =for stopwords Doug Bell joe Liam Widdowson Thomas Klausner
1745              
1746             =over 4
1747              
1748             =item *
1749              
1750             Doug Bell <doug@preaction.me>
1751              
1752             =item *
1753              
1754             joe <draxil@gmail.com>
1755              
1756             =item *
1757              
1758             Liam Widdowson <lbw@telstra.com>
1759              
1760             =item *
1761              
1762             Thomas Klausner <domm@plix.at>
1763              
1764             =back
1765              
1766             =head1 COPYRIGHT AND LICENSE
1767              
1768             This software is copyright (c) 2021 by Joshua Hoblitt.
1769              
1770             This is free software; you can redistribute it and/or modify it under
1771             the same terms as the Perl 5 programming language system itself.
1772              
1773             The full text of the license can be found in the
1774             F<LICENSE> file included with this distribution.
1775              
1776             =cut