File Coverage

blib/lib/Time/Piece.pm
Criterion Covered Total %
statement 377 398 94.7
branch 169 198 85.3
condition 63 93 67.7
subroutine 81 81 100.0
pod 4 51 7.8
total 694 821 84.5


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