File Coverage

blib/lib/Time/P.pm
Criterion Covered Total %
statement 234 339 69.0
branch 104 188 55.3
condition 52 87 59.7
subroutine 25 26 96.1
pod 1 1 100.0
total 416 641 64.9


line stmt bran cond sub pod time code
1 10     10   63 use strict;
  10         17  
  10         272  
2 10     10   46 use warnings;
  10         19  
  10         440  
3             package Time::P;
4             $Time::P::VERSION = '0.024';
5             # ABSTRACT: Parse times from strings.
6              
7 10     10   49 use Carp qw/ croak /;
  10         19  
  10         446  
8 10     10   53 use Exporter qw/ import /;
  10         17  
  10         279  
9 10     10   79 use Function::Parameters qw/ :lax /;
  10         19  
  10         56  
10 10     10   8960 use Data::Munge qw/ list2re /;
  10         13111  
  10         581  
11 10     10   70 use List::Util qw/ uniq /;
  10         21  
  10         849  
12              
13 10     10   3351 use Time::C::Util qw/ get_fmt_tok get_locale /;
  10         28  
  10         580  
14              
15 10     10   69 use constant DEBUG => 0;
  10         18  
  10         1553  
16              
17             our @EXPORT = qw/ strptime /;
18              
19              
20             my %parser; %parser = (
21             '%A' => fun (:$locale) {
22             my @weekdays = @{ get_locale(weekdays => $locale) };
23             my $re = list2re(@weekdays);
24             return qr"(?$re)";
25             },
26             '%a' => fun (:$locale) {
27             my @weekdays_abbr = @{ get_locale(weekdays_abbr => $locale) };
28             my $re = list2re(@weekdays_abbr);
29             return qr"(?$re)";
30             },
31             '%B' => fun (:$locale) {
32             my @months = @{ get_locale(months => $locale) };
33             my $re = list2re(@months);
34             return qr"(?$re)";
35             },
36             '%b' => fun (:$locale) {
37             my @months_abbr = @{ get_locale(months_abbr => $locale) };
38             my $re = list2re(@months_abbr);
39             return qr"(?$re)";
40             },
41             '%C' => fun () { qr"(?[0-9][0-9])"; },
42             '%-C' => fun () { qr"(?[0-9][0-9]?)"; },
43             '%c' => fun (:$locale) { _compile_fmt(get_locale(datetime => $locale), locale => $locale); },
44             '%D' => fun () {
45             return $parser{'%m'}->(), qr!/!, $parser{'%d'}->(), qr!/!, $parser{'%y'}->();
46             },
47             '%d' => fun () { qr"(?[0-9][0-9])"; },
48             '%-d' => fun () { qr"(?[0-9][0-9]?)"; },
49             '%EC' => fun (:$locale) {
50             my @eras = _get_eras(period => $locale);
51             return $parser{'%C'}->() if not @eras;
52             my $re = list2re(@eras);
53             return qr"(?$re)";
54             },
55             '%Ec' => fun (:$locale) { _compile_fmt(get_locale(era_datetime => $locale), locale => $locale); },
56             '%EX' => fun (:$locale) { _compile_fmt(get_locale(era_time => $locale), locale => $locale); },
57             '%Ex' => fun (:$locale) { _compile_fmt(get_locale(era_date => $locale), locale => $locale); },
58             '%EY' => fun (:$locale) {
59             my @eras = _get_eras(full => $locale);
60             return $parser{'%Y'}->() if not @eras;
61              
62             my @ret = map { my $re = join "", _compile_fmt($_, locale => $locale); qr/$re/ } uniq @eras;
63             my $full_re = join "|", @ret;
64             return qr/$full_re/;
65             },
66             '%Ey' => fun () { qr"(?[0-9]+)"; },
67             '%e' => fun () { qr"(?:\s(?[0-9])|(?[0-9][0-9]))"; },
68             '%-e' => fun () { qr"(?[0-9][0-9]?)"; },
69             '%F' => fun () {
70             return $parser{'%Y'}->(), qr/-/, $parser{'%m'}->(), qr/-/, $parser{'%d'}->();
71             },
72             '%G' => fun () { qr"(?[0-9]{4})"; },
73             '%-G' => fun () { qr"(?[0-9]{1,4})"; },
74             '%g' => fun () { qr"(?[0-9][0-9])"; },
75             '%-g' => fun () { qr"(?[0-9][0-9]?)"; },
76             '%H' => fun () { qr"(?[0-9][0-9])"; },
77             '%-H' => fun () { qr"(?[0-9][0-9]?)"; },
78             '%h' => fun (:$locale) { $parser{'%b'}->(locale => $locale) },
79             '%I' => fun () { qr"(?[0-9][0-9])"; },
80             '%-I' => fun () { qr"(?[0-9][0-9]?)"; },
81             '%j' => fun () { qr"(?[0-9]{3})"; },
82             '%-j' => fun () { qr"(?[0-9]{1,3})"; },
83             '%k' => fun () { qr"(?:\s(?[0-9])|(?[0-9][0-9]))"; },
84             '%-k' => fun () { qr"(?[0-9][0-9]?)"; },
85             '%l' => fun () { qr"(?:\s(?[0-9])|(?[0-9][0-9]))"; },
86             '%-l' => fun () { qr"(?[0-9][0-9]?)"; },
87             '%M' => fun () { qr"(?[0-9][0-9])"; },
88             '%-M' => fun () { qr"(?[0-9][0-9]?)"; },
89             '%m' => fun () { qr"(?[0-9][0-9])"; },
90             '%-m' => fun () { qr"(?[0-9][0-9]?)"; },
91             '%n' => fun () { qr"\s+"; },
92             '%OC' => fun (:$locale) {
93             my @d = @{ get_locale(digits => $locale) };
94             return $parser{'%d'}->() if not @d;
95             croak "Not enough digits in alt_digits for $locale to represent %OC." if @d < 100;
96             my $re = list2re(@d);
97             return qr"(?$re)";
98             },
99             '%Od' => fun (:$locale) {
100             my @d = @{ get_locale(digits => $locale) };
101             return $parser{'%d'}->() if not @d;
102             croak "Not enough digits in alt_digits for $locale to represent %Od." if @d < 32;
103             my $re = list2re(@d);
104             return qr"(?$re)";
105             },
106             '%Oe' => fun (:$locale) {
107             my @d = @{ get_locale(digits => $locale) };
108             return $parser{'%e'}->() if not @d;
109             croak "Not enough digits in alt_digits for $locale to represent %Oe." if @d < 32;
110             my $re = list2re(@d);
111             return qr"(?$re)";
112             },
113             '%OH' => fun (:$locale) {
114             my @d = @{ get_locale(digits => $locale) };
115             return $parser{'%H'}->() if not @d;
116             croak "Not enough digits in alt_digits for $locale to represent %OH." if @d < 24;
117             my $re = list2re(@d);
118             return qr"(?$re)";
119             },
120             '%OI' => fun (:$locale) {
121             my @d = @{ get_locale(digits => $locale) };
122             return $parser{'%I'}->() if not @d;
123             croak "Not enough digits in alt_digits for $locale to represent %OI." if @d < 13;
124             my $re = list2re(@d);
125             return qr"(?$re)";
126             },
127             '%OM' => fun (:$locale) {
128             my @d = @{ get_locale(digits => $locale) };
129             return $parser{'%M'}->() if not @d;
130             croak "Not enough digits in alt_digits for $locale to represent %OM." if @d < 60;
131             my $re = list2re(@d);
132             return qr"(?$re)";
133             },
134             '%Om' => fun (:$locale) {
135             my @d = @{ get_locale(digits => $locale) };
136             return $parser{'%m'}->() if not @d;
137             croak "Not enough digits in alt_digits for $locale to represent %Om." if @d < 13;
138             my $re = list2re(@d);
139             return qr"(?$re)";
140             },
141             '%Op' => fun (:$locale) { $parser{'%p'}->(locale => $locale); }, # one %c spec in my_MM locale erroneously says %Op instead of %p
142             '%OS' => fun (:$locale) {
143             my @d = @{ get_locale(digits => $locale) };
144             return $parser{'%S'}->() if not @d;
145             croak "Not enough digits in alt_digits for $locale to represent %OS." if @d < 60;
146             my $re = list2re(@d);
147             return qr"(?$re)";
148             },
149             '%OU' => fun (:$locale) {
150             my @d = @{ get_locale(digits => $locale) };
151             return $parser{'%U'}->() if not @d;
152             croak "Not enough digits in alt_digits for $locale to represent %OU." if @d < 54;
153             my $re = list2re(@d);
154             return qr"(?$re)";
155             },
156             '%Ou' => fun (:$locale) {
157             my @d = @{ get_locale(digits => $locale) };
158             return $parser{'%u'}->() if not @d;
159             croak "Not enough digits in alt_digits for $locale to represent %Ou." if @d < 8;
160             my $re = list2re(@d);
161             return qr"(?$re)";
162             },
163             '%OV' => fun (:$locale) {
164             my @d = @{ get_locale(digits => $locale) };
165             return $parser{'%V'}->() if not @d;
166             croak "Not enough digits in alt_digits for $locale to represent %OV." if @d < 54;
167             my $re = list2re(@d);
168             return qr"(?$re)";
169             },
170             '%OW' => fun (:$locale) {
171             my @d = @{ get_locale(digits => $locale) };
172             return $parser{'%W'}->() if not @d;
173             croak "Not enough digits in alt_digits for $locale to represent %OW." if @d < 54;
174             my $re = list2re(@d);
175             return qr"(?$re)";
176             },
177             '%Ow' => fun (:$locale) {
178             my @d = @{ get_locale(digits => $locale) };
179             return $parser{'%w'}->() if not @d;
180             croak "Not enough digits in alt_digits for $locale to represent %Ow." if @d < 7;
181             my $re = list2re(@d);
182             return qr"(?$re)";
183             },
184             '%Oy' => fun (:$locale) {
185             my @d = @{ get_locale(digits => $locale) };
186             return $parser{'%y'}->() if not @d;
187             croak "Not enough digits in alt_digits for $locale to represent %Oy." if @d < 100;
188             my $re = list2re(@d);
189             return qr"(?$re)";
190             },
191             '%P' => fun (:$locale) { $parser{'%p'}->(locale => $locale); }, # a few %r specs in some locales erroneously say %P instead of %p (wal_ET, ur_PK, pa_PK, iw_IL, he_IL, en_GB, dv_MV, cy_GB)
192             '%p' => fun (:$locale) {
193             my @am_pm = @{ get_locale(am_pm => $locale) };
194             return () unless @am_pm;
195             my $re = list2re(@am_pm);
196             return qr"(?

$re)";

197             },
198             '%X' => fun (:$locale) { _compile_fmt(get_locale(time => $locale), locale => $locale); },
199             '%x' => fun (:$locale) { _compile_fmt(get_locale(date => $locale), locale => $locale); },
200             '%R' => fun () {
201             return $parser{'%H'}->(), qr/:/, $parser{'%M'}->();
202             },
203             '%r' => fun (:$locale) { _compile_fmt(get_locale(time_ampm => $locale), locale => $locale); },
204             '%S' => fun () { qr"(?[0-9][0-9])"; },
205             '%-S' => fun () { qr"(?[0-9][0-9]?)"; },
206             '%s' => fun () { qr"\s*(?[0-9]+)"; },
207             '%T' => fun () {
208             return $parser{'%H'}->(), qr/:/, $parser{'%M'}->(), qr/:/, $parser{'%S'}->();
209             },
210             '%t' => fun () { qr"\s+"; },
211             '%U' => fun () { qr"(?[0-9][0-9])"; },
212             '%-U' => fun () { qr"(?[0-9][0-9]?)"; },
213             '%u' => fun () { qr"(?[0-9])"; },
214             '%V' => fun () { qr"(?[0-9][0-9])"; },
215             '%-V' => fun () { qr"(?[0-9][0-9]?)"; },
216             '%v' => fun (:$locale) {
217             return $parser{'%e'}->(), qr/-/, $parser{'%b'}->(locale => $locale), qr/-/, $parser{'%Y'}->()
218             },
219             '%W' => fun () { qr"(?[0-9][0-9])"; },
220             '%-W' => fun () { qr"(?[0-9][0-9]?)"; },
221             '%w' => fun () { qr"(?[0-9])"; },
222             '%Y' => fun () { qr"(?-?[0-9]{4})"; },
223             '%-Y' => fun () { qr"(?-?[0-9]{1,4})"; },
224             '%y' => fun () { qr"(?[0-9][0-9])"; },
225             '%-y' => fun () { qr"(?[0-9][0-9]?)"; },
226             '%Z' => fun () { qr"(?\S+)"; },
227             '%z' => fun () { qr"(?[-+][0-9][0-9](?::?[0-9][0-9])?)"; },
228             '%%' => fun () { qr"%"; },
229             );
230              
231              
232 190 100   190 1 1301 fun strptime ($str, $fmt, :$locale = 'C', :$strict = 1, :$struct = {}) {
  190 100       588  
  190 100       487  
  190         467  
  190         420  
  190         274  
233 190         314 my %parse = ();
234              
235 190         563 my @res = _compile_fmt($fmt, locale => $locale);
236              
237 190 50       499 croak "Could not match '%s' using '%s'.", $str, $fmt if not @res;
238              
239 190 50       902 @res = (qr/^/, @res, qr/$/) if $strict;
240              
241 190         316 my $re;
242 190   66     2430 while (defined ($re = shift @res) and $str =~ m/\G$re/gc) {
243 1531         3450 warn "matched with $re\n" if DEBUG;
244 9     9   60992 %parse = (%parse, %+);
  9         3042  
  9         1021  
  1531         23941  
245             }
246              
247 190 50       600 if (@res) {
248 0         0 croak sprintf "Could not match '%s' using '%s'. Match failed at position %d (%s) while trying to match with /%s/.", $str, $fmt, pos($str), substr($str, pos($str)), $re;
249             }
250              
251 190         589 $struct = { %$struct, _coerce_struct(\%parse, $struct, locale => $locale) };
252              
253 190         1341 return %$struct;
254             }
255              
256 195     195   484 fun _compile_fmt ($fmt, :$locale) {
  195         340  
  195         366  
  195         263  
257 195         345 my @res = ();
258              
259 195         280 my $pos = 0;
260              
261             # _get_tok will increment $pos for us
262 195         522 while (defined(my $tok = get_fmt_tok($fmt, $pos))) {
263 1152 100       2718 if (exists $parser{$tok}) {
    50          
264 585         1334 my @p_res = $parser{$tok}->(locale => $locale);
265 585         930 warn "pushing @p_res to list\n" if DEBUG;
266 585         1521 push @res, @p_res;
267             } elsif ($tok =~ /^%/) {
268 0         0 croak "Unsupported format specifier: $tok";
269             } else {
270 567         4524 my $re = qr/\Q$tok\E/;
271 567         978 warn "pushing $re to list\n" if DEBUG;
272 567         1445 push @res, $re;
273             }
274             }
275              
276 195         305 warn "returning @res\n" if DEBUG;
277 195         544 return @res;
278             }
279              
280 190     190   477 fun _get_mday ($struct, :$locale) {
  190         348  
  190         309  
  190         316  
281 190         1080 my $mday = $struct->{'d'};
282 190 100       462 if (not defined $mday) { $mday = $struct->{'e'}; }
  175         310  
283 190 50 66     812 if (not defined $mday and defined(my $Od = $struct->{Od})) {
284 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
285 0         0 $mday = _get_index($Od, @d);
286             }
287 190 50 66     721 if (not defined $mday and defined(my $Oe = $struct->{Oe})) {
288 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
289 0         0 $mday = _get_index($Oe, @d);
290             }
291              
292 190         347 return $mday;
293             }
294              
295 190     190   423 fun _get_year ($struct, :$locale) {
  190         367  
  190         307  
  190         286  
296 190         297 my $wyear = 0;
297 190         357 my $year = $struct->{'Y'};
298 190 100       491 if (not defined $year) {
299 92 100       232 if (defined $struct->{'G'}) {
    50          
    50          
    50          
300 82         135 $year = $struct->{'G'};
301 82         142 $wyear = 1;
302             } elsif (defined $struct->{'C'}) {
303 0         0 $year = $struct->{'C'} * 100;
304 0 0       0 $year += $struct->{'y'} if defined $struct->{'y'};
305 0 0 0     0 if (defined $struct->{'g'} and not defined $struct->{'y'}) {
306 0         0 $year += $struct->{'g'};
307 0         0 $wyear = 1;
308             }
309             } elsif (defined $struct->{'y'}) {
310 0         0 $year = $struct->{'y'} + 1900;
311 0         0 require Time::C;
312 0 0       0 if ($year < (Time::C->now_utc()->year - 50)) { $year += 100; }
  0         0  
313             } elsif (defined $struct->{'g'}) {
314 0         0 $year = $struct->{'g'} + 1900;
315 0         0 require Time::C;
316 0 0       0 if ($year < (Time::C->now_utc()->year - 50)) { $year += 100; }
  0         0  
317 0         0 $wyear = 1;
318             }
319             }
320 190 100       446 if (not defined $year) {
321 10         20 my $Ey = $struct->{Ey};
322 10         18 my $EC = $struct->{EC};
323              
324 10 100       38 if (defined $EC) {
    50          
325 1         4 my @eras = @{ get_locale(era => $locale) };
  1         6  
326 1         4 foreach my $era (@eras) {
327 1         8 my @fields = split /:/, $era;
328 1 50       5 next if $EC ne $fields[4];
329              
330 1         7 my %s = strptime($fields[2], "%-Y/%m/%d");
331 1 50       6 $s{year}++ if $s{year} < 1;
332 1         3 $year = $s{year};
333 1 50       6 $year += $Ey - $fields[1] if defined $Ey;
334 1 50       3 $year-- if not defined $Ey;
335 1         4 last;
336             }
337             } elsif (defined $Ey) {
338 0         0 my @eras = @{ get_locale(era => $locale) };
  0         0  
339 0         0 foreach my $era (@eras) {
340 0         0 my @fields = split /:/, $era;
341              
342 0         0 my %s = strptime($fields[2], "%-Y/%m/%d");
343 0 0       0 $s{year}++ if $s{year} < 1;
344 0         0 require Time::C;
345 0 0       0 next if $s{year} > Time::C->now_utc()->year;
346              
347 0         0 $year = $s{year} + $Ey - $fields[1];
348 0         0 last;
349             }
350             }
351             }
352 190 50 66     541 if (not defined $year and defined(my $Oy = $struct->{Oy})) {
353 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
354 0 0       0 if (defined(my $C = $struct->{C})) {
    0          
355 0         0 $year = $C * 100;
356             } elsif (defined(my $OC = $struct->{OC})) {
357 0         0 $year = _get_index($OC, @d) * 100;
358             } else {
359 0         0 $year = 1900;
360             }
361 0         0 $year += _get_index($Oy, @d);
362 0 0       0 if (not defined $struct->{C}) {
363 0         0 require Time::C;
364 0 0       0 if ($year < (Time::C->now_utc()->year - 50)) { $year += 100; }
  0         0  
365             }
366             }
367              
368 190         544 return ($year, $wyear);
369             }
370              
371 190     190   417 fun _get_wday($struct, :$locale) {
  190         306  
  190         325  
  190         258  
372 190   33     546 my $wday = $struct->{'u'} // $struct->{'w'};
373              
374 190 100       422 if (not defined $wday) {
375 28 50       107 if (defined $struct->{'A'}) {
    100          
376 0         0 $wday = _get_index($struct->{'A'}, @{ get_locale(weekdays => $locale) });
  0         0  
377             } elsif (defined $struct->{'a'}) {
378 8         22 $wday = _get_index($struct->{'a'}, @{ get_locale(weekdays_abbr => $locale) });
  8         29  
379             }
380             }
381 190 100       396 if (not defined $wday) {
382 20 50       74 if (defined(my $Ou = $struct->{Ou})) {
    50          
383 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
384 0         0 $wday = _get_index($Ou, @d);
385             } elsif (defined(my $Ow = $struct->{Ow})) {
386 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
387 0         0 $wday = _get_index($Ow, @d);
388             }
389             }
390 190 100 100     780 $wday = 7 if defined $wday and $wday == 0;
391              
392 190         403 return $wday;
393             }
394              
395 190     190   412 fun _get_u_week ($struct, :$locale) {
  190         352  
  190         334  
  190         268  
396 190         315 my $u_week = $struct->{U};
397              
398 190 50 33     775 if (not defined $u_week and defined(my $OU = $struct->{OU})) {
399 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
400 0         0 $u_week = _get_index($OU, @d);
401             }
402              
403 190         333 return $u_week;
404             }
405              
406 190     190   387 fun _get_w_week ($struct, :$locale) {
  190         349  
  190         313  
  190         272  
407 190         322 my $w_week = $struct->{W};
408              
409 190 50 66     669 if (not defined $w_week and defined(my $OW = $struct->{OW})) {
410 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
411 0         0 $w_week = _get_index($OW, @d);
412             }
413              
414 190         370 return $w_week;
415             }
416              
417 190     190   426 fun _get_v_week ($struct, :$locale) {
  190         324  
  190         312  
  190         261  
418 190         305 my $v_week = $struct->{V};
419              
420 190 50 66     706 if (not defined $v_week and defined(my $OV = $struct->{OV})) {
421 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
422 0         0 $v_week = _get_index($OV, @d);
423             }
424              
425 190         371 return $v_week;
426             }
427              
428 190     190   408 fun _get_month ($struct, :$locale) {
  190         393  
  190         311  
  190         275  
429 190         340 my $month = $struct->{'m'};
430 190 100       454 if (not defined $month) {
431 179 50       606 if (defined $struct->{'B'}) {
    100          
432 0         0 $month = _get_index($struct->{'B'}, @{ get_locale(months => $locale) }) + 1;
  0         0  
433             } elsif (defined $struct->{'b'}) {
434 6         15 $month = _get_index($struct->{'b'}, @{ get_locale(months_abbr => $locale) }) + 1;
  6         24  
435             }
436             }
437 190 50 66     1110 if (not defined $month and defined(my $Om = $struct->{Om})) {
438 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
439 0         0 $month = _get_index($Om, @d);
440             }
441              
442 190         380 return $month;
443             }
444              
445 190     190   474 fun _get_hour ($struct, :$locale) {
  190         338  
  190         315  
  190         263  
446 190         347 my $hour = $struct->{'H'};
447 190 100       443 if (not defined $hour) { $hour = $struct->{'k'}; }
  179         293  
448 190 100       406 if (not defined $hour) {
449 179   33     540 $hour = $struct->{'I'} // $struct->{'l'};
450 179 50 33     469 if (defined $hour and length $struct->{'p'}) {
451 0 0       0 if (_get_index($struct->{'p'}, @{ get_locale(am_pm => $locale) })) {
  0         0  
452             # PM
453 0 0       0 if ($hour < 12) { $hour += 12; }
  0         0  
454             } else {
455             # AM
456 0 0       0 if ($hour == 12) { $hour = 0; }
  0         0  
457             }
458             }
459             }
460              
461 190 50 66     1293 if (not defined $hour and defined(my $OH = $struct->{OH})) {
    50 66        
462 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
463 0         0 $hour = _get_index($OH, @d);
464             } elsif (not defined $hour and defined(my $OI = $struct->{OI})) {
465 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
466 0         0 $hour = _get_index($OI, @d);
467 0 0       0 if (length $struct->{p}) {
468 0 0       0 if (_get_index($struct->{p}, @{ get_locale(am_pm => $locale) })) {
  0         0  
469             # PM
470 0 0       0 if ($hour < 12) { $hour += 12; }
  0         0  
471             } else {
472             # AM
473 0 0       0 if ($hour == 12) { $hour = 0; }
  0         0  
474             }
475             }
476             }
477              
478 190         393 return $hour;
479             }
480              
481 190     190   399 fun _get_minute ($struct, :$locale) {
  190         320  
  190         312  
  190         313  
482 190         297 my $min = $struct->{'M'};
483              
484 190 50 66     789 if (not defined $min and defined(my $OM = $struct->{OM})) {
485 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
486 0         0 $min = _get_index($OM, @d);
487             }
488              
489 190         355 return $min;
490             }
491              
492 190     190   447 fun _get_second ($struct, :$locale) {
  190         330  
  190         324  
  190         250  
493 190         305 my $sec = $struct->{'S'};
494              
495 190 50 66     767 if (not defined $sec and defined(my $OS = $struct->{OS})) {
496 0         0 my @d = @{ get_locale(digits => $locale) };
  0         0  
497 0         0 $sec = _get_index($OS, @d);
498             }
499              
500 190         327 return $sec;
501             }
502              
503 190     190   547 fun _coerce_struct ($struct, $orig, :$locale) {
  190         377  
  190         385  
  190         288  
504             # First, if we know the epoch, great
505 190         333 my $epoch = $struct->{'s'};
506              
507             # Then set up as many date bits we know about
508             # year + day of year
509             # year + month + day of month
510             # year + week + day of week
511              
512 190         484 my ($year, $wyear) = _get_year($struct, locale => $locale);
513              
514 190         382 my $yday = $struct->{'j'};
515              
516 190         458 my $month = _get_month($struct, locale => $locale);
517              
518 190         440 my $mday = _get_mday($struct, locale => $locale);
519              
520 190         458 my $u_week = _get_u_week($struct, locale => $locale);
521 190         418 my $w_week = _get_w_week($struct, locale => $locale);
522 190         416 my $v_week = _get_v_week($struct, locale => $locale);
523              
524 190         430 my $wday = _get_wday($struct, locale => $locale);
525              
526 190 50 66     681 if (not defined $w_week and defined $u_week) {
527 0         0 $w_week = $u_week;
528 0 0       0 if (not defined $wday) { $wday = 7; } # if no wday defined, should set to first day of week, and since the u_week starts at sunday, wday = 7
  0         0  
529 0 0       0 $w_week-- if $wday == 7;
530             }
531              
532 190 100 100     673 if (not defined $v_week and defined $w_week) {
533 82 50       185 if ($wyear) { croak "Can't strptime a %G/%g year with a %W/%U week"; }
  0         0  
534              
535 82         454 require Time::C;
536 82   33     415 my $t = Time::C->new($year // $orig->{year} // Time::C->now_utc->year);
      0        
537 82         179 $v_week = $w_week;
538 82 100 100     224 if (($t->day_of_week > 1) and ($t->day_of_week < 5)) { $v_week++; }
  36         74  
539             }
540              
541 190 100 66     2071 if ($wyear and defined $v_week and $v_week > 1) {
    100 100        
    50 100        
      66        
      66        
      33        
542 54         292 require Time::C;
543 54         220 $year = Time::C->mktime(year => $year, week => $v_week)->year;
544             } elsif (defined $v_week and $v_week > 1 and defined $year) {
545 55         279 require Time::C;
546 55 50       221 if (Time::C->mktime(year => $year, week => $v_week)->year == $year + 1) {
547 0 0       0 $year-- if not defined $month;
548             }
549             } elsif (defined $v_week and $v_week > 1 and defined $orig->{year}) {
550 0         0 require Time::C;
551 0 0       0 if (Time::C->mktime(year => $orig->{year}, week => $v_week)->year == $orig->{year} + 1) {
552 0 0       0 $year = $orig->{year} - 1 if not defined $month;
553             }
554             }
555              
556             # Next try to set up time bits -- these are pretty easy in comparison
557              
558 190         907 my $hour = _get_hour($struct, locale => $locale);
559              
560 190         406 my $min = _get_minute($struct, locale => $locale);
561              
562 190         392 my $sec = _get_second($struct, locale => $locale);
563              
564             # And last see if we have some timezone or at least offset info
565              
566 190         365 my $tz = $struct->{'Z'}; # should verify that it's a useful tz
567 190 100       406 if (defined $tz) {
568 1         8 require Time::C;
569 1 50       3 undef $tz if not defined eval { Time::C->now($tz); };
  1         8  
570             }
571              
572 190         290 my $offset = $struct->{'z'};
573              
574 190 50       378 my $offset_n = defined $offset ? _offset_to_minutes($offset) : undef;
575              
576 190         343 my %struct = ();
577              
578 190 100       420 $struct{second} = $sec if defined $sec;
579 190 100       422 $struct{minute} = $min if defined $min;
580 190 100       411 $struct{hour} = $hour if defined $hour;
581 190 100       416 $struct{mday} = $mday if defined $mday;
582 190 100       364 $struct{month} = $month if defined $month;
583 190 100       520 $struct{week} = $v_week if defined $v_week;
584 190 100       473 $struct{wday} = $wday if defined $wday;
585 190 50       384 $struct{yday} = $yday if defined $yday;
586 190 100       434 $struct{year} = $year if defined $year;
587 190 50       377 $struct{epoch} = $epoch if defined $epoch;
588 190 100       379 $struct{tz} = $tz if defined $tz;
589 190 50       387 $struct{offset} = $offset_n if defined $offset_n;
590              
591 190         951 return %struct;
592             }
593              
594 0     0   0 fun _offset_to_minutes ($offset) {
  0         0  
595 0         0 my ($sign, $hours, $minutes) = $offset =~ m/^([+-])([0-9][0-9]):?([0-9][0-9])?$/;
596 0 0       0 return $sign eq '+' ? ($hours * 60 + $minutes) : -($hours * 60 + $minutes);
597             }
598              
599 14     14   62 fun _get_index ($needle, @haystack) {
  14         27  
600 14 50 33     53 if (not @haystack and $needle eq '') { return 0; }
  0         0  
601              
602 14         48 foreach my $i (0 .. $#haystack) {
603 71 100       184 return $i if $haystack[$i] eq $needle;
604             }
605 0         0 croak "Could not find $needle in the list.";
606             }
607              
608 3     3   9 fun _get_eras ($type, $locale) {
  3         6  
609 3         6 my @eras = @{ get_locale(era => $locale) };
  3         9  
610 3 100       7 my @ret = map { my @fields = split /:/; $type eq 'period' ? $fields[4] : $fields[5] } @eras;
  27         85  
  27         70  
611              
612 3         16 return @ret;
613             }
614              
615             1;
616              
617             __END__