File Coverage

blib/lib/DateTime/Ordinal.pm
Criterion Covered Total %
statement 111 118 94.0
branch 65 76 85.5
condition 6 8 75.0
subroutine 16 18 88.8
pod 2 4 50.0
total 200 224 89.2


line stmt bran cond sub pod time code
1             package DateTime::Ordinal;
2              
3 7     7   879582 use 5.006;
  7         26  
4 7     7   40 use strict;
  7         15  
  7         235  
5 7     7   32 use warnings;
  7         22  
  7         393  
6              
7 7     7   4390 use parent 'DateTime';
  7         2364  
  7         48  
8 7     7   4400575 use POSIX qw( floor );
  7         17  
  7         68  
9             our $VERSION = '0.07';
10              
11             our (%strftime_patterns, %sub_format, @ORDINALS, @NTT);
12             BEGIN {
13 7     7   37 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 7     7   1501 no strict 'refs';
  7         39  
  7         11940  
60 308         1005 *{"${sub}"} = sub {
61 931     931   538940 _sub($sub, @_);
62 308         780 };
63             }
64             %strftime_patterns = (
65 1         6 'a' => sub { $_[0]->day_abbr },
66 4         27 'A' => sub { $_[0]->day_name },
67 1         12 'b' => sub { $_[0]->month_abbr },
68 4         24 'B' => sub { $_[0]->month_name },
69             'c' => sub {
70 1         9 $_[0]->format_cldr( $_[0]->{locale}->datetime_format_default() );
71             },
72             'C' => sub {
73 2         7 my $y = int( $_[0]->year / 100 );
74 2 100       12 return $_[1] ? _ordinal($y) : $y;
75             },
76 11 100       84 'd' => sub { $_[1] ? $_[0]->day_of_month($_[1]) : sprintf( '%02d', $_[0]->day_of_month ) },
77 1         5 'D' => sub { $_[0]->strftime('%m/%d/%y') },
78 2 100       10 'e' => sub { $_[1] ? $_[0]->day_of_month($_[1]) : sprintf( '%2d', $_[0]->day_of_month ) },
79 1         6 'F' => sub { $_[0]->strftime('%Y-%m-%d') },
80             'g' => sub {
81 2         7 my $w = substr( $_[0]->week_year, -2 );
82 2 100       9 return $_[1] ? _ordinal($w) : $w;
83             },
84 2         6 'G' => sub { $_[0]->week_year($_[1]) },
85 7 100       38 'H' => sub { $_[1] ? $_[0]->hour($_[1]) : sprintf( '%02d', $_[0]->hour ) },
86 3 100       13 'I' => sub { $_[1] ? $_[0]->hour_12($_[1]) : sprintf( '%02d', $_[0]->hour_12 ) },
87 4 100       26 'j' => sub { $_[1] ? $_[0]->day_of_year($_[1]) : sprintf( '%03d', $_[0]->day_of_year ) },
88 2 100       12 'k' => sub { $_[1] ? $_[0]->hour($_[1]) : sprintf( '%2d', $_[0]->hour ) },
89 2 100       10 'l' => sub { $_[1] ? $_[0]->hour_12($_[1]) : sprintf( '%2d', $_[0]->hour_12 ) },
90 10 100       56 'm' => sub { $_[1] ? $_[0]->month($_[1]) : sprintf( '%02d', $_[0]->month ) },
91 8 100       43 'M' => sub { $_[1] ? $_[0]->minute($_[1]) : sprintf( '%02d', $_[0]->minute ) },
92 1         5 'n' => sub {"\n"}, # should this be OS-sensitive?
93             'N' => \&_format_nanosecs,
94 2         8 'p' => sub { $_[0]->am_or_pm() },
95 1         4 'P' => sub { lc $_[0]->am_or_pm() },
96 1         6 'r' => sub { $_[0]->strftime('%I:%M:%S %p') },
97 1         5 'R' => sub { $_[0]->strftime('%H:%M') },
98 1         13 's' => sub { $_[0]->epoch },
99 2         10 'S' => sub { sprintf( '%02d', $_[0]->second ) },
100 1         7 't' => sub {"\t"},
101 1         4 'T' => sub { $_[0]->strftime('%H:%M:%S') },
102 1         7 'u' => sub { $_[0]->day_of_week($_[1]) },
103             'U' => sub {
104 1         5 my $sun = $_[0]->day_of_year - ( $_[0]->day_of_week + 7 ) % 7;
105 1         10 return sprintf( '%02d', int( ( $sun + 6 ) / 7 ) );
106             },
107 1         5 'V' => sub { sprintf( '%02d', $_[0]->week_number ) },
108             'w' => sub {
109 2         7 my $dow = $_[0]->day_of_week;
110 2         6 my $w = $dow % 7;
111 2 100       24 return $_[1] ? _ordinal($w) : $w;
112             },
113             'W' => sub {
114 1         5 my $mon = $_[0]->day_of_year - ( $_[0]->day_of_week + 6 ) % 7;
115 1         10 return sprintf( '%02d', int( ( $mon + 6 ) / 7 ) );
116             },
117             'x' => sub {
118 1         12 $_[0]->format_cldr( $_[0]->{locale}->date_format_default() );
119             },
120             'X' => sub {
121 1         8 $_[0]->format_cldr( $_[0]->{locale}->time_format_default() );
122             },
123             'y' => sub {
124 3         10 my $y = sprintf( '%02d', substr( $_[0]->year, -2 ) );
125 3 100       16 return $_[1] ? _ordinal($y) : $y;
126             },
127 8         50 'Y' => sub { $_[0]->year($_[1]) },
128 1         8 'z' => sub { DateTime::TimeZone->offset_as_string( $_[0]->offset ) },
129 1         5 'Z' => sub { $_[0]->{tz}->short_name_for_datetime( $_[0] ) },
130 1         3 '%' => sub {'%'},
131 7         502 );
132 7         50 $strftime_patterns{h} = $strftime_patterns{b};
133 7         32 @ORDINALS = qw/th st nd rd/;
134 7         190 @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         512 f => sub { _num2text(shift) },
182 181         542 o => sub { _ordinal(shift) },
183 171         513 of => sub { _num2text(shift, 1) },
184 1         3 oe => sub { _ordinal(shift, 1) }
185 7         8509 );
186             }
187              
188 16     16 0 6738 sub quarter_0 { _sub('quarter_0', $_[0], $_[1], 4); }
189              
190             sub import {
191 7     7   86 my ($package, %args) = @_;
192 7 50       14421 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 78     78 1 608872 my $self = shift;
199              
200             # make a copy or caller's scalars get munged
201 78         198 my @patterns = @_;
202 78         125 my @r;
203 78         194 foreach my $p (@patterns) {
204 78         725 $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 107 50       858 ? $strftime_patterns{N}->($self, $5)
    50          
    0          
    50          
    100          
219             : '' # this won't happen
220             )
221             /sgex;
222 78 50       575 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 947     947   2818 my ($orig, $self, $ordinal, $default) = @_;
240 947         2989 $orig = "SUPER::$orig";
241 947   100     3810 my $val = $self->$orig || $default || 0;
242 947 100 66     12456 return $sub_format{$ordinal || ''} && $val ? $sub_format{$ordinal}->($val, $self) : $val;
243             }
244              
245             sub _ordinal {
246 192 100   192   179498 return ($_[1] ? '' : $_[0]) . $ORDINALS[$_[0] =~ m{(?<!1)([123])$} ? $1 : 0];
    100          
247             }
248            
249             sub _format_nanosecs {
250 1     1   3 my $self = shift;
251 1 50       6 my $precision = @_ ? shift : 9;
252            
253 1         8 my $divide_by = 10**( 9 - $precision );
254            
255             return sprintf(
256             '%0' . $precision . 'u',
257 1         16 floor( $self->{rd_nanosecs} / $divide_by )
258             );
259             }
260              
261             sub _num2text {
262 375 100   375   254138 my ($ns, $l, $o, @n2t) = ('', 3, ($_[1] ? -1 : 0), reverse(split('', $_[0])));
263             my $hundred = sub {
264 406     406   1434 my ($string, $ord, @trip) = ('', @_, 0, 0, 0);
265 406 100 66     1673 if ($trip[1] > 1) {
    100          
266 24         35 for (0, 1) {
267 48 100       205 $string = sprintf(
    100          
    100          
268             "%s%s",
269             $NTT[$_][$trip[$_]][$string ? 0 : $ord],
270             ($string ? '-' . $string : '')
271             ) if $trip[$_];
272             }
273 366         1788 } elsif ($trip[0] || $trip[1]) { $string = $NTT[0][$trip[1] . $trip[0]][$ord]; }
274 406 50       997 $string = sprintf(
    100          
    100          
275             "%s %s%s",
276             $NTT[0][$trip[2]][0],
277             $NTT[2],
278             ($string ? ' and ' . $string : ($ord != 0 ? 'th' : ''))
279             ) if $trip[2];
280 406         1163 return $string;
281 375         2577 };
282 375         1219 $ns = $hundred->($o, splice(@n2t, 0, 3));
283 375         1116 while (@n2t) {
284 31         57 my $h = $hundred->(0, splice(@n2t, 0, 3));
285 31 100       137 $ns = sprintf(
    100          
    100          
    100          
286             "%s %s%s",
287             $h,
288             $NTT[$l],
289             ($ns
290             ? ($ns =~ m/and/
291             ? ', '
292             : ' and '
293             ) . $ns
294             : ($o == 0
295             ? ''
296             :'th'
297             )
298             )
299             ) if $h;
300 31         50 $l++;
301             }
302 375         4588 return $ns;
303             }
304              
305             1; # End of DateTime::Ordinal
306              
307             __END__
308              
309             =head1 NAME
310              
311             DateTime::Ordinal - The great new DateTime::Ordinal!
312              
313             =head1 VERSION
314              
315             Version 0.07
316              
317             =cut
318              
319             =head1 SYNOPSIS
320              
321             Quick summary of what the module does.
322              
323             use DateTime::Ordinal;
324              
325             my $dt = DateTime::Ordinal->new(
326             year => 3000,
327             month => 4,
328             day => 1,
329             hour => 2,
330             minute => 3,
331             second => 4,
332             );
333              
334             $dt->day # 1
335             $dt->day('o') # 1st
336             $dt->day('f') # one
337             $dt->day('of') # first
338              
339             $dt->hour # 2
340             $dt->hour('o') # 2nd
341             $dt->hour('f') # two
342             $dt->hour('of') # second
343              
344             $dt->minute # 3
345             $dt->minute('o') # 3rd
346             $dt->minute('f') # three
347             $dt->minute('of') # third
348              
349             $dt->second # 4
350             $dt->second('o') # 4th
351             $dt->second('f') # four
352             $dt->second('of') # fourth
353              
354             $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)");
355             # "It's the third minute of the 2nd hour on day one in the fourth month within the year three thousand");
356              
357             ...
358              
359             use Lingua::ITA::Numbers
360             use DateTime::Ordinal (
361             sub_format => {
362             f => sub {
363             my $number = Lingua::ITA::Numbers->new(shift);
364             return $number->get_string;
365             }
366             }
367             );
368              
369             my $dt = DateTime::Ordinal->new(
370             hour => 1,
371             minute => 2,
372             second => 3,
373             locale => 'it'
374             );
375              
376             $dt->hour('f') # uno
377             $dt->minute('f') # due
378             $dt->second('f') # tre
379              
380             =head1 SUBROUTINES/METHODS
381              
382             =cut
383              
384             =head2 strftime
385              
386             =cut
387              
388             =head2 month
389              
390             =cut
391              
392             =head2 mon
393              
394             =cut
395              
396             =head2 month_0
397              
398             =cut
399              
400             =head2 mon_0
401              
402             =cut
403              
404             =head2 day_of_month
405              
406             =cut
407              
408             =head2 day
409              
410             =cut
411              
412             =head2 mday
413              
414             =cut
415              
416             =head2 weekday_of_month
417              
418             =cut
419              
420             =head2 quarter
421              
422             =cut
423              
424             =head2 day_of_month_0
425              
426             =cut
427              
428             =head2 day_0
429              
430             =cut
431              
432             =head2 mday_0
433              
434             =cut
435              
436             =head2 day_of_week
437              
438             =cut
439              
440             =head2 wday
441              
442             =cut
443              
444             =head2 dow
445              
446             =cut
447              
448             =head2 day_of_week_0
449              
450             =cut
451              
452             =head2 wday_0
453              
454             =cut
455              
456             =head2 dow_0
457              
458             =cut
459              
460             =head2 local_day_of_week
461              
462             =cut
463              
464             =head2 day_of_quarter
465              
466             =cut
467              
468             =head2 doq
469              
470             =cut
471              
472             =head2 day_of_quarter_0
473              
474             =cut
475              
476             =head2 doq_0
477              
478             =cut
479              
480             =head2 day_of_year
481              
482             =cut
483              
484             =head2 doy
485              
486             =cut
487              
488             =head2 day_of_year_0
489              
490             =cut
491              
492             =head2 doy_0
493              
494             =cut
495              
496             =head2 hour
497              
498             =cut
499              
500             =head2 hour_1
501              
502             =cut
503              
504             =head2 hour_12
505              
506             =cut
507              
508             =head2 hour_12_0
509              
510             =cut
511              
512             =head2 minute
513              
514             =cut
515              
516             =head2 min
517              
518             =cut
519              
520             =head2 second
521              
522             =cut
523              
524             =head2 sec
525              
526             =cut
527              
528             =head2 nanosecond
529              
530             =cut
531              
532             =head2 millisecond
533              
534             =cut
535              
536             =head2 microsecond
537              
538             =cut
539              
540             =head2 leap_seconds
541              
542             =cut
543              
544             =head2 week
545              
546             =cut
547              
548             =head2 year
549              
550             =cut
551              
552             =head2 week_year
553              
554             =cut
555              
556             =head2 week_number
557              
558             =cut
559              
560             =head2 week_of_month
561              
562             =cut
563              
564             =head2 strptime
565              
566             =cut
567              
568             =head1 AUTHOR
569              
570             LNATION, C<< <thisusedtobeanemail at gmail.com> >>
571              
572             =head1 BUGS
573              
574             Please report any bugs or feature requests to C<bug-datetime-ordinal at rt.cpan.org>, or through
575             the web interface at L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=DateTime-Ordinal>. I will be notified, and then you'll
576             automatically be notified of progress on your bug as I make changes.
577              
578             =head1 SUPPORT
579              
580             You can find documentation for this module with the perldoc command.
581              
582             perldoc DateTime::Ordinal
583              
584              
585             You can also look for information at:
586              
587             =over 4
588              
589             =item * RT: CPAN's request tracker (report bugs here)
590              
591             L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=DateTime-Ordinal>
592              
593             =item * Search CPAN
594              
595             L<https://metacpan.org/release/DateTime-Ordinal>
596              
597             =back
598              
599             =head1 ACKNOWLEDGEMENTS
600              
601             =head1 LICENSE AND COPYRIGHT
602              
603             This software is Copyright (c) 2019->2025 by LNATION.
604              
605             This is free software, licensed under:
606              
607             The Artistic License 2.0 (GPL Compatible)
608              
609              
610             =cut
611