File Coverage

blib/lib/Time/Piece.pm
Criterion Covered Total %
statement 285 356 80.0
branch 106 148 71.6
condition 33 52 63.4
subroutine 71 77 92.2
pod 1 47 2.1
total 496 680 72.9


line stmt bran cond sub pod time code
1             package Time::Piece;
2              
3 8     8   459506 use strict;
  8         69  
  8         980  
4              
5 8     8   38 use XSLoader ();
  8         11  
  8         109  
6 8     8   2977 use Time::Seconds;
  8         18  
  8         462  
7 8     8   51 use Carp;
  8         13  
  8         484  
8 8     8   3833 use Time::Local;
  8         16250  
  8         595  
9 8     8   56 use Scalar::Util qw/ blessed /;
  8         14  
  8         549  
10              
11 8     8   42 use Exporter ();
  8         13  
  8         1356  
12              
13             our @EXPORT = qw(
14             localtime
15             gmtime
16             );
17              
18             our %EXPORT_TAGS = (
19             ':override' => 'internal',
20             );
21              
22             our $VERSION = '1.33';
23              
24             XSLoader::load( 'Time::Piece', $VERSION );
25              
26             my $DATE_SEP = '-';
27             my $TIME_SEP = ':';
28             my @MON_LIST = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
29             my @FULLMON_LIST = qw(January February March April May June July
30             August September October November December);
31             my @DAY_LIST = qw(Sun Mon Tue Wed Thu Fri Sat);
32             my @FULLDAY_LIST = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday);
33             my $IS_WIN32 = ($^O =~ /Win32/);
34              
35             my $LOCALE;
36              
37             use constant {
38 8         7449 'c_sec' => 0,
39             'c_min' => 1,
40             'c_hour' => 2,
41             'c_mday' => 3,
42             'c_mon' => 4,
43             'c_year' => 5,
44             'c_wday' => 6,
45             'c_yday' => 7,
46             'c_isdst' => 8,
47             'c_epoch' => 9,
48             'c_islocal' => 10,
49 8     8   81 };
  8         15  
