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   2849364 use strict;
  8         63  
  8         244  
5 8     8   45 use warnings;
  8         18  
  8         202  
6 8     8   2833 use namespace::autoclean;
  8         110017  
  8         42  
7              
8             our $VERSION = '0.14';
9              
10 8     8   720 use Carp qw( croak );
  8         19  
  8         468  
11 8     8   5703 use DateTime 1.45;
  8         2855525  
  8         435  
12 8     8   5460 use DateTime::Format::Builder 0.77;
  8         561817  
  8         117  
13 8     8   4841 use DateTime::Format::ISO8601::Types;
  8         28  
  8         57  
14 8     8   95759 use Params::ValidationCompiler 0.26 qw( validation_for );
  8         199  
  8         43674  
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 17516 shift;
27 481 100       1797 ($default_legacy_year) = $validator->(@_)
28             if @_;
29              
30 475         1547 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 219548 shift;
47 10889 100       29276 ($default_cut_off_year) = $validator->(@_)
48             if @_;
49              
50 10883         22974 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 452299 my ($class) = shift;
79 574         14222 my %args = $validator->(@_);
80              
81             $args{legacy_year} = $class->DefaultLegacyYear
82 562 100       17294 unless exists $args{legacy_year};
83             $args{cut_off_year} = $class->DefaultCutOffYear
84 562 100       1815 unless exists $args{cut_off_year};
85              
86 562   33     2250 $class = ref($class) || $class;
87              
88 562         1366 my $self = bless( \%args, $class );
89              
90 562 100       1544 if ( $args{base_datetime} ) {
91 122         758 $self->set_base_datetime( object => $args{base_datetime} );
92             }
93              
94 556         1571 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 327452 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 243 my $self = shift;
114              
115 135         2746 my %args = $validator->(@_);
116              
117             # ISO8601 only allows years 0 to 9999
118             # this implementation ignores the needs of expanded formats
119 135         4494 my $dt = DateTime->from_object( object => $args{object} );
120 135         74150 my $lower_bound = DateTime->new( year => 0 );
121 135         38597 my $upper_bound = DateTime->new( year => 10000 );
122              
123 135 100       37767 if ( $dt < $lower_bound ) {
124 6         446 croak 'base_datetime must be greater then or equal to ',
125             $lower_bound->iso8601;
126             }
127 129 100       9892 if ( $dt >= $upper_bound ) {
128 6         394 croak 'base_datetime must be less then ', $upper_bound->iso8601;
129             }
130              
131 123         7910 $self->{base_datetime} = $dt;
132              
133 123         837 return $self;
134             }
135             }
136              
137 10     10 1 1289 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 15 my $self = shift;
148              
149 9         171 ( $self->{legacy_year} ) = $validator->(@_);
150              
151 3         43 return $self;
152             }
153             }
154              
155 501     501 1 115281 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 204 my $self = shift;
166              
167 106         2011 ( $self->{cut_off_year} ) = $validator->(@_);
168              
169 100         1022 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 31503 my $self = shift;
182 5         124 my ($dt) = $validator->(@_);
183              
184 5 100       102 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       17 if ( $dt->time_zone->is_utc ) {
191 2         19 $tz = 'Z';
192             }
193             else {
194 3         24 $tz = q{};
195 3         9 $cldr .= 'ZZZZZ';
196             }
197              
198 5         26 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   26158 my %p = @_;
900              
901 12         39 my $year = _base_dt( $p{self} )->year;
902              
903 12         2530 $year =~ s/.$//;
904 12         39 $p{parsed}{year} = $year . $p{parsed}{year};
905              
906 12         41 return 1;
907             }
908              
909             sub _fix_2_digit_year {
910 21033     21033   16532959 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     91353 if ( ref $p{self} && exists $p{self}{legacy_year} ) {
915 11022 100       24805 if ( $p{self}{legacy_year} ) {
916             my $cutoff
917             = exists $p{self}{cut_off_year}
918             ? $p{self}{cut_off_year}
919 10022 50       22790 : $p{self}->DefaultCutOffYear;
920 10022 100       36495 $p{parsed}{year} += $p{parsed}{year} > $cutoff ? 1900 : 2000;
921             }
922             else {
923 1000   33     3633 my $century = ( $p{self}{base_datetime} || DateTime->now )
924             ->strftime('%C');
925 1000         34502 $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     32424 : $p{self}->DefaultCutOffYear;
933 10011 100       37117 $p{parsed}{year} += $p{parsed}{year} > $cutoff ? 1900 : 2000;
934             }
935              
936 21033         64260 return 1;
937             }
938              
939             sub _add_minute {
940 11     11   109 my %p = @_;
941              
942 11         31 $p{parsed}{minute} = _base_dt( $p{self} )->minute;
943              
944 11         1145 return 1;
945             }
946              
947             sub _add_hour {
948 29     29   284 my %p = @_;
949              
950 29         72 $p{parsed}{hour} = _base_dt( $p{self} )->hour;
951              
952 29         4456 return 1;
953             }
954              
955             sub _add_day {
956 116     116   1028 my %p = @_;
957              
958 116         274 $p{parsed}{day} = _base_dt( $p{self} )->day;
959              
960 116         19964 return 1;
961             }
962              
963             sub _add_week {
964 8     8   83 my %p = @_;
965              
966 8         26 $p{parsed}{week} = _base_dt( $p{self} )->week;
967              
968 8         871 return 1;
969             }
970              
971             sub _add_month {
972 119     119   1120 my %p = @_;
973              
974 119         259 $p{parsed}{month} = _base_dt( $p{self} )->month;
975              
976 119         21124 return 1;
977             }
978              
979             sub _add_year {
980 142     142   351831 my %p = @_;
981              
982 142         398 $p{parsed}{year} = _base_dt( $p{self} )->year;
983              
984 142         27029 return 1;
985             }
986              
987             sub _base_dt {
988 437 100 100 437   2056 return $_[0]{base_datetime} if ref $_[0] && $_[0]{base_datetime};
989 268         874 return DateTime->now;
990             }
991              
992             sub _fractional_second {
993 62     62   111380 my %p = @_;
994              
995             ## no critic (ValuesAndExpressions::ProhibitMismatchedOperators)
996 62         329 $p{parsed}{nanosecond} = ".$p{ parsed }{ nanosecond }" * 10**9;
997              
998 62         181 return 1;
999             }
1000              
1001             sub _fractional_minute {
1002 9     9   83 my %p = @_;
1003              
1004             ## no critic (ValuesAndExpressions::ProhibitMismatchedOperators)
1005 9         49 $p{parsed}{second} = ".$p{ parsed }{ second }" * 60;
1006              
1007 9         28 return 1;
1008             }
1009              
1010             sub _fractional_hour {
1011 3     3   33 my %p = @_;
1012              
1013             ## no critic (ValuesAndExpressions::ProhibitMismatchedOperators)
1014 3         19 $p{parsed}{minute} = ".$p{ parsed }{ minute }" * 60;
1015              
1016 3         12 return 1;
1017             }
1018              
1019             sub _normalize_offset {
1020 66     66   36113 my %p = @_;
1021              
1022 66         224 $p{parsed}{time_zone} =~ s/://;
1023              
1024 66 100       263 if ( length $p{parsed}{time_zone} == 3 ) {
1025 27         65 $p{parsed}{time_zone} .= '00';
1026             }
1027              
1028 66         177 return 1;
1029             }
1030              
1031             sub _normalize_week {
1032 3980     3980   15386290 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         14007 my $od = $p{parsed}{week} * 7;
1038 3980 100       13453 $od += ( exists $p{parsed}{day_of_week} ? $p{parsed}{day_of_week} : 1 );
1039             $od -= DateTime->new(
1040             year => $p{parsed}{year},
1041 3980         15662 month => 1,
1042             day => 4,
1043             )->day_of_week + 3;
1044              
1045 3980         1211503 my ( $year, $day_of_year );
1046 3980 100       10812 if ( $od < 1 ) {
1047 20         60 $year = $p{parsed}{year} - 1;
1048 20         82 $day_of_year = DateTime->new( year => $year )->year_length + $od;
1049             }
1050             else {
1051             my $year_length
1052 3960         12972 = DateTime->new( year => $p{parsed}{year} )->year_length;
1053 3960 100       1170912 if ( $od > $year_length ) {
1054 6         22 $year = $p{parsed}{year} + 1;
1055 6         14 $day_of_year = $od - $year_length;
1056             }
1057             else {
1058 3954         9682 $year = $p{parsed}{year};
1059 3954         8892 $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         13313 $p{parsed}{year} = $year;
1066 3980         7756 $p{parsed}{day_of_year} = $day_of_year;
1067              
1068 3980         8070 delete $p{parsed}{week};
1069 3980         6211 delete $p{parsed}{day_of_week};
1070              
1071 3980         13018 return 1;
1072             }
1073              
1074             sub _normalize_century {
1075 6     6   71601 my %p = @_;
1076              
1077 6         23 $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.14
1099              
1100             =head1 SYNOPSIS
1101              
1102             use DateTime::Format::ISO8601;
1103              
1104             my $str = '2020-07-25T11:32:31';
1105              
1106             my $dt = DateTime::Format::ISO8601->parse_datetime($str);
1107             $dt = DateTime::Format::ISO8601->parse_time($str);
1108              
1109             # or
1110              
1111             my $iso8601 = DateTime::Format::ISO8601->new;
1112             $dt = $iso8601->parse_datetime($str);
1113             $dt = $iso8601->parse_time($str);
1114              
1115             $str = DateTime::Format::ISO8601->format_datetime($dt);
1116              
1117             =head1 DESCRIPTION
1118              
1119             Parses almost all ISO8601 date and time formats. ISO8601 time-intervals will
1120             be supported in a later release.
1121              
1122             =head1 METHODS
1123              
1124             This class provides the following methods:
1125              
1126             =head2 Constructors
1127              
1128             =head3 DateTime::Format::ISO8601->new( ... )
1129              
1130             Accepts an optional hash.
1131              
1132             my $iso8601 = DateTime::Format::ISO8601->new(
1133             base_datetime => $dt,
1134             cut_off_year => 42,
1135             legacy_year => 1,
1136             );
1137              
1138             =over 4
1139              
1140             =item * base_datetime
1141              
1142             A C<DateTime> object that will be used to fill in missing information from
1143             incomplete date/time formats.
1144              
1145             This key is optional.
1146              
1147             =item * cut_off_year
1148              
1149             A integer representing the cut-off point between interpreting 2-digits years
1150             as 19xx or 20xx.
1151              
1152             2-digit years < cut_off_year will be interpreted as 20xx
1153             2-digit years >= cut_off_year will be untreated as 19xx
1154              
1155             This key defaults to the value of C<DefaultCutOffYear>.
1156              
1157             =item * legacy_year
1158              
1159             A boolean value controlling if a 2-digit year is interpreted as being in the
1160             current century (unless a C<base_datetime> is set) or if C<cut_off_year>
1161             should be used to place the year in either 20xx or 19xx.
1162              
1163             If this is true, then the C<cut_off_year> is used. If this is false, then the
1164             year is always interpreted as being in the current century.
1165              
1166             This key defaults to the value of C<DefaultLegacyYear>.
1167              
1168             =back
1169              
1170             =head3 $iso8601->clone
1171              
1172             Returns a replica of the given object.
1173              
1174             =head2 Object Methods
1175              
1176             =head3 $iso8601->base_datetime
1177              
1178             Returns a C<DateTime> object if a C<base_datetime> has been set.
1179              
1180             =head3 $iso8601->set_base_datetime( object => $object )
1181              
1182             Accepts a C<DateTime> object that will be used to fill in missing information
1183             from incomplete date/time formats.
1184              
1185             =head3 $iso8601->cut_off_year
1186              
1187             Returns a integer representing the cut-off point between interpreting
1188             2-digits years as 19xx or 20xx.
1189              
1190             =head3 $iso8601->set_cut_off_year($int)
1191              
1192             Accepts a integer representing the cut-off point between interpreting
1193             2-digits years as 19xx or 20xx.
1194              
1195             2-digit years < legacy_year will be interpreted as 20xx
1196             2-digit years >= legacy_year will be interpreted as 19xx
1197              
1198             =head3 $iso8601->legacy_year
1199              
1200             Returns a boolean value indicating the 2-digit year handling behavior.
1201              
1202             =head3 $iso8601->set_legacy_year($bool)
1203              
1204             Accepts a boolean value controlling if a 2-digit year is interpreted as being
1205             in the current century (unless a C<base_datetime> is set) or if
1206             C<cut_off_year> should be used to place the year in either 20xx or 19xx.
1207              
1208             =head2 Class Methods
1209              
1210             =head3 DateTime::Format::ISO8601->DefaultCutOffYear($int)
1211              
1212             Accepts a integer representing the cut-off point for 2-digit years when
1213             calling C<parse_*> as class methods and the default value for C<cut_off_year>
1214             when creating objects. If called with no parameters this method will return
1215             the default value for C<cut_off_year>.
1216              
1217             =head3 DateTime::Format::ISO8601->DefaultLegacyYear($bool)
1218              
1219             Accepts a boolean value controlling the legacy year behavior when calling
1220             C<parse_*> as class methods and the default value for C<legacy_year> when
1221             creating objects. If called with no parameters this method will return the
1222             default value for C<legacy_year>.
1223              
1224             =head2 Parser(s)
1225              
1226             These methods may be called as either class or object methods.
1227              
1228             =head3 parse_datetime
1229              
1230             =head3 parse_time
1231              
1232             Please see the L</FORMATS> section.
1233              
1234             =head2 Formatter
1235              
1236             This may be called as either class or object method.
1237              
1238             =head3 format_datetime($dt)
1239              
1240             Formats the datetime in an ISO8601-compatible format. This differs from
1241             L<DateTime/iso8601> by including nanoseconds/milliseconds and the correct
1242             timezone offset.
1243              
1244             =head1 FORMATS
1245              
1246             There are 6 strings that can match against date only or time only formats.
1247             The C<parse_datetime> method will attempt to match these ambiguous strings
1248             against date only formats. If you want to match against the time only formats
1249             use the C<parse_time> method.
1250              
1251             =head2 Conventions
1252              
1253             =over 4
1254              
1255             =item * Expanded ISO8601
1256              
1257             These formats are supported with exactly 6 digits for the year.
1258             Support for a variable number of digits will be in a later release.
1259              
1260             =item * Precision
1261              
1262             If a format doesn't include a year all larger time unit up to and including
1263             the year are filled in using the current date/time or [if set] the
1264             C<base_datetime> object.
1265              
1266             =item * Fractional time
1267              
1268             There is no limit on the expressed precision.
1269              
1270             =back
1271              
1272             =head2 Supported via parse_datetime
1273              
1274             The supported formats are listed by the section of ISO 8601:2000(E) in
1275             which they appear.
1276              
1277             =head3 5.2 Dates
1278              
1279             =over 4
1280              
1281             =item * 5.2.1.1
1282              
1283             =over 8
1284              
1285             =item YYYYMMDD
1286              
1287             =item YYYY-MM-DD
1288              
1289             =back
1290              
1291             =item * 5.2.1.2
1292              
1293             =over 8
1294              
1295             =item YYYY-MM
1296              
1297             =item YYYY
1298              
1299             =item YY
1300              
1301             =back
1302              
1303             =item * 5.2.1.3
1304              
1305             =over 8
1306              
1307             =item YYMMDD
1308              
1309             =item YY-MM-DD
1310              
1311             =item -YYMM
1312              
1313             =item -YY-MM
1314              
1315             =item -YY
1316              
1317             =item --MMDD
1318              
1319             =item --MM-DD
1320              
1321             =item --MM
1322              
1323             =item ---DD
1324              
1325             =back
1326              
1327             =item * 5.2.1.4
1328              
1329             =over 8
1330              
1331             =item +[YY]YYYYMMDD
1332              
1333             =item +[YY]YYYY-MM-DD
1334              
1335             =item +[YY]YYYY-MM
1336              
1337             =item +[YY]YYYY
1338              
1339             =item +[YY]YY
1340              
1341             =back
1342              
1343             =item * 5.2.2.1
1344              
1345             =over 8
1346              
1347             =item YYYYDDD
1348              
1349             =item YYYY-DDD
1350              
1351             =back
1352              
1353             =item * 5.2.2.2
1354              
1355             =over 8
1356              
1357             =item YYDDD
1358              
1359             =item YY-DDD
1360              
1361             =item -DDD
1362              
1363             =back
1364              
1365             =item * 5.2.2.3
1366              
1367             =over 8
1368              
1369             =item +[YY]YYYYDDD
1370              
1371             =item +[YY]YYYY-DDD
1372              
1373             =back
1374              
1375             =item * 5.2.3.1
1376              
1377             =over 8
1378              
1379             =item YYYYWwwD
1380              
1381             =item YYYY-Www-D
1382              
1383             =back
1384              
1385             =item * 5.2.3.2
1386              
1387             =over 8
1388              
1389             =item YYYYWww
1390              
1391             =item YYYY-Www
1392              
1393             =item YYWwwD
1394              
1395             =item YY-Www-D
1396              
1397             =item YYWww
1398              
1399             =item YY-Www
1400              
1401             =item -YWwwD
1402              
1403             =item -Y-Www-D
1404              
1405             =item -YWww
1406              
1407             =item -Y-Www
1408              
1409             =item -WwwD
1410              
1411             =item -Www-D
1412              
1413             =item -Www
1414              
1415             =item -W-D
1416              
1417             =back
1418              
1419             =item * 5.2.3.4
1420              
1421             =over 8
1422              
1423             =item +[YY]YYYYWwwD
1424              
1425             =item +[YY]YYYY-Www-D
1426              
1427             =item +[YY]YYYYWww
1428              
1429             =item +[YY]YYYY-Www
1430              
1431             =back
1432              
1433             =back
1434              
1435             =head3 5.3 Time of Day
1436              
1437             =over 4
1438              
1439             =item * 5.3.1.1 - 5.3.1.3
1440              
1441             Values can optionally be prefixed with 'T'.
1442              
1443             =item * 5.3.1.1
1444              
1445             =over 8
1446              
1447             =item hh:mm:ss
1448              
1449             =back
1450              
1451             =item * 5.3.1.2
1452              
1453             =over 8
1454              
1455             =item hh:mm
1456              
1457             =back
1458              
1459             =item * 5.3.1.3 - 5.3.1.4
1460              
1461             fractional (decimal) separator maybe either ',' or '.'
1462              
1463             =item * 5.3.1.3
1464              
1465             =over 8
1466              
1467             =item hhmmss,ss
1468              
1469             =item hh:mm:ss,ss
1470              
1471             =item hhmm,mm
1472              
1473             =item hh:mm,mm
1474              
1475             =item hh,hh
1476              
1477             =back
1478              
1479             =item * 5.3.1.4
1480              
1481             =over 8
1482              
1483             =item -mm:ss
1484              
1485             =item -mmss,s
1486              
1487             =item -mm:ss,s
1488              
1489             =item -mm,m
1490              
1491             =item --ss,s
1492              
1493             =back
1494              
1495             =item * 5.3.3 - 5.3.4.2
1496              
1497             Values can optionally be prefixed with 'T'.
1498              
1499             =item * 5.3.3
1500              
1501             =over 8
1502              
1503             =item hhmmssZ
1504              
1505             =item hh:mm:ssZ
1506              
1507             =item hhmmZ
1508              
1509             =item hh:mmZ
1510              
1511             =item hhZ
1512              
1513             =item hhmmss.ssZ
1514              
1515             =item hh:mm:ss.ssZ
1516              
1517             =back
1518              
1519             =item * 5.3.4.2
1520              
1521             =over 8
1522              
1523             =item hhmmss[+-]hhmm
1524              
1525             =item hh:mm:ss[+-]hh:mm
1526              
1527             =item hhmmss[+-]hh
1528              
1529             =item hh:mm:ss[+-]hh
1530              
1531             =item hhmmss.ss[+-]hhmm
1532              
1533             =item hh:mm:ss.ss[+-]hh:mm
1534              
1535             =back
1536              
1537             =back
1538              
1539             =head3 5.4 Combinations of date and time of day
1540              
1541             =over 4
1542              
1543             =item * 5.4.1
1544              
1545             =over 8
1546              
1547             =item YYYYMMDDThhmmss
1548              
1549             =item YYYY-MM-DDThh:mm:ss
1550              
1551             =item YYYYMMDDThhmmssZ
1552              
1553             =item YYYY-MM-DDThh:mm:ssZ
1554              
1555             =item YYYYMMDDThhmmss[+-]hhmm
1556              
1557             =item YYYY-MM-DDThh:mm:ss[+-]hh:mm
1558              
1559             =item YYYYMMDDThhmmss[+-]hh
1560              
1561             =item YYYY-MM-DDThh:mm:ss[+-]hh
1562              
1563             =back
1564              
1565             =item * 5.4.2
1566              
1567             =over 8
1568              
1569             =item YYYYMMDDThhmmss.ss
1570              
1571             =item YYYY-MM-DDThh:mm:ss.ss
1572              
1573             =item YYYYMMDDThhmmss.ss[+-]hh
1574              
1575             =item YYYY-MM-DDThh:mm:ss.ss[+-]hh
1576              
1577             =item YYYYMMDDThhmmss.ss[+-]hhmm
1578              
1579             =item YYYY-MM-DDThh:mm:ss.ss[+-]hh:mm
1580              
1581             =back
1582              
1583             =item * 5.4.3
1584              
1585             Support for this section is not complete.
1586              
1587             =over 8
1588              
1589             =item YYYYMMDDThhmm
1590              
1591             =item YYYY-MM-DDThh:mm
1592              
1593             =item YYYYMMDDThhmmZ
1594              
1595             =item YYYY-MM-DDThh:mmZ
1596              
1597             =item YYYYDDDThhmm
1598              
1599             =item YYYY-DDDThh:mm
1600              
1601             =item YYYYDDDThhmmZ
1602              
1603             =item YYYY-DDDThh:mmZ
1604              
1605             =item YYYYWwwDThhmm[+-]hhmm
1606              
1607             =item YYYY-Www-DThh:mm[+-]hh
1608              
1609             =back
1610              
1611             =back
1612              
1613             =head3 5.5 Time-Intervals
1614              
1615             These are not currently supported
1616              
1617             =head2 Supported via parse_time
1618              
1619             =head3 5.3.1.1 - 5.3.1.3
1620              
1621             Values can optionally be prefixed with 'T'.
1622              
1623             =over 4
1624              
1625             =item * 5.3.1.1
1626              
1627             =over 8
1628              
1629             =item hhmmss
1630              
1631             =back
1632              
1633             =item * 5.3.1.2
1634              
1635             =over 8
1636              
1637             =item hhmm
1638              
1639             =item hh
1640              
1641             =back
1642              
1643             =item * 5.3.1.4
1644              
1645             =over 8
1646              
1647             =item -mmss
1648              
1649             =item -mm
1650              
1651             =item --ss
1652              
1653             =back
1654              
1655             =back
1656              
1657             =head1 STANDARDS DOCUMENT
1658              
1659             =head2 Title
1660              
1661             ISO8601:2000(E)
1662             Data elements and interchange formats - information exchange -
1663             Representation of dates and times
1664             Second edition 2000-12-15
1665              
1666             =head2 Reference Number
1667              
1668             ISO/TC 154 N 362
1669              
1670             =head1 CREDITS
1671              
1672             Iain 'Spoon' Truskett (SPOON) who wrote L<DateTime::Format::Builder>.
1673             That has grown into I<The Vacuum Energy Powered C<Swiss Army> Katana>
1674             of date and time parsing. This module was inspired by and conceived
1675             in honor of Iain's work.
1676              
1677             Tom Phoenix (PHOENIX) and PDX.pm for helping me solve the ISO week conversion
1678             bug. Not by fixing the code but motivation me to fix it so I could
1679             participate in a game of C<Zendo>.
1680              
1681             Jonathan Leffler (JOHNL) for reporting a test bug.
1682              
1683             Kelly McCauley for a patch to add 8 missing formats.
1684              
1685             Alasdair Allan (AALLAN) for complaining about excessive test execution time.
1686              
1687             Everyone at the DateTime C<Asylum>.
1688              
1689             =head1 SEE ALSO
1690              
1691             =over 4
1692              
1693             =item *
1694              
1695             L<DateTime>
1696              
1697             =item *
1698              
1699             L<DateTime::Format::Builder>
1700              
1701             =back
1702              
1703             =head1 SUPPORT
1704              
1705             Bugs may be submitted at L<https://github.com/houseabsolute/DateTime-Format-ISO8601/issues>.
1706              
1707             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
1708              
1709             =head1 SOURCE
1710              
1711             The source code repository for DateTime-Format-ISO8601 can be found at L<https://github.com/houseabsolute/DateTime-Format-ISO8601>.
1712              
1713             =head1 AUTHORS
1714              
1715             =over 4
1716              
1717             =item *
1718              
1719             Joshua Hoblitt <josh@hoblitt.com>
1720              
1721             =item *
1722              
1723             Dave Rolsky <autarch@urth.org>
1724              
1725             =back
1726              
1727             =head1 CONTRIBUTORS
1728              
1729             =for stopwords Doug Bell joe Thomas Klausner
1730              
1731             =over 4
1732              
1733             =item *
1734              
1735             Doug Bell <doug@preaction.me>
1736              
1737             =item *
1738              
1739             joe <draxil@gmail.com>
1740              
1741             =item *
1742              
1743             Thomas Klausner <domm@plix.at>
1744              
1745             =back
1746              
1747             =head1 COPYRIGHT AND LICENSE
1748              
1749             This software is copyright (c) 2020 by Joshua Hoblitt.
1750              
1751             This is free software; you can redistribute it and/or modify it under
1752             the same terms as the Perl 5 programming language system itself.
1753              
1754             The full text of the license can be found in the
1755             F<LICENSE> file included with this distribution.
1756              
1757             =cut