File Coverage

blib/lib/DateTime/Ordinal.pm
Criterion Covered Total %
statement 64 111 57.6
branch 43 74 58.1
condition 6 8 75.0
subroutine 14 16 87.5
pod 2 4 50.0
total 129 213 60.5


line stmt bran cond sub pod time code
1             package DateTime::Ordinal;
2              
3 5     5   424985 use 5.006;
  5         49  
4 5     5   23 use strict;
  5         7  
  5         84  
5 5     5   19 use warnings;
  5         7  
  5         103  
6              
7 5     5   20 use base 'DateTime';
  5         8  
  5         3815  
8              
9             our $VERSION = '0.03';
10              
11             our (%strftime_patterns, %sub_format, @ORDINALS, @NTT);
12             BEGIN {
13 5     5   25 for my $sub (qw/
14             month
15             mon
16             month_0
17             mon_0
18             day_of_month
19             day
20             mday
21             weekday_of_month
22             quarter
23             day_of_month_0
24             day_0
25             mday_0
26             day_of_week
27             wday
28             dow
29             day_of_week_0
30             wday_0
31             dow_0
32             local_day_of_week
33             day_of_quarter
34             doq
35             day_of_quarter_0
36             doq_0
37             day_of_year
38             doy
39             day_of_year_0
40             doy_0
41             hour
42             hour_1
43             hour_12
44             hour_12_0
45             minute
46             min
47             second
48             sec
49             nanosecond
50             millisecond
51             microsecond
52             leap_seconds
53             week
54             year
55             week_year
56             week_number
57             week_of_month
58             /) {
59 5     5   2111761 no strict 'refs';
  5         10  
  5         6322  
60 220         679 *{"${sub}"} = sub {
61 835     835   137377 _sub($sub, @_);
62 220         535 };
63             }
64             %strftime_patterns = (
65 0         0 'a' => sub { $_[0]->day_abbr },
66 3         11 'A' => sub { $_[0]->day_name },
67 0         0 'b' => sub { $_[0]->month_abbr },
68 3         10 'B' => sub { $_[0]->month_name },
69             'c' => sub {
70 0         0 $_[0]->format_cldr( $_[0]->{locale}->datetime_format_default() );
71             },
72             'C' => sub {
73 0         0 my $y = int( $_[0]->year / 100 );
74 0 0       0 return $_[1] ? _cardinal($y) : $y;
75             },
76 8 100       28 'd' => sub { $_[1] ? $_[0]->day_of_month($_[1]) : sprintf( '%02d', $_[0]->day_of_month ) },
77 0         0 'D' => sub { $_[0]->strftime('%m/%d/%y') },
78 0 0       0 'e' => sub { $_[1] ? $_[0]->day_of_month($_[1]) : sprintf( '%2d', $_[0]->day_of_month ) },
79 0         0 'F' => sub { $_[0]->strftime('%Y-%m-%d') },
80             'g' => sub {
81 0         0 my $w = substr( $_[0]->week_year, -2 );
82 0 0       0 return $_[1] ? _cardinal($w) : $w;
83             },
84 0         0 'G' => sub { $_[0]->week_year($_[1]) },
85 3 50       11 'H' => sub { $_[1] ? $_[0]->hour($_[1]) : sprintf( '%02d', $_[0]->hour ) },
86 0 0       0 'I' => sub { $_[1] ? $_[0]->hour_12($_[1]) : sprintf( '%02d', $_[0]->hour_12 ) },
87 2 50       8 'j' => sub { $_[1] ? $_[0]->day_of_year($_[1]) : sprintf( '%03d', $_[0]->day_of_year ) },
88 0 0       0 'k' => sub { $_[1] ? $_[0]->hour($_[1]) : sprintf( '%2d', $_[0]->hour ) },
89 0 0       0 'l' => sub { $_[1] ? $_[0]->hour_12($_[1]) : sprintf( '%2d', $_[0]->hour_12 ) },
90 6 50       18 'm' => sub { $_[1] ? $_[0]->month($_[1]) : sprintf( '%02d', $_[0]->month ) },
91 3 50       12 'M' => sub { $_[1] ? $_[0]->minute($_[1]) : sprintf( '%02d', $_[0]->minute ) },
92 0         0 'n' => sub {"\n"}, # should this be OS-sensitive?
93             'N' => \&_format_nanosecs,
94 0         0 'p' => sub { $_[0]->am_or_pm() },
95 0         0 'P' => sub { lc $_[0]->am_or_pm() },
96 0         0 'r' => sub { $_[0]->strftime('%I:%M:%S %p') },
97 0         0 'R' => sub { $_[0]->strftime('%H:%M') },
98 0         0 's' => sub { $_[0]->epoch },
99 0         0 'S' => sub { sprintf( '%02d', $_[0]->second ) },
100 0         0 't' => sub {"\t"},
101 0         0 'T' => sub { $_[0]->strftime('%H:%M:%S') },
102 0         0 'u' => sub { $_[0]->day_of_week($_[1]) },
103             'U' => sub {
104 0         0 my $sun = $_[0]->day_of_year - ( $_[0]->day_of_week + 7 ) % 7;
105 0         0 return sprintf( '%02d', int( ( $sun + 6 ) / 7 ) );
106             },
107 0         0 'V' => sub { sprintf( '%02d', $_[0]->week_number ) },
108             'w' => sub {
109 0         0 my $dow = $_[0]->day_of_week;
110 0         0 my $w = $dow % 7;
111 0 0       0 return $_[1] ? _ordinal($w) : $w;
112             },
113             'W' => sub {
114 0         0 my $mon = $_[0]->day_of_year - ( $_[0]->day_of_week + 6 ) % 7;
115 0         0 return sprintf( '%02d', int( ( $mon + 6 ) / 7 ) );
116             },
117             'x' => sub {
118 0         0 $_[0]->format_cldr( $_[0]->{locale}->date_format_default() );
119             },
120             'X' => sub {
121 0         0 $_[0]->format_cldr( $_[0]->{locale}->time_format_default() );
122             },
123             'y' => sub {
124 0         0 my $y = sprintf( '%02d', substr( $_[0]->year, -2 ) );
125 0 0       0 return $_[1] ? _ordinal($y) : $y;
126             },
127 7         15 'Y' => sub { $_[0]->year($_[1]) },
128 0         0 'z' => sub { DateTime::TimeZone->offset_as_string( $_[0]->offset ) },
129 0         0 'Z' => sub { $_[0]->{tz}->short_name_for_datetime( $_[0] ) },
130 0         0 '%' => sub {'%'},
131 5         368 );
132 5         20 $strftime_patterns{h} = $strftime_patterns{b};
133 5         15 @ORDINALS = qw/th st nd rd/;
134 5         88 @NTT = (
135             [
136             '',
137             ['one', 'first'],
138             ['two', 'second'],
139             ['three', 'third'],
140             ['four', 'fourth'],
141             ['five', 'fifth'],
142             ['six', 'sixth'],
143             ['seven', 'seventh'],
144             ['eight', 'eighth'],
145             ['nine', 'ninth'],
146             ['ten', 'tenth'],
147             ['eleven', 'eleventh'],
148             ['twelve', 'twelfth'],
149             ['thirteen', 'thirteenth'],
150             ['fourteen', 'fourteenth'],
151             ['fifthteen', 'fifthteenth'],
152             ['sixteen', 'sixteenth'],
153             ['seventeen', 'seventeenth'],
154             ['eighteen', 'eighteenth'],
155             ['nineteen', 'nineteenth'],
156             ],
157             [
158             '',
159             '',
160             ['twenty', 'twentieth'],
161             ['thirty', 'thirtieth'],
162             ['forty', 'fortieth'],
163             ['fifty', 'fiftieth'],
164             ['sixty', 'sixtieth'],
165             ['seventy', 'seventieth'],
166             ['eighty', 'eightieth'],
167             ['ninety', 'nintieth']
168             ],
169             'hundred',
170             'thousand',
171             'million',
172             'billion',
173             'trillion',
174             'quadrillion',
175             'quintillion',
176             'sextillion',
177             'septillion',
178             'octillion'
179             );
180             %sub_format = (
181 168         312 f => sub { _num2text(shift) },
182 172         305 o => sub { _ordinal(shift) },
183 171         338 of => sub { _num2text(shift, 1) },
184 0         0 oe => sub { _ordinal(shift, 1) }
185 5         4251 );
186             }
187              
188 16     16 0 3407 sub quarter_0 { _sub('quarter_0', $_[0], $_[1], 4); }
189              
190             sub import {
191 5     5   64 my ($package, %args) = @_;
192 5 50       9313 set_sub_format($args{sub_format}) if ($args{sub_format});
193             }
194              
195 0     0 0 0 sub set_sub_format { %sub_format = (%sub_format, %{$_[0]}); }
  0         0  