50              
51             sub localtime {
52 22 100   22 0 1918 unshift @_, __PACKAGE__ unless eval { $_[0]->isa('Time::Piece') };
  22         117  
53 22         40 my $class = shift;
54 22         32 my $time = shift;
55 22 100       44 $time = time if (!defined $time);
56 22         101 $class->_mktime($time, 1);
57             }
58              
59             sub gmtime {
60 23 100   23 0 2704 unshift @_, __PACKAGE__ unless eval { $_[0]->isa('Time::Piece') };
  23         130  
61 23         47 my $class = shift;
62 23         31 my $time = shift;
63 23 100       46 $time = time if (!defined $time);
64 23         59 $class->_mktime($time, 0);
65             }
66              
67              
68             # Check if the supplied param is either a normal array (as returned from
69             # localtime in list context) or a Time::Piece-like wrapper around one.
70             #
71             # We need to differentiate between an array ref that we can interrogate and
72             # other blessed objects (like overloaded values).
73             sub _is_time_struct {
74 87 100   87   206 return 1 if ref($_[1]) eq 'ARRAY';
75 60 100 100     453 return 1 if blessed($_[1]) && $_[1]->isa('Time::Piece');
76              
77 52         118 return 0;
78             }
79              
80              
81             sub new {
82 11     11 0 10148 my $class = shift;
83 11         23 my ($time) = @_;
84              
85 11         13 my $self;
86              
87 11 100 66     59 if ($class->_is_time_struct($time)) {
    100          
    100          
88 4 100       21 $self = $time->[c_islocal] ? $class->localtime($time) : $class->gmtime($time);
89             }
90             elsif (defined($time)) {
91 4         33 $self = $class->localtime($time);
92             }
93             elsif (ref($class) && $class->isa(__PACKAGE__)) {
94 1         6 $self = $class->_mktime($class->epoch, $class->[c_islocal]);
95             }
96             else {
97 2         8 $self = $class->localtime();
98             }
99              
100 11   66     53 return bless $self, ref($class) || $class;
101             }
102              
103             sub parse {
104 0     0 0 0 my $proto = shift;
105 0   0     0 my $class = ref($proto) || $proto;
106 0         0 my @components;
107              
108 0         0 warnings::warnif("deprecated",
109             "parse() is deprecated, use strptime() instead.");
110              
111 0 0       0 if (@_ > 1) {
112 0         0 @components = @_;
113             }
114             else {
115 0         0 @components = shift =~ /(\d+)$DATE_SEP(\d+)$DATE_SEP(\d+)(?:(?:T|\s+)(\d+)$TIME_SEP(\d+)(?:$TIME_SEP(\d+)))/;
116 0         0 @components = reverse(@components[0..5]);
117             }
118 0         0 return $class->new( timelocal(@components ));
119             }
120              
121             sub _mktime {
122 76     76   149 my ($class, $time, $islocal) = @_;
123              
124 76   66     480 $class = blessed($class) || $class;
125              
126 76 100       180 if ($class->_is_time_struct($time)) {
127 31         65 my @new_time = @$time;
128 31         64 my @tm_parts = (@new_time[c_sec .. c_mon], $new_time[c_year]+1900);
129 31 100       119 $new_time[c_epoch] = $islocal ? timelocal(@tm_parts) : timegm(@tm_parts);
130              
131 31 50       1281 return wantarray ? @new_time : bless [@new_time[0..9], $islocal], $class;
132             }
133 45         1014 _tzset();
134 45 100       1459 my @time = $islocal ?
135             CORE::localtime($time)
136             :
137             CORE::gmtime($time);
138 45 50       644 wantarray ? @time : bless [@time, $time, $islocal], $class;
139             }
140              
141             my %_special_exports = (
142 11     11   415 localtime => sub { my $c = $_[0]; sub { $c->localtime(@_) } },
143 11     11   1027 gmtime => sub { my $c = $_[0]; sub { $c->gmtime(@_) } },
144             );
145              
146             sub export {
147 9     9 0 27 my ($class, $to, @methods) = @_;
148 9         22 for my $method (@methods) {
149 18 50       40 if (exists $_special_exports{$method}) {
150 8     8   57 no strict 'refs';
  8         15  
  8         254  
151 8     8   37 no warnings 'redefine';
  8         13  
  8         13529  
152 18         40 *{$to . "::$method"} = $_special_exports{$method}->($class);
  18         3805  
153             } else {
154 0         0 $class->Exporter::export($to, $method);
155             }
156             }
157             }
158              
159             sub import {
160             # replace CORE::GLOBAL localtime and gmtime if passed :override
161 9     9   423 my $class = shift;
162 9         13 my %params;
163 9         45 map($params{$_}++,@_,@EXPORT);
164 9 50       35 if (delete $params{':override'}) {
165 0         0 $class->export('CORE::GLOBAL', keys %params);
166             }
167             else {
168 9         40 $class->export(scalar caller, keys %params);
169             }
170             }
171              
172             ## Methods ##
173              
174             sub sec {
175 36     36 0 51 my $time = shift;
176 36         128 $time->[c_sec];
177             }
178              
179             *second = \&sec;
180              
181             sub min {
182 36     36 0 43 my $time = shift;
183 36         71 $time->[c_min];
184             }
185              
186             *minute = \&min;
187              
188             sub hour {
189 36     36 0 46 my $time = shift;
190 36         68 $time->[c_hour];
191             }
192              
193             sub mday {
194 44     44 0 60 my $time = shift;
195 44         100 $time->[c_mday];
196             }
197              
198             *day_of_month = \&mday;
199              
200             sub mon {
201 53     53 0 68 my $time = shift;
202 53         203 $time->[c_mon] + 1;
203             }
204              
205             sub _mon {
206 11     11   18 my $time = shift;
207 11         22 $time->[c_mon];
208             }
209              
210             sub month {
211 3     3 0 11 my $time = shift;
212 3 100       9 if (@_) {
    50          
213 1         5 return $_[$time->[c_mon]];
214             }
215             elsif (@MON_LIST) {
216 2         9 return $MON_LIST[$time->[c_mon]];
217             }
218             else {
219 0         0 return $time->strftime('%b');
220             }
221             }
222              
223             *monname = \&month;
224              
225             sub fullmonth {
226 0     0 0 0 my $time = shift;
227 0 0       0 if (@_) {
    0          
228 0         0 return $_[$time->[c_mon]];
229             }
230             elsif (@FULLMON_LIST) {
231 0         0 return $FULLMON_LIST[$time->[c_mon]];
232             }
233             else {
234 0         0 return $time->strftime('%B');
235             }
236             }
237              
238             sub year {
239 64     64 0 610 my $time = shift;
240 64         231 $time->[c_year] + 1900;
241             }
242              
243             sub _year {
244 2     2   4 my $time = shift;
245 2         9 $time->[c_year];
246             }
247              
248             sub yy {
249 2     2 0 6 my $time = shift;
250 2         7 my $res = $time->[c_year] % 100;
251 2 100       11 return $res > 9 ? $res : "0$res";
252             }
253              
254             sub wday {
255 3     3 0 6 my $time = shift;
256 3         16 $time->[c_wday] + 1;
257             }
258              
259             sub _wday {
260 5     5   10 my $time = shift;
261 5         17 $time->[c_wday];
262             }
263              
264             *day_of_week = \&_wday;
265              
266             sub wdayname {
267 2     2 0 6 my $time = shift;
268 2 100       6 if (@_) {
    50          
269 1         5 return $_[$time->[c_wday]];
270             }
271             elsif (@DAY_LIST) {
272 1         6 return $DAY_LIST[$time->[c_wday]];
273             }
274             else {
275 0         0 return $time->strftime('%a');
276             }
277             }
278              
279             *day = \&wdayname;
280              
281             sub fullday {
282 0     0 0 0 my $time = shift;
283 0 0       0 if (@_) {
    0          
284 0         0 return $_[$time->[c_wday]];
285             }
286             elsif (@FULLDAY_LIST) {
287 0         0 return $FULLDAY_LIST[$time->[c_wday]];
288             }
289             else {
290 0         0 return $time->strftime('%A');
291             }
292             }
293              
294             sub yday {
295 5     5 0 10 my $time = shift;
296 5         17 $time->[c_yday];
297             }
298              
299             *day_of_year = \&yday;
300              
301             sub isdst {
302 5     5 0 9 my $time = shift;
303 5         19 $time->[c_isdst];
304             }
305              
306             *daylight_savings = \&isdst;
307              
308             # Thanks to Tony Olekshy for this algorithm
309             sub tzoffset {
310 3     3 0 13 my $time = shift;
311              
312 3 50       9 return Time::Seconds->new(0) unless $time->[c_islocal];
313              
314 3         8 my $epoch = $time->epoch;
315              
316             my $j = sub {
317              
318 6     6   13 my ($s,$n,$h,$d,$m,$y) = @_; $m += 1; $y += 1900;
  6         7  
  6         7  
319              
320 6         12 $time->_jd($y, $m, $d, $h, $n, $s);
321              
322 3         13 };
323              
324             # Compute floating offset in hours.
325             #
326             # Note use of crt methods so the tz is properly set...
327             # See: http://perlmonks.org/?node_id=820347
328 3         17 my $delta = 24 * ($j->(_crt_localtime($epoch)) - $j->(_crt_gmtime($epoch)));
329              
330             # Return value in seconds rounded to nearest minute.
331 3 50       32 return Time::Seconds->new( int($delta * 60 + ($delta >= 0 ? 0.5 : -0.5)) * 60 );
332             }
333              
334             sub epoch {
335 95     95 1 157 my $time = shift;
336 95 50       179 if (defined($time->[c_epoch])) {
337 95         651 return $time->[c_epoch];
338             }
339             else {
340             my $epoch = $time->[c_islocal] ?
341 0         0 timelocal(@{$time}[c_sec .. c_mon], $time->[c_year]+1900)
342             :
343 0 0       0 timegm(@{$time}[c_sec .. c_mon], $time->[c_year]+1900);
  0         0  
344 0         0 $time->[c_epoch] = $epoch;
345 0         0 return $epoch;
346             }
347             }
348              
349             sub hms {
350 9     9 0 16 my $time = shift;
351 9 100       24 my $sep = @_ ? shift(@_) : $TIME_SEP;
352 9         62 sprintf("%02d$sep%02d$sep%02d", $time->[c_hour], $time->[c_min], $time->[c_sec]);
353             }
354              
355             *time = \&hms;
356              
357             sub ymd {
358 11     11 0 20 my $time = shift;
359 11 100       28 my $sep = @_ ? shift(@_) : $DATE_SEP;
360 11         31 sprintf("%d$sep%02d$sep%02d", $time->year, $time->mon, $time->[c_mday]);
361             }
362              
363             *date = \&ymd;
364              
365             sub mdy {
366 3     3 0 7 my $time = shift;
367 3 100       11 my $sep = @_ ? shift(@_) : $DATE_SEP;
368 3         12 sprintf("%02d$sep%02d$sep%d", $time->mon, $time->[c_mday], $time->year);
369             }
370              
371             sub dmy {
372 3     3 0 6 my $time = shift;
373 3 100       11 my $sep = @_ ? shift(@_) : $DATE_SEP;
374 3         13 sprintf("%02d$sep%02d$sep%d", $time->[c_mday], $time->mon, $time->year);
375             }
376              
377             sub datetime {
378 3     3 0 499 my $time = shift;
379 3         14 my %seps = (date => $DATE_SEP, T => 'T', time => $TIME_SEP, @_);
380 3         10 return join($seps{T}, $time->date($seps{date}), $time->time($seps{time}));
381             }
382              
383              
384              
385             # Julian Day is always calculated for UT regardless
386             # of local time
387             sub julian_day {
388 25     25 0 416 my $time = shift;
389             # Correct for localtime
390 25 100       88 $time = $time->gmtime( $time->epoch ) if $time->[c_islocal];
391              
392             # Calculate the Julian day itself
393 25         49 my $jd = $time->_jd( $time->year, $time->mon, $time->mday,
394             $time->hour, $time->min, $time->sec);
395              
396 25         128 return $jd;
397             }
398              
399             # MJD is defined as JD - 2400000.5 days
400             sub mjd {
401 13     13 0 49 return shift->julian_day - 2_400_000.5;
402             }
403              
404             # Internal calculation of Julian date. Needed here so that
405             # both tzoffset and mjd/jd methods can share the code
406             # Algorithm from Hatcher 1984 (QJRAS 25, 53-55), and
407             # Hughes et al, 1989, MNRAS, 238, 15
408             # See: http://adsabs.harvard.edu/cgi-bin/nph-bib_query?bibcode=1989MNRAS.238.1529H&db_key=AST
409             # for more details
410              
411             sub _jd {
412 31     31   40 my $self = shift;
413 31         50 my ($y, $m, $d, $h, $n, $s) = @_;
414              
415             # Adjust input parameters according to the month
416 31 100       53 $y = ( $m > 2 ? $y : $y - 1);
417 31 100       55 $m = ( $m > 2 ? $m - 3 : $m + 9);
418              
419             # Calculate the Julian Date (assuming Julian calendar)
420 31         87 my $J = int( 365.25 *( $y + 4712) )
421             + int( (30.6 * $m) + 0.5)
422             + 59
423             + $d
424             - 0.5;
425              
426             # Calculate the Gregorian Correction (since we have Gregorian dates)
427 31         61 my $G = 38 - int( 0.75 * int(49+($y/100)));
428              
429             # Calculate the actual Julian Date
430 31         39 my $JD = $J + $G;
431              
432             # Modify to include hours/mins/secs in floating portion.
433 31         68 return $JD + ($h + ($n + $s / 60) / 60) / 24;
434             }
435              
436             sub week {
437 11     11 0 16 my $self = shift;
438              
439 11         24 my $J = $self->julian_day;
440             # Julian day is independent of time zone so add on tzoffset
441             # if we are using local time here since we want the week day
442             # to reflect the local time rather than UTC
443 11 50       23 $J += ($self->tzoffset/(24*3600)) if $self->[c_islocal];
444              
445             # Now that we have the Julian day including fractions
446             # convert it to an integer Julian Day Number using nearest
447             # int (since the day changes at midday we convert all Julian
448             # dates to following midnight).
449 11         14 $J = int($J+0.5);
450              
451 8     8   4399 use integer;
  8         115  
  8         36  
452 11         26 my $d4 = ((($J + 31741 - ($J % 7)) % 146097) % 36524) % 1461;
453 11         16 my $L = $d4 / 1460;
454 11         14 my $d1 = (($d4 - $L) % 365) + $L;
455 11         53 return $d1 / 7 + 1;
456             }
457              
458             sub _is_leap_year {
459 6     6   11 my $year = shift;
460 6 100 100     52 return (($year %4 == 0) && !($year % 100 == 0)) || ($year % 400 == 0)
461             ? 1 : 0;
462             }
463              
464             sub is_leap_year {
465 2     2 0 5 my $time = shift;
466 2         5 my $year = $time->year;
467 2         8 return _is_leap_year($year);
468             }
469              
470             my @MON_LAST = qw(31 28 31 30 31 30 31 31 30 31 30 31);
471              
472             sub month_last_day {
473 2     2 0 4 my $time = shift;
474 2         6 my $year = $time->year;
475 2         5 my $_mon = $time->_mon;
476 2 100       14 return $MON_LAST[$_mon] + ($_mon == 1 ? _is_leap_year($year) : 0);
477             }
478              
479             my $trans_map_common = {
480              
481             'c' => sub {
482             my ( $format ) = @_;
483             if($LOCALE->{PM} && $LOCALE->{AM}){
484             $format =~ s/%c/%a %d %b %Y %I:%M:%S %p/;
485             }
486             else{
487             $format =~ s/%c/%a %d %b %Y %H:%M:%S/;
488             }
489             return $format;
490             },
491             'r' => sub {
492             my ( $format ) = @_;
493             if($LOCALE->{PM} && $LOCALE->{AM}){
494             $format =~ s/%r/%I:%M:%S %p/;
495             }
496             else{
497             $format =~ s/%r/%H:%M:%S/;
498             }
499             return $format;
500             },
501             'X' => sub {
502             my ( $format ) = @_;
503             if($LOCALE->{PM} && $LOCALE->{AM}){
504             $format =~ s/%X/%I:%M:%S %p/;
505             }
506             else{
507             $format =~ s/%X/%H:%M:%S/;
508             }
509             return $format;
510             },
511             };
512              
513             my $strftime_trans_map = {
514             %{$trans_map_common},
515              
516             'e' => sub {
517             my ( $format, $time ) = @_;
518             $format =~ s/%e/%d/ if $IS_WIN32;
519             return $format;
520             },
521             'D' => sub {
522             my ( $format, $time ) = @_;
523             $format =~ s/%D/%m\/%d\/%y/;
524             return $format;
525             },
526             'F' => sub {
527             my ( $format, $time ) = @_;
528             $format =~ s/%F/%Y-%m-%d/;
529             return $format;
530             },
531             'R' => sub {
532             my ( $format, $time ) = @_;
533             $format =~ s/%R/%H:%M/;
534             return $format;
535             },
536             's' => sub {
537             #%s not portable if time parts are from gmtime since %s will
538             #cause a call to native mktime (and thus uses local TZ)
539             my ( $format, $time ) = @_;
540             $format =~ s/%s/$time->[c_epoch]/;
541             return $format;
542             },
543             'T' => sub {
544             my ( $format, $time ) = @_;
545             $format =~ s/%T/%H:%M:%S/ if $IS_WIN32;
546             return $format;
547             },
548             'u' => sub {
549             my ( $format, $time ) = @_;
550             $format =~ s/%u/%w/ if $IS_WIN32;
551             return $format;
552             },
553             'V' => sub {
554             my ( $format, $time ) = @_;
555             my $week = sprintf( "%02d", $time->week() );
556             $format =~ s/%V/$week/ if $IS_WIN32;
557             return $format;
558             },
559             'x' => sub {
560             my ( $format, $time ) = @_;
561             $format =~ s/%x/%a %d %b %Y/;
562             return $format;
563             },
564             'z' => sub { #%[zZ] not portable if time parts are from gmtime
565             my ( $format, $time ) = @_;
566             $format =~ s/%z/+0000/ if not $time->[c_islocal];
567             return $format;
568             },
569             'Z' => sub {
570             my ( $format, $time ) = @_;
571             $format =~ s/%Z/UTC/ if not $time->[c_islocal];
572             return $format;
573             },
574             };
575              
576             sub strftime {
577 44     44 0 2270 my $time = shift;
578 44 50       96 my $format = @_ ? shift(@_) : '%a, %d %b %Y %H:%M:%S %Z';
579 44         85 $format = _translate_format($format, $strftime_trans_map, $time);
580              
581 44 100       115 return $format unless $format =~ /%/; #if translate removes everything
582              
583 40         85 return _strftime($format, $time->epoch, $time->[c_islocal]);
584             }
585              
586             my $strptime_trans_map = {
587             %{$trans_map_common},
588             };
589              
590             sub strptime {
591 20     20 0 3316 my $time = shift;
592 20         31 my $string = shift;
593 20 50       49 my $format = @_ ? shift(@_) : "%a, %d %b %Y %H:%M:%S %Z";
594 20 50       61 my $islocal = (ref($time) ? $time->[c_islocal] : 0);
595 20   66     52 my $locales = $LOCALE || &Time::Piece::_default_locale();
596 20         39 $format = _translate_format($format, $strptime_trans_map);
597 20         200 my @vals = _strptime($string, $format, $islocal, $locales);
598             # warn(sprintf("got vals: %d-%d-%d %d:%d:%d\n", reverse(@vals[c_sec..c_year])));
599 20         74 return scalar $time->_mktime(\@vals, $islocal);
600             }
601              
602             sub day_list {
603 4 100 66 4 0 23 shift if ref($_[0]) && $_[0]->isa(__PACKAGE__); # strip first if called as a method
604 4         11 my @old = @DAY_LIST;
605 4 100       10 if (@_) {
606 2         6 @DAY_LIST = @_;
607 2         6 &Time::Piece::_default_locale();
608             }
609 4         10 return @old;
610             }
611              
612             sub mon_list {
613 4 100 66 4 0 585 shift if ref($_[0]) && $_[0]->isa(__PACKAGE__); # strip first if called as a method
614 4         14 my @old = @MON_LIST;
615 4 100       8 if (@_) {
616 2         6 @MON_LIST = @_;
617 2         4 &Time::Piece::_default_locale();
618             }
619 4         13 return @old;
620             }
621              
622             sub time_separator {
623 5 100 66 5 0 23 shift if ref($_[0]) && $_[0]->isa(__PACKAGE__);
624 5         8 my $old = $TIME_SEP;
625 5 100       9 if (@_) {
626 2         4 $TIME_SEP = $_[0];
627             }
628 5         13 return $old;
629             }
630              
631             sub date_separator {
632 5 100 66 5 0 28 shift if ref($_[0]) && $_[0]->isa(__PACKAGE__);
633 5         7 my $old = $DATE_SEP;
634 5 100       12 if (@_) {
635 2         3 $DATE_SEP = $_[0];
636             }
637 5         11 return $old;
638             }
639              
640 8         187 use overload '""' => \&cdate,
641             'cmp' => \&str_compare,
642 8     8   11586 'fallback' => undef;
  8         29  
