File Coverage

blib/lib/DateTime/Moonpig.pm
Criterion Covered Total %
statement 64 66 96.9
branch 24 24 100.0
condition 1 3 33.3
subroutine 18 19 94.7
pod 9 9 100.0
total 116 121 95.8


line stmt bran cond sub pod time code
1 4     4   112790 use strict;
  4         9  
  4         170  
2 4     4   22 use warnings;
  4         6  
  4         203  
3             package DateTime::Moonpig;
4             {
5             $DateTime::Moonpig::VERSION = '1.03';
6             }
7             # ABSTRACT: a DateTime object with different math
8              
9 4     4   20 use base 'DateTime';
  4         11  
  4         5666  
10 4     4   758569 use Carp qw(confess croak);
  4         10  
  4         337  
11             use overload
12 4         42 '+' => \&plus,
13             '-' => \&minus,
14 4     4   24 ;
  4         8  
15 4     4   259 use Scalar::Util qw(blessed reftype);
  4         8  
  4         195  
16 4     4   23 use Sub::Install ();
  4         9  
  4         75  
17              
18 4     4   3356 use namespace::autoclean;
  4         12327  
  4         25  
19              
20             sub new {
21 13     13 1 35326 my ($base, @arg) = @_;
22 13   33     89 my $class = ref($base) || $base;
23              
24 13 100       44 if (@arg == 1) { return $class->from_epoch( epoch => $arg[0] ) }
  3         20  
25              
26 10         49 my %arg = @arg;
27 10 100       56 $arg{time_zone} = 'UTC' unless exists $arg{time_zone};
28 10         92 bless $class->SUPER::new(%arg) => $class;
29             }
30              
31             sub new_datetime {
32 0     0 1 0 my ($class, $dt) = @_;
33 0         0 bless $dt->clone => $class;
34             }
35              
36             # $a is expected to be epoch seconds
37             sub plus {
38 34     34 1 56051 my ($self, $a) = @_;
39 34         59 my $class = ref($self);
40 34         96 my $a_sec = $class->_to_sec($a);
41 29         134 return $class->from_epoch( epoch => $self->epoch + $a_sec,
42             time_zone => $self->time_zone,
43             );
44             }
45              
46             sub minus {
47 25     25 1 20097 my ($a, $b, $rev) = @_;
48             # if $b is a datetime, the result is an interval
49             # but if $b is an interval, the result is another datetime
50 25 100       112 if (blessed($b)) {
    100          
51 13 100       105 if ($b->can("as_seconds")) {
    100          
52 6 100       241 croak "subtracting a date from a scalar object is forbidden"
53             if $rev;
54 4         16 return $a->plus( - $b->as_seconds );
55             } elsif ($b->can("epoch")) {
56 6 100       21 my $res = ( $a->epoch - $b->epoch ) * ($rev ? -1 : 1);
57 6         86 return $a->interval_factory($res);
58             } else {
59 1         5 croak "Can't subtract X from $a when X has neither 'as_seconds' nor 'epoch' method";
60             }
61             } elsif (ref $b) {
62 1         11 croak "Can't subtract unblessed " . reftype($b) . " reference from $a";
63             } else { # $b is a number
64 11 100       533 croak "subtracting a date from a number is forbidden"
65             if $rev;
66 8         28 return $a + (-$b);
67             }
68             }
69              
70             sub number_of_days_in_month {
71 1     1 1 516 my ($self) = @_;
72 1         7 return (ref $self)
73             ->last_day_of_month(year => $self->year, month => $self->month)
74             ->day;
75             }
76              
77             for my $mutator (qw(
78             add_duration subtract_duration
79             truncate
80             set
81             _year _month _day _hour _minute _second _nanosecond
82             )) {
83             (my $method = $mutator) =~ s/^_/set_/;
84             Sub::Install::install_sub({
85 11     11   12489 code => sub { confess "Do not mutate DateTime objects! (http://rjbs.manxome.org/rubric/entry/1929)" },
86             as => $method,
87             });
88             }
89              
90 6     6 1 22 sub interval_factory { return $_[1] }
91              
92             sub _to_sec {
93 34     34   48 my ($self, $a) = @_;
94 34 100       75 if (ref($a)) {
95 10 100       66 if (blessed($a)) {
96 9 100       71 if ($a->can('as_seconds')) {
97 5         24 return $a->as_seconds;
98             } else {
99 4         510 croak "Can't add $self to object with no 'as_seconds' method";
100             }
101             } else {
102 1         156 croak "Can't add $self to unblessed " . reftype($a) . " reference";
103             }
104             } else {
105 24         48 return $a;
106             }
107             }
108              
109             sub precedes {
110 7     7 1 5144 my ($self, $d) = @_;
111 7         26 return $self->compare($d) < 0;
112             }
113              
114             sub follows {
115 7     7 1 2707 my ($self, $d) = @_;
116 7         27 return $self->compare($d) > 0;
117             }
118              
119             sub st {
120 12     12 1 2936 my ($self) = @_;
121 12         48 join q{ }, $self->ymd('-'), $self->hms(':');
122             }
123              
124             =head1 NAME
125              
126             DateTime::Moonpig - Saner interface to C
127              
128             =head1 SYNOPSIS
129              
130             $birthday = DateTime::Moonpig->new( year => 1969,
131             month => 4,
132             day => 2,
133             hour => 2,
134             minute => 38,
135             );
136             $now = DateTime::Moonpig->new( time() );
137              
138             printf "%d\n", $now - $birthday; # returns number of seconds difference
139              
140             $later = $now + 60; # one minute later
141             $earlier = $now - 2*3600; # two hours earlier
142              
143             if ($now->follows($birthday)) { ... } # true
144             if ($birthday->precedes($now)) { ... } # also true
145              
146             =head1 DESCRIPTION
147              
148             C is a thin wrapper around the L module
149             to fix problems with that module's design and interface. The main
150             points are:
151              
152             =over 4
153              
154             =item *
155              
156             Methods for mutating C objects in place have been
157             overridden to throw a fatal exception. These include C
158             and C, C* methods such as C, and
159             C.
160              
161             =item *
162              
163             The addition and subtraction operators have been overridden.
164              
165             Adding a C to an integer I returns a new
166             C equal to a time I seconds later than the
167             original. Similarly, subtracting I returns a new C equal to a
168             time I seconds earlier than the original.
169              
170             Subtracting two Cs returns the number of seconds elapsed between
171             them. It does not return an object of any kind.
172              
173             =item *
174              
175             The C method can be called with a single argument, which is
176             interpreted as a Unix epoch time, such as is returned by Perl's
177             built-in C function.
178              
179             =item *
180              
181             A few convenient methods have been added
182              
183             =back
184              
185             =head2 CHANGES TO C METHODS
186              
187             =head3 C
188              
189             C is just like C, except:
190              
191             =over 4
192              
193             =item * The call
194              
195             DateTime::Moonpig->new( $n )
196              
197             is shorthand for
198              
199             DateTime::Moonpig->from_epoch( epoch => $n )
200              
201              
202             =item *
203              
204             If no C argument is specified, the returned object will be
205             created in the C time zone. C creates objects in its
206             "floating" time zone by default. Such objects can be created via
207              
208             DateTime::Moonpig->new( time_zone => "floating", ... );
209              
210             if you think that's what you really want. I advise against it because
211             a C object without an attached time zone has no definite
212             meaning. It seems to refer to a particular time, but when pressed to
213             say what time it refers to, you can't.
214              
215             =item *
216              
217             C can be called on a C object, which is then ignored. So for
218             example if C<$dtm> is any C object, then these two calls are
219             equivalent:
220              
221             $dtm->new( ... );
222             DateTime::Moonpig->new( ... );
223              
224             =back
225              
226             =head3 Mutators are fatal errors
227              
228             The following C methods will throw an exception if called:
229              
230             add_duration
231             subtract_duration
232              
233             truncate
234              
235             set
236              
237             set_year
238             set_month
239             set_day
240             set_hour
241             set_minute
242             set_second
243             set_nanosecond
244              
245             Rik has a sad story about why these are a bad idea:
246             L
247             (Summary: B.)
248              
249             The following mutators don't actually mutate the time value, and are allowed:
250              
251             set_time_zone
252             set_locale
253             set_formatter
254              
255             The behavior of C is complicated by the C
256             module's handling of time zone changes. It is possible to mutate a
257             time by setting its time zone to "floating" and then setting it again.
258             The normal behavior of C, to preserve the I time
259             represented by the object, is bypassed if you do this.
260              
261             =head2 OVERLOADING
262              
263             The overloading of all operators, except C<+> and C<->, is inherited
264             from C.
265              
266             =head3 Summary
267              
268             The C<+> and C<-> operators behave as follows:
269              
270             =over 4
271              
272             =item *
273              
274             You can add a
275             C to a scalar, which will be interpreted as a number of seconds to
276             move forward in time. (Or backward, if negative.)
277              
278             =item *
279              
280             You can similarly subtract a scalar from a C. Subtracting a
281             C from a scalar is a fatal error.
282              
283             =item *
284              
285             You can subtract a C from another date object, such as another
286             C, or vice versa. The result is the number of seconds between the
287             times represented by the two objects.
288              
289             =item *
290              
291             An object will be treated like a scalar if it implements an
292             C method; it will be treated like a date object if it
293             implements an C method.
294              
295             =back
296              
297             =head3 Full details
298              
299             You can add a number to a C object, or subtract a number from a C
300             object; the number will be interpreted as a number of seconds to add
301             or subtract:
302              
303             # 1969-04-02 02:38:00
304             $birthday = DateTime::Moonpig->new( year => 1969,
305             month => 4,
306             day => 2,
307             hour => 2,
308             minute => 38,
309             second => 0,
310             );
311              
312             $x0 = $birthday + 10; # 1969-04-02 02:38:10
313             $x1 = $birthday - 10; # 1969-04-02 02:37:50
314             $x2 = $birthday + (-10); # 1969-04-02 02:37:50
315              
316             $x3 = $birthday + 100; # 1969-04-02 02:39:40
317             $x4 = $birthday - 100; # 1969-04-02 02:36:20
318              
319             # identical to $birthday + 100
320             $x5 = 100 + $birthday; # 1969-04-02 02:39:40
321              
322             # forbidden
323             $x6 = 100 - $birthday; # croaks
324              
325             # handy technique
326             sub hours { $_[0} * 3600 }
327             $x7 = $birthday + hours(12); # 1969-04-02 14:38:00
328             $x8 = $birthday - hours(12); # 1969-04-01 14:38:00
329              
330             C<$birthday> is I modified by any of this. The resulting objects will be in the same time zone as the original object, in this case UTC.
331              
332             You can add any object to a C object if the other object supports an
333             C method. C and C objects do I provide this method.
334              
335             package MyDaysInterval; # Silly example
336             sub new {
337             my ($class, $days) = @_;
338             bless { days => $days } => $class;
339             }
340              
341             sub as_seconds { $_[0]{days} * 86400 }
342              
343             package main;
344              
345             my $three_days = MyDaysInterval->new(3);
346              
347             $y0 = $birthday + $three_days; # 1969-04-05 02:38:00
348              
349             # forbidden
350             $y1 = $birthday + DateTime->new(...); # croaks
351             $y2 = $birthday + $birthday; # croaks
352              
353             Again, C<$birthday> is not modified by any of this arithmetic.
354              
355             You can subtract any object I a C object, but
356             not vice versa, if that object provides an C method. It
357             will be interpreted as a time interval, and the result will be a new
358             C object:
359              
360             $z2 = $birthday - $three_days; # 1969-03-30 02:38:00
361              
362             # forbidden
363             $z3 = $three_days - $birthday; # croaks
364              
365             If you have another object that represents a time, and that implements
366             an C method that returns its value as seconds since the Unix
367             epoch, you may subtract it from a C object or vice
368             versa. The result is the number of seconds between the second and the
369             first operands. Since C implements C, you
370             can subtract one C object from another to get the
371             number of seconds difference between them:
372              
373             $x0 = $birthday + 10; # 1969-04-02 02:38:10
374              
375             $z4 = $x0 - $birthday; # 10
376             $z5 = $birthday - $x0; # -10
377              
378             package Feb13; # Silly example
379             sub new {
380             my ($class) = @_;
381             bless [ "DUMMY" ] => $class;
382             }
383             sub epoch { return 1234567890 } # Feb 13 23:31:30 2009 UTC
384              
385             package main;
386              
387             my $feb13 = Feb13->new();
388              
389             $feb13_dt = DateTime->new( year => 2009,
390             month => 2,
391             day => 13,
392             hour => 23,
393             minute => 31,
394             second => 30,
395             time_zone => "UTC",
396             );
397              
398             $z6 = $birthday - $feb13; # -1258232010
399             $z7 = $birthday - $feb13_dt; # -1258232010
400             $z8 = $feb13 - $birthday; # 1258232010
401              
402             # WATCH OUT - will NOT return 1258232010
403             $z9 = $feb13_dt - $birthday; # returns a DateTime::Duration object
404              
405             In this last example, C's overloading is respected, rather than
406             C's, and we get back a C object that represents
407             the elapsed difference of 40-some years. Sorry, can't fix that; it's determined by Perl, which has to decide which of the two conflicting definitions of C<-> to honor, and chooses the other one.
408              
409             None of these subtractions will modify any of the argument objects.
410              
411             =head3 C
412              
413             When two time objects are subtracted, the result is normally a number.
414             However, the numeric difference is first passed to the target object's
415             C method, which has the option to transform it and
416             return an object (or something else) instead. The default
417             C returns its argument unchanged. So for example,
418              
419             $z0 = $x0 - $birthday; # 10
420              
421             is actually returning the result of C<< $x0->interval_factory(10) >>, which is 10.
422              
423             =head3 Absolute time, not calendar time
424              
425             C C and C always do real-time calculations, never civil
426             calendar calculations. If your locality began observing daylight
427             savings on 2007-03-11, as most of the USA did, then:
428              
429             $a_day = DateTime::Moonpig->new( year => 2007,
430             month => 3,
431             day => 11,
432             hour => 1,
433             minute => 0,
434             second => 0,
435             time_zone => "America/New_York",
436             );
437             $next_day = $a_day->plus(24*3600);
438              
439             At this point C<$next_day> is exactly 24E3600 seconds ahead
440             of C<$a_day>. Because the civil calendar day for 2007-03-11 in New
441             York was only 23 hours long, C<$next_day> represents represents
442             2007-03-12 02:00:00 instead of 2007-03-12 01:00:00. This should be what you
443             expect; if not please correct your expectation.
444              
445             =head2 NEW METHODS
446              
447             =head3 C
448              
449             C<< DateTime::Moonpig->new_datetime( $dt ) >> takes a C object and
450             returns an equivalent C object.
451              
452             =head3 C, C
453              
454             These methods implement the overloading for the C<+> and C<->
455             operators as per L<"OVERLOADING"> above. See the L man
456             page for fuller details.
457              
458             =head3 C, C
459              
460             $a->precedes($b)
461             $a->follows($b)
462              
463             return true if time C<$a> is strictly earlier than time C<$b>, or
464             strictly later than time C<$b>, respectively. If C<$a> and C<$b>
465             represent the same time, both methods will return false. At most one will be
466             true for a given pair of dates. They are implemented as
467             calls to C.
468              
469             =head3 C
470              
471             Return a string representing the target time in the format
472              
473             1969-04-02 02:38:00
474              
475             This is convenient and readable, but does not comply with ISO 8601.
476             It also omits the time zone, so beware.
477              
478             The name C is short for "string".
479              
480             =head3 C
481              
482             This method takes no argument and returns the number of days in the
483             month it represents. For example:
484              
485             DateTime::Moonpig->new( year => 1969,
486             month => 4,
487             day => 2,
488             )
489             ->number_of_days_in_month()
490              
491             returns 30.
492              
493             =head3 C
494              
495             Used internally for manufacturing objects that represent time
496             intervals. See the description of the C<-> operator under
497             L<"OVERLOADING">, above.
498              
499             =head1 BUGS
500              
501             Please submit bug reports at
502             L.
503              
504             Please *do not* submit bug reports at C.
505              
506             =head1 LICENSE
507              
508             Copyright E 2010 IC Group, Inc.
509              
510             This is free software; you can redistribute it and/or modify it under
511             the same terms as the Perl 5 programming language system itself.
512              
513             See the C file for a full statement of your rights under this
514             license.
515              
516             =head1 AUTHOR
517              
518             Mark Jason DOMINUS, C
519              
520             Ricardo SIGNES, C
521              
522             =head2 WUT
523              
524             C was originally part of the I project,
525             where it was used successfully for several years before this CPAN
526             release. For more complete details, see:
527              
528             =over 4
529              
530             =item *
531              
532             L - Long blog article on the design and development of Moonpig generally.
533              
534             =item *
535              
536             L - Slides and other materials
537             from a one-hour talk about Moonpig.
538              
539             =item *
540              
541             L - Perl 2013 Advent
542             Calendar article introducing this module and complaining about
543             C.
544              
545             =back
546              
547             =cut
548              
549              
550             1;