196              
197             sub strftime {
198 20     20 1 16969 my $self = shift;
199              
200             # make a copy or caller's scalars get munged
201 20         37 my @patterns = @_;
202 20         25 my @r;
203 20         33 foreach my $p (@patterns) {
204 20         148 $p =~ s/
205             (?:
206             %\{(\w+)\}(?:\s*\(([\w\d]+)\))* # method name like %{day_name}
207             |
208             %([%a-zA-Z])(?:\s*\(([\w\d]+)\))* # single character specifier like %d
209             |
210             %(\d+)N # special case for %N
211             )
212             /
213             ( $1
214             ? ( $self->can($1) ? $self->$1($2) : "\%{$1}" )
215             : $3
216             ? ( $strftime_patterns{$3} ? $strftime_patterns{$3}->($self, $4) : "\%$2" )
217             : $5
218 39 50       198 ? $strftime_patterns{N}->($self, $5)
    50          
    0          
    50          
    100          
219             : '' # this won't happen
220             )
221             /sgex;
222 20 50       111 return $p unless wantarray;
223 0         0 push @r, $p;
224             }
225              
226 0         0 return @r;
227             }
228              
229             sub strptime {
230 0     0 1 0 require DateTime::Format::Strptime;
231             DateTime::Ordinal->from_object(object => DateTime::Format::Strptime->new(
232             on_error => 'croak',
233             pattern => $_[1],
234 0 0       0 ($_[3] ? %{$_[3]} : ())
  0         0  
235             )->parse_datetime($_[2]));
236             }
237              
238             sub _sub {
239 851     851   1543 my ($orig, $self, $ordinal, $default) = @_;
240 851         1440 $orig = "SUPER::$orig";
241 851   100     2447 my $val = $self->$orig || $default || 0;
242 851 100 66     7215 return $sub_format{$ordinal || ''} && $val ? $sub_format{$ordinal}->($val, $self) : $val;
243             }
244              
245             sub _ordinal {
246 178 100   178   6614 return $_[1] ? '' : $_[0] . $ORDINALS[$_[0] =~ m{(?<!1)([123])$} ? $1 : 0];
    50          
247             }
248              
249             sub _num2text {
250 375 100   375   17470 my ($ns, $l, $o, @n2t) = ('', 3, ($_[1] ? -1 : 0), reverse(split('', $_[0])));
251             my $hundred = sub {
252 406     406   951 my ($string, $ord, @trip) = ('', @_, 0, 0, 0);
253 406 100 66     1099 if ($trip[1] > 1) {
    100          
254 24         36 for (0, 1) {
255 48 100       206 $string = sprintf(
    100          
    100          
256             "%s%s",
257             $NTT[$_][$trip[$_]][$string ? 0 : $ord],
258             ($string ? '-' . $string : '')
259             ) if $trip[$_];
260             }
261 366         1047 } elsif ($trip[0] || $trip[1]) { $string = $NTT[0][$trip[1] . $trip[0]][$ord]; }
262 406 50       724 $string = sprintf(
    100          
    100          
263             "%s %s%s",
264             $NTT[0][$trip[2]][0],
265             $NTT[2],
266             ($string ? ' and ' . $string : ($ord != 0 ? 'th' : ''))
267             ) if $trip[2];
268 406         826 return $string;
269 375         1692 };
270 375         836 $ns = $hundred->($o, splice(@n2t, 0, 3));
271 375         736 while (@n2t) {
272 31         52 my $h = $hundred->(0, splice(@n2t, 0, 3));
273 31 100       136 $ns = sprintf(
    100          
    100          
    100          
274             "%s %s%s",
275             $h,
276             $NTT[$l],
277             ($ns
278             ? ($ns =~ m/and/
279             ? ', '
280             : ' and '
281             ) . $ns
282             : ($o == 0
283             ? ''
284             :'th'
285             )
286             )
287             ) if $h;
288 31         60 $l++;
289             }
290 375         2906 return $ns;
291             }
292              
293             1; # End of DateTime::Ordinal
294              
295             __END__
296              
297             =head1 NAME
298              
299             DateTime::Ordinal - The great new DateTime::Ordinal!
300              
301             =head1 VERSION
302              
303             Version 0.03
304              
305             =cut
306              
307             =head1 SYNOPSIS
308              
309             Quick summary of what the module does.
310              
311             use DateTime::Ordinal;
312              
313             my $dt = DateTime::Ordinal->new(
314             year => 3000,
315             month => 4,
316             day => 1,
317             hour => 2,
318             minute => 3,
319             second => 4,
320             );
321              
322             $dt->day # 1
323             $dt->day('o') # 1st
324             $dt->day('f') # one
325             $dt->day('of') # first
326              
327             $dt->hour # 2
328             $dt->hour('o') # 2nd
329             $dt->hour('f') # two
330             $dt->hour('of') # second
331              
332             $dt->minute # 3
333             $dt->minute('o') # 3rd
334             $dt->minute('f') # three
335             $dt->minute('of') # third
336              
337             $dt->second # 4
338             $dt->second('o') # 4th
339             $dt->second('f') # four
340             $dt->second('of') # fourth
341              
342             $dt->strftime("It's the %M(of) minute of the %H(o) hour on day %d(f) in the %m(of) month within the year %Y(f)");
343             # "It's the third minute of the 2nd hour on day one in the fourth month within the year three thousand");
344              
345             ...
346              
347             use Lingua::ITA::Numbers
348             use DateTime::Ordinal (
349             sub_format => {
350             f => sub {
351             my $number = Lingua::ITA::Numbers->new(shift);
352             return $number->get_string;
353             }
354             }
355             );
356              
357             my $dt = DateTime::Ordinal->new(
358             hour => 1,
359             minute => 2,
360             second => 3,
361             locale => 'it'
362             );
363              
364             $dt->hour('f') # uno
365             $dt->minute('f') # due
366             $dt->second('f') # tre
367              
368             =head1 SUBROUTINES/METHODS
369              
370             =cut
371              
372             =head2 strftime
373              
374             =cut
375              
376             =head2 month
377              
378             =cut
379              
380             =head2 mon
381              
382             =cut
383              
384             =head2 month_0
385              
386             =cut
387              
388             =head2 mon_0
389              
390             =cut
391              
392             =head2 day_of_month
393              
394             =cut
395              
396             =head2 day
397              
398             =cut
399              
400             =head2 mday
401              
402             =cut
403              
404             =head2 weekday_of_month
405              
406             =cut
407              
408             =head2 quarter
409              
410             =cut
411              
412             =head2 day_of_month_0
413              
414             =cut
415              
416             =head2 day_0
417              
418             =cut
419              
420             =head2 mday_0
421              
422             =cut
423              
424             =head2 day_of_week
425              
426             =cut
427              
428             =head2 wday
429              
430             =cut
431              
432             =head2 dow
433              
434             =cut
435              
436             =head2 day_of_week_0
437              
438             =cut
439              
440             =head2 wday_0
441              
442             =cut
443              
444             =head2 dow_0
445              
446             =cut
447              
448             =head2 local_day_of_week
449              
450             =cut
451              
452             =head2 day_of_quarter
453              
454             =cut
455              
456             =head2 doq
457              
458             =cut
459              
460             =head2 day_of_quarter_0
461              
462             =cut
463              
464             =head2 doq_0
465              
466             =cut
467              
468             =head2 day_of_year
469              
470             =cut
471              
472             =head2 doy
473              
474             =cut
475              
476             =head2 day_of_year_0
477              
478             =cut
479              
480             =head2 doy_0
481              
482             =cut
483              
484             =head2 hour
485              
486             =cut
487              
488             =head2 hour_1
489              
490             =cut
491              
492             =head2 hour_12
493              
494             =cut
495              
496             =head2 hour_12_0
497              
498             =cut
499              
500             =head2 minute
501              
502             =cut
503              
504             =head2 min
505              
506             =cut
507              
508             =head2 second
509              
510             =cut
511              
512             =head2 sec
513              
514             =cut
515              
516             =head2 nanosecond
517              
518             =cut
519              
520             =head2 millisecond
521              
522             =cut
523              
524             =head2 microsecond
525              
526             =cut
527              
528             =head2 leap_seconds
529              
530             =cut
531              
532             =head2 week
533              
534             =cut
535              
536             =head2 year
537              
538             =cut
539              
540             =head2 week_year
541              
542             =cut
543              
544             =head2 week_number
545              
546             =cut
547              
548             =head2 week_of_month
549              
550             =cut
551              
552             =head2 strptime
553              
554             =cut
555              
556             =head1 AUTHOR
557              
558             LNATION, C<< <thisusedtobeanemail at gmail.com> >>
559              
560             =head1 BUGS
561              
562             Please report any bugs or feature requests to C<bug-datetime-ordinal at rt.cpan.org>, or through
563             the web interface at L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=DateTime-Ordinal>. I will be notified, and then you'll
564             automatically be notified of progress on your bug as I make changes.
565              
566             =head1 SUPPORT
567              
568             You can find documentation for this module with the perldoc command.
569              
570             perldoc DateTime::Ordinal
571              
572              
573             You can also look for information at:
574              
575             =over 4
576              
577             =item * RT: CPAN's request tracker (report bugs here)
578              
579             L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=DateTime-Ordinal>
580              
581             =item * AnnoCPAN: Annotated CPAN documentation
582              
583             L<http://annocpan.org/dist/DateTime-Ordinal>
584              
585             =item * CPAN Ratings
586              
587             L<https://cpanratings.perl.org/d/DateTime-Ordinal>
588              
589             =item * Search CPAN
590              
591             L<https://metacpan.org/release/DateTime-Ordinal>
592              
593             =back
594              
595              
596             =head1 ACKNOWLEDGEMENTS
597              
598              
599             =head1 LICENSE AND COPYRIGHT
600              
601             This software is Copyright (c) 2019 by LNATION.
602              
603             This is free software, licensed under:
604              
605             The Artistic License 2.0 (GPL Compatible)
606              
607              
608             =cut
609