643              
644             sub cdate {
645 18     18 0 2431 my $time = shift;
646 18 100       43 if ($time->[c_islocal]) {
647 4         461 return scalar(CORE::localtime($time->epoch));
648             }
649             else {
650 14         28 return scalar(CORE::gmtime($time->epoch));
651             }
652             }
653              
654             sub str_compare {
655 3     3 0 7 my ($lhs, $rhs, $reverse) = @_;
656              
657 3 50 33     19 if (blessed($rhs) && $rhs->isa('Time::Piece')) {
658 3         7 $rhs = "$rhs";
659             }
660 3 50       8 return $reverse ? $rhs cmp $lhs->cdate : $lhs->cdate cmp $rhs;
661             }
662              
663             use overload
664 8         45 '-' => \&subtract,
665 8     8   1879 '+' => \&add;
  8         17  
666              
667             sub subtract {
668 3     3 0 990 my $time = shift;
669 3         7 my $rhs = shift;
670              
671 3 100       11 if (shift)
672             {
673             # SWAPED is set (so someone tried an expression like NOTDATE - DATE).
674             # Imitate Perl's standard behavior and return the result as if the
675             # string $time resolves to was subtracted from NOTDATE. This way,
676             # classes which override this one and which have a stringify function
677             # that resolves to something that looks more like a number don't need
678             # to override this function.
679 1         72 return $rhs - "$time";
680             }
681              
682 2 100 66     32 if (blessed($rhs) && $rhs->isa('Time::Piece')) {
683 1         4 return Time::Seconds->new($time->epoch - $rhs->epoch);
684             }
685             else {
686             # rhs is seconds.
687 1         4 return $time->_mktime(($time->epoch - $rhs), $time->[c_islocal]);
688             }
689             }
690              
691             sub add {
692 2     2 0 386 my $time = shift;
693 2         4 my $rhs = shift;
694              
695 2         83 return $time->_mktime(($time->epoch + $rhs), $time->[c_islocal]);
696             }
697              
698             use overload
699 8     8   1868 '<=>' => \&compare;
  8         25  
  8         57  
