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;