File Coverage

blib/lib/Rose/DateTime/Util.pm
Criterion Covered Total %
statement 133 153 86.9
branch 90 120 75.0
condition 11 19 57.8
subroutine 15 15 100.0
pod 7 8 87.5
total 256 315 81.2


line stmt bran cond sub pod time code
1             package Rose::DateTime::Util;
2              
3 3     3   75363 use strict;
  3         8  
  3         152  
4              
5 3     3   59 use Carp();
  3         7  
  3         50  
6              
7 3     3   4325 use DateTime;
  3         553303  
  3         142  
8 3     3   34 use DateTime::Infinite;
  3         6  
  3         1621  
9              
10             require Exporter;
11             our @ISA = qw(Exporter);
12              
13             our @EXPORT_OK = qw(format_date parse_date parse_european_date parse_epoch);
14              
15             our %EXPORT_TAGS =
16             (
17             std => [ qw(format_date parse_date parse_european_date) ],
18             all => \@EXPORT_OK
19             );
20              
21             our $VERSION = '0.540';
22              
23             our $TZ = 'floating';
24             our $Debug = 0;
25             our $European_Dates = __PACKAGE__->init_european_dates;
26             our $Error;
27              
28 1     1 1 449 sub error { $Error }
29              
30             sub time_zone
31             {
32 9     9 1 14504 my($class) = shift;
33 9 100       38 return $TZ = shift if(@_);
34 7         43 return $TZ;
35             }
36              
37             sub european_dates
38             {
39 14     14 1 4812 my($class) = shift;
40              
41 14 100       48 if(@_)
42             {
43 9 100       29 if(defined $_[0])
44             {
45 6 100       36 return $European_Dates = $_[0] ? 1 : 0;
46             }
47              
48 3         13 return $European_Dates = $class->init_european_dates;
49             }
50              
51 5         28 return $European_Dates;
52             }
53              
54             sub init_european_dates
55             {
56             #my($class) = shift;
57              
58 6     6 0 33 my $locale_class = DateTime->DefaultLocale;
59            
60 6 50       76 unless(ref $locale_class)
61             {
62 0         0 $locale_class = DateTime::Locale->load($locale_class);
63             }
64              
65             # Fall back to the older (pre-0.4) DateTime::Locale API
66 6 50       88 my $short =
67             $locale_class->can('date_format_short') ?
68             $locale_class->date_format_short :
69             $locale_class->short_date_format;
70              
71 6         31 $short =~ tr{dmyDMY}{}cd;
72 6         13 $short =~ tr{dmyDMY}{dmydmy}s;
73              
74             # date_parts_order() is deprecated in DateTime::Locale 0.44+
75             #if($locale_class->date_parts_order eq 'dmy')
76 6 100       21 if($short eq 'dmy')
77             {
78 1         4 return 1;
79             }
80              
81 5         15 return 0;
82             }
83              
84             sub parse_european_date
85             {
86 772     772 1 724932 local $European_Dates = 1;
87 772         1570 &parse_date; # implicitly pass the current args: @_
88             }
89              
90             sub parse_date
91             {
92 2774     2774 1 1970072 my($arg, $time_zone) = @_;
93              
94 2774   66     13919 $time_zone ||= $TZ;
95              
96 2774         3729 my($fsecs, $secs, $mins, $hours, $mday, $month, $year, $wday, $yday, $isdst,
97             $month_abbrev, $date, $ampm, $hours2, $ampm2);
98              
99 2774         5084 $Error = undef;
100              
101 3     3   20 no warnings 'uninitialized';
  3         4  
  3         3020  
102              
103 2774 100 66     49765 if(ref $arg && $arg->isa('DateTime'))
    100 66        
    100          
    100          
    100          
    100          
    100          
104             {
105 3 100       11 if(@_ > 1)
106             {
107 2         4 my $error;
108              
109 2         4 TRY:
110             {
111 2         3 local $@;
112 2         4 eval { $arg->set_time_zone($time_zone) };
  2         11  
113 2         297 $error = $@;
114             }
115              
116 2 100       7 if($error)
117             {
118 1         2 $Error = $error;
119 1         4 return undef;
120             }
121             }
122              
123 2         8 return $arg;
124             }
125             elsif(($year, $month, $mday, $hours, $mins, $secs, $fsecs, $ampm, $hours2, $ampm2) = ($arg =~
126             m{
127             ^
128             (\d{4}) \s* [-._]? \s* # year
129             (\d{2}) \s* [-._]? \s* # month
130             (\d{2}) # day
131             (?:
132             \s* [-._T]? \s*
133             (?:
134             (\d\d?) : # hour
135             (\d\d) # min
136             (?: (?: : (\d\d) )? (?: \. (\d{0,9}) )? )? # sec? nanosec?
137             (?: \s* ([aApP]\.?[mM]\.?) )? # am/pm?
138             |
139             (\d\d?) # hour
140             (?: \s* ([aApP]\.?[mM]\.?) ) # am/pm
141             )
142             )?
143             $
144             }x))
145             {
146             # yyyy mm dd [hh:mm[:ss[.nnnnnnnnn]]] [am/pm] also valid w/o spaces or w/ hyphens
147              
148 394 100       873 $hours = $hours2 if(defined $hours2);
149 394 100       748 $ampm = $ampm2 if(defined $ampm2);
150              
151 394         1042 $date = _timelocal($secs, $mins, $hours, $mday, $month, $year, $ampm, $fsecs, $time_zone);
152             }
153             elsif(($month, $mday, $year, $hours, $mins, $secs, $fsecs, $ampm) = ($arg =~
154             m{
155             ^
156             (\d{1,2}) [-/._] (\d{1,2}) [-/._] (\d{4}) # xx-xx-yyyy
157             (?:
158             (?: \s+ | [-._T] )
159             (\d\d?) # hour
160             (?::(\d\d)(?::(\d\d))?)?(?:\.(\d{0,9}))? # min? sec? nanosec?
161             (?:\s*([aApP]\.?[mM]\.?))? # am/pm
162             )?
163             $
164             }x))
165             {
166             # Normal: mm/dd/yyyy, mm-dd-yyyy, mm.dd.yyyy [hh:mm[:ss][.nnnnnnnnn]] [am/pm]
167             # European: dd/mm/yyyy, dd-mm-yyyy, dd.mm.yyyy [hh:mm[:ss][.nnnnnnnnn]] [am/pm]
168              
169 2364 100       5727 if($European_Dates)
170             {
171 1546         3635 ($mday, $month) = ($month, $mday); # swap month and day in Euro-mode
172             }
173              
174 2364         6543 $date = _timelocal($secs, $mins, $hours, $mday, $month, $year, $ampm, $fsecs, $time_zone);
175             }
176             elsif(lc $arg eq 'now' || lc $arg eq 'now!')
177             {
178             # Right now
179 1         11 return DateTime->now(time_zone => $time_zone);
180             }
181             elsif($arg =~ /^(?: (-?\d+)(?:\.(\d{0,9}))? | (-?\d*)\.(\d{1,9}) )$/x)
182             {
183 8 100       34 my $epoch = defined $1 ? $1 : $3;
184 8 100       25 my $fsecs = defined $2 ? $2 : $4;
185              
186 8 50       21 $epoch = 0 if($epoch eq '-');
187              
188             # In Unix time format (guessing)
189 8   100     51 $date = DateTime->from_epoch(epoch => $epoch || 0, time_zone => $time_zone);
190              
191 8 100       2061 if($fsecs)
192             {
193 6         10 my $len = length $fsecs;
194              
195 6 100       19 if($len < 9)
    50          
196             {
197 3         27 $fsecs .= ('0' x (9 - length $fsecs));
198             }
199             elsif($len > 9)
200             {
201 0         0 $fsecs = substr($fsecs, 0, 9);
202             }
203              
204 6         22 $date->set(nanosecond => $fsecs);
205             }
206              
207 8         1858 return $date;
208             }
209             elsif($arg =~ /^today$/i)
210             {
211 1         25 $date = DateTime->now(time_zone => $time_zone);
212 1         401 $date->truncate(to => 'day');
213             }
214             elsif($arg =~ /^(-)?infinity$/i)
215             {
216 2 100       9 if($1)
217             {
218 1         10 return DateTime::Infinite::Past->new;
219             }
220              
221 1         13 return DateTime::Infinite::Future->new;
222             }
223             else
224             {
225 1 50       6 $Error = "Could not parse date: $arg" .
226             (($Error) ? " - $Error" : '');
227 1         4 return undef;
228             }
229              
230 2759 50       20722 unless($date)
231             {
232 0 0       0 $Error = "Could not parse date: $arg" .
233             (($Error) ? " - $Error" : '');
234 0         0 return undef;
235             }
236              
237 2759         127229 return $date;
238             }
239              
240             sub parse_epoch
241             {
242 1     1 1 673 my($arg, $time_zone) = @_;
243              
244 1   33     9 $time_zone ||= $TZ;
245              
246 1         2 $Error = undef;
247              
248 1         2 my $date;
249              
250 3     3   20 no warnings 'uninitialized';
  3         5  
  3         2873  
251              
252 1 50       9 if($arg =~ /^(?: (-?\d+)(?:\.(\d{0,9}))? | (-?\d*)\.(\d{1,9}) )$/x)
253             {
254 1 50       4 my $epoch = defined $1 ? $1 : $3;
255 1 50       5 my $fsecs = defined $2 ? $2 : $4;
256              
257 1 50       4 $epoch = 0 if($epoch eq '-');
258              
259             # In Unix time format (guessing)
260 1   50     10 $date = DateTime->from_epoch(epoch => $epoch || 0, time_zone => $time_zone);
261              
262 1 50       489 if($fsecs)
263             {
264 0         0 my $len = length $fsecs;
265              
266 0 0       0 if($len < 9)
    0          
267             {
268 0         0 $fsecs .= ('0' x (9 - length $fsecs));
269             }
270             elsif($len > 9)
271             {
272 0         0 $fsecs = substr($fsecs, 0, 9);
273             }
274              
275 0         0 $date->set(nanosecond => $fsecs);
276             }
277              
278 1         4 return $date;
279             }
280             else
281             {
282 0         0 return parse_date(@_);
283             }
284              
285 0 0       0 unless($date)
286             {
287 0 0       0 $Error = "Could not parse epoch: $arg" .
288             (($Error) ? " - $Error" : '');
289 0         0 return undef;
290             }
291              
292 0         0 return $date;
293             }
294              
295             sub format_date
296             {
297 74     74 1 40333 my($date, @formats) = @_;
298              
299 74         108 my(@localtime, %formats, @ret, $ret);
300              
301 74 50       177 return undef unless(defined $date);
302             #return $date if($date =~ /^-?infinity$/i);
303              
304 74 50 33     553 unless(ref $date && $date->isa('DateTime'))
305             {
306 0         0 Carp::croak("format_date() requires a DateTime object as its first argument");
307             }
308              
309 74 100       503 return '-infinity' if($date->isa('DateTime::Infinite::Past'));
310 73 100       504 return 'infinity' if($date->isa('DateTime::Infinite::Future'));
311              
312 72         128 foreach my $format (@formats)
313             {
314 74         198 $format =~ s/%t/%l:%M:%S %p/g; # strftime() treats %t as a \t
315              
316             # Formats not handled by strftime()
317 74 100       264 if($format =~ /%[EFf]/)
318             {
319 31 50       75 unless(%formats)
320             {
321 31         34 my $date_word;
322 31         102 my $mday = $date->day;
323              
324             #if($mday =~ /([^1]|^)1$/) { $date_word = $mday . 'st' }
325             #elsif($mday =~ /([^1]|^)2$/) { $date_word = $mday . 'nd' }
326             #elsif($mday =~ /([^1]|^)3$/) { $date_word = $mday . 'rd' }
327             #else { $date_word = $mday . 'th' }
328              
329             # Requires a reasonably modern perl
330 31 100       252 if($mday =~ /(?
  3 100       5  
    100          
331 2         5 elsif($mday =~ /(?
332 2         3 elsif($mday =~ /(?
333 24         37 else { $date_word = $mday . 'th' }
334              
335 31         126 %formats =
336             (
337             #'%e' => $mday, # DateTime snagged this one
338             '%E' => $date_word,
339             '%F' => $date->strftime("%A, %B $date_word %Y"),
340             '%f' => $date->month + 0,
341             );
342             }
343              
344 31         1932 $format =~ s/(%[eEFf])/$formats{$1}/g;
345             }
346              
347 74         240 push(@ret, $date->strftime($format));
348             }
349              
350 72 100       4563 return wantarray ? @ret : join(' ', @ret);
351             }
352              
353             #
354             # Internal Subroutines
355             #
356              
357             sub _timelocal
358             {
359 2758     2758   6822 my($secs, $mins, $hours, $mday, $month, $year, $ampm, $fsecs, $tz) = @_;
360              
361 2758         6294 my($date);
362              
363 2758 100       5582 $hours = 0 unless(defined($hours));
364              
365 2758 100       4556 if(defined $fsecs)
366             {
367 2022         2815 my $len = length $fsecs;
368              
369 2022 100       5039 if($len < 9)
    50          
370             {
371 1348         3268 $fsecs .= ('0' x (9 - length $fsecs));
372             }
373             elsif($len > 9)
374             {
375 0         0 $fsecs = substr($fsecs, 0, 9);
376             }
377             }
378             else
379             {
380 736         908 $fsecs = 0;
381             }
382              
383 2758 100       8808 $secs = 0 unless(defined $secs);
384 2758 100       4791 $mins = 0 unless(defined $mins);
385              
386 2758 100       4985 if($ampm)
387             {
388 1352 100       4217 if($ampm =~ /^p/i)
    50          
389             {
390 1349 50       4855 $hours += 12 unless($hours == 12);
391             }
392             elsif($hours == 12)
393             {
394 0         0 $hours = 0;
395             }
396             }
397              
398 2758         3188 my $error;
399              
400 2758         2832 TRY:
401             {
402 2758         2724 local $@;
403              
404             eval
405 2758         3872 {
406 2758         12118 $date = DateTime->new(year => $year,
407             month => $month,
408             day => $mday,
409             hour => $hours,
410             minute => $mins,
411             second => $secs,
412             nanosecond => $fsecs,
413             time_zone => $tz);
414             };
415              
416 2758         779723 $error = $@;
417             }
418              
419 2758 50       6841 if($error)
420             {
421 0         0 $Error = $error;
422 0 0       0 warn $Error if($Debug); # $ENV{'MOD_PERL'}
423 0         0 return;
424             }
425              
426 2758         6741 return $date;
427             }
428              
429             1;
430              
431             # Can't figure out how to hide comments like this from search.cpan.org's
432             # POD-to-HTML translator...
433             # =begin comment
434             # B The local time zone may not be known on all systems (in
435             # particular, Win32 systems). If you are on such a system, you will encounter a
436             # fatal error if C tries to construct a L object with
437             # a time zone of "local".
438             #
439             # See the L documentation for information on the various
440             # ways to successfully indicate your local time zone, or set a different default
441             # time zone for this class by calling
442             # Ltime_zone(...)> with a new time zone as an
443             # argument.
444             # =end comment
445              
446             __END__