700              
701             sub get_epochs {
702 8     8 0 11 my ($lhs, $rhs, $reverse) = @_;
703 8 100 66     43 unless (blessed($rhs) && $rhs->isa('Time::Piece')) {
704 3         7 $rhs = $lhs->new($rhs);
705             }
706 8 50       16 if ($reverse) {
707 0         0 return $rhs->epoch, $lhs->epoch;
708             }
709 8         17 return $lhs->epoch, $rhs->epoch;
710             }
711              
712             sub compare {
713 8     8 0 48 my ($lhs, $rhs) = get_epochs(@_);
714 8         31 return $lhs <=> $rhs;
715             }
716              
717             sub add_months {
718 8     8 0 18 my ($time, $num_months) = @_;
719              
720 8 100       180 croak("add_months requires a number of months") unless defined($num_months);
721              
722 7         15 my $final_month = $time->_mon + $num_months;
723 7         10 my $num_years = 0;
724 7 100 100     25 if ($final_month > 11 || $final_month < 0) {
725             # these two ops required because we have no POSIX::floor and don't
726             # want to load POSIX.pm
727 5 100 100     20 if ($final_month < 0 && $final_month % 12 == 0) {
728 2         5 $num_years = int($final_month / 12) + 1;
729             }
730             else {
731 3         6 $num_years = int($final_month / 12);
732             }
733 5 100       10 $num_years-- if ($final_month < 0);
734              
735 5         7 $final_month = $final_month % 12;
736             }
737              
738 7         15 my @vals = _mini_mktime($time->sec, $time->min, $time->hour,
739             $time->mday, $final_month, $time->year - 1900 + $num_years);
740             # warn(sprintf("got %d vals: %d-%d-%d %d:%d:%d [%d]\n", scalar(@vals), reverse(@vals), $time->[c_islocal]));
741 7         18 return scalar $time->_mktime(\@vals, $time->[c_islocal]);
742             }
743              
744             sub add_years {
745 1     1 0 4 my ($time, $years) = @_;
746 1         4 $time->add_months($years * 12);
747             }
748              
749             sub truncate {
750 0     0 0 0 my ($time, %params) = @_;
751 0 0       0 return $time unless exists $params{to};
752             #if ($params{to} eq 'week') { return $time->_truncate_week; }
753 0         0 my %units = (
754             second => 0,
755             minute => 1,
756             hour => 2,
757             day => 3,
758             month => 4,
759             quarter => 5,
760             year => 5
761             );
762 0         0 my $to = $units{$params{to}};
763 0 0       0 croak "Invalid value of 'to' parameter: $params{to}" unless defined $to;
764 0         0 my $start_month = 0;
765 0 0       0 if ($params{to} eq 'quarter') {
766 0         0 $start_month = int( $time->_mon / 3 ) * 3;
767             }
768 0         0 my @down_to = (0, 0, 0, 1, $start_month, $time->year);
769 0         0 return $time->_mktime([@down_to[0..$to-1], @$time[$to..c_isdst]],
770             $time->[c_islocal]);
771             }
772              
773             #Given a format and a translate map, replace format flags in
774             #accordance with the logic from the translation map subroutines
775             sub _translate_format {
776 64     64   127 my ( $format, $trans_map, $time ) = @_;
777              
778 64         287 $format =~ s/%%/\e\e/g; #escape the escape
779 64         1630 my $lexer = _build_format_lexer($format);
780              
781 64         110 while(my $flag = $lexer->() ){
782 124 100       290 next unless exists $trans_map->{$flag};
783 21         55 $format = $trans_map->{$flag}($format, $time);
784             }
785              
786 64         107 $format =~ s/\e\e/%%/g;
787 64         218 return $format;
788             }
789              
790             sub _build_format_lexer {
791 64     64   84 my $format = shift();
792              
793             #Higher Order Perl p.359 (or thereabouts)
794             return sub {
795             LABEL: {
796 188 100   188   187 return $1 if $format =~ m/\G%([a-zA-Z])/gc; #return single char flags
  253         763  
797              
798 129 100       227 redo LABEL if $format =~ m/\G(.)/gc;
799 64         117 return; #return at empty string
800             }
801 64         231 };
802             }
803              
804             sub use_locale {
805             #get locale month/day names from posix strftime (from Piece.xs)
806 0     0 0 0 my $locales = _get_localization();
807              
808 0   0     0 $locales->{PM} ||= '';
809 0   0     0 $locales->{AM} ||= '';
810              
811 0         0 $locales->{pm} = lc $locales->{PM};
812 0         0 $locales->{am} = lc $locales->{AM};
813             #should probably figure out how to get a
814             #region specific format for %c someday
815 0         0 $locales->{c_fmt} = '';
816              
817             #Set globals. If anything is
818             #weird just use original
819 0 0       0 if( @{$locales->{weekday}} < 7 ){
  0         0  
820 0         0 @{$locales->{weekday}} = @FULLDAY_LIST;
  0         0  
821             }
822             else {
823 0         0 @FULLDAY_LIST = @{$locales->{weekday}};
  0         0  
824             }
825              
826 0 0       0 if( @{$locales->{wday}} < 7 ){
  0         0  
827 0         0 @{$locales->{wday}} = @DAY_LIST;
  0         0  
828             }
829             else {
830 0         0 @DAY_LIST = @{$locales->{wday}};
  0         0  
831             }
832              
833 0 0       0 if( @{$locales->{month}} < 12 ){
  0         0  
834 0         0 @{$locales->{month}} = @FULLMON_LIST;
  0         0  
835             }else {
836 0         0 @FULLMON_LIST = @{$locales->{month}};
  0         0  
837             }
838              
839 0 0       0 if( @{$locales->{mon}} < 12 ){
  0         0  
840 0         0 @{$locales->{mon}} = @MON_LIST;
  0         0  
841             }
842             else{
843 0         0 @MON_LIST= @{$locales->{mon}};
  0         0  
844             }
845              
846 0         0 $LOCALE = $locales;
847             }
848              
849             #$Time::Piece::LOCALE is used by strptime and thus needs to be
850             #in sync with what ever users change to via day_list() and mon_list().
851             #Should probably deprecate this use of gloabl state, but oh well...
852             sub _default_locale {
853 7     7   35 my $locales = {};
854              
855 7         14 @{ $locales->{weekday} } = @FULLDAY_LIST;
  7         26  
856 7         13 @{ $locales->{wday} } = @DAY_LIST;
  7         20  
857 7         11 @{ $locales->{month} } = @FULLMON_LIST;
  7         27  
858 7         12 @{ $locales->{mon} } = @MON_LIST;
  7         28  
859 7         12 $locales->{alt_month} = $locales->{month};
860              
861 7         15 $locales->{PM} = 'PM';
862 7         88 $locales->{AM} = 'AM';
863 7         249 $locales->{pm} = 'pm';
864 7         13 $locales->{am} = 'am';
865 7         11 $locales->{c_fmt} = '';
866              
867 7         25 $LOCALE = $locales;
868             }
869              
870             sub _locale {
871 0     0     return $LOCALE;
872             }
873              
874              
875             1;
876             __END__