DateTime-Moonpig
view release on metacpan or search on metacpan
lib/DateTime/Moonpig.pm view on Meta::CPAN
use strict;
use warnings;
package DateTime::Moonpig;
{
$DateTime::Moonpig::VERSION = '1.03';
}
# ABSTRACT: a DateTime object with different math
use base 'DateTime';
use Carp qw(confess croak);
use overload
'+' => \&plus,
'-' => \&minus,
;
use Scalar::Util qw(blessed reftype);
use Sub::Install ();
use namespace::autoclean;
sub new {
my ($base, @arg) = @_;
my $class = ref($base) || $base;
if (@arg == 1) { return $class->from_epoch( epoch => $arg[0] ) }
my %arg = @arg;
$arg{time_zone} = 'UTC' unless exists $arg{time_zone};
bless $class->SUPER::new(%arg) => $class;
}
sub new_datetime {
my ($class, $dt) = @_;
bless $dt->clone => $class;
}
# $a is expected to be epoch seconds
sub plus {
my ($self, $a) = @_;
my $class = ref($self);
my $a_sec = $class->_to_sec($a);
return $class->from_epoch( epoch => $self->epoch + $a_sec,
time_zone => $self->time_zone,
);
}
sub minus {
my ($a, $b, $rev) = @_;
# if $b is a datetime, the result is an interval
# but if $b is an interval, the result is another datetime
if (blessed($b)) {
if ($b->can("as_seconds")) {
croak "subtracting a date from a scalar object is forbidden"
if $rev;
return $a->plus( - $b->as_seconds );
} elsif ($b->can("epoch")) {
my $res = ( $a->epoch - $b->epoch ) * ($rev ? -1 : 1);
return $a->interval_factory($res);
} else {
croak "Can't subtract X from $a when X has neither 'as_seconds' nor 'epoch' method";
}
} elsif (ref $b) {
croak "Can't subtract unblessed " . reftype($b) . " reference from $a";
} else { # $b is a number
croak "subtracting a date from a number is forbidden"
if $rev;
return $a + (-$b);
}
}
sub number_of_days_in_month {
my ($self) = @_;
return (ref $self)
->last_day_of_month(year => $self->year, month => $self->month)
->day;
}
for my $mutator (qw(
add_duration subtract_duration
truncate
set
_year _month _day _hour _minute _second _nanosecond
)) {
(my $method = $mutator) =~ s/^_/set_/;
Sub::Install::install_sub({
code => sub { confess "Do not mutate DateTime objects! (http://rjbs.manxome.org/rubric/entry/1929)" },
as => $method,
});
}
sub interval_factory { return $_[1] }
sub _to_sec {
my ($self, $a) = @_;
lib/DateTime/Moonpig.pm view on Meta::CPAN
=item *
You can similarly subtract a scalar from a C<DateTime::Moonpig>. Subtracting a
C<DateTime::Moonpig> from a scalar is a fatal error.
=item *
You can subtract a C<DateTime::Moonpig> from another date object, such as another
C<DateTime::Moonpig>, or vice versa. The result is the number of seconds between the
times represented by the two objects.
=item *
An object will be treated like a scalar if it implements an
C<as_seconds> method; it will be treated like a date object if it
implements an C<epoch> method.
=back
=head3 Full details
You can add a number to a C<DateTime::Moonpig> object, or subtract a number from a C<DateTime::Moonpig>
object; the number will be interpreted as a number of seconds to add
or subtract:
# 1969-04-02 02:38:00
$birthday = DateTime::Moonpig->new( year => 1969,
month => 4,
day => 2,
hour => 2,
minute => 38,
second => 0,
);
$x0 = $birthday + 10; # 1969-04-02 02:38:10
$x1 = $birthday - 10; # 1969-04-02 02:37:50
$x2 = $birthday + (-10); # 1969-04-02 02:37:50
$x3 = $birthday + 100; # 1969-04-02 02:39:40
$x4 = $birthday - 100; # 1969-04-02 02:36:20
# identical to $birthday + 100
$x5 = 100 + $birthday; # 1969-04-02 02:39:40
# forbidden
$x6 = 100 - $birthday; # croaks
# handy technique
sub hours { $_[0} * 3600 }
$x7 = $birthday + hours(12); # 1969-04-02 14:38:00
$x8 = $birthday - hours(12); # 1969-04-01 14:38:00
C<$birthday> is I<never> modified by any of this. The resulting objects will be in the same time zone as the original object, in this case UTC.
You can add any object to a C<DateTime::Moonpig> object if the other object supports an
C<as_seconds> method. C<DateTime> and C<DateTime::Moonpig> objects do I<not> provide this method.
package MyDaysInterval; # Silly example
sub new {
my ($class, $days) = @_;
bless { days => $days } => $class;
}
sub as_seconds { $_[0]{days} * 86400 }
package main;
my $three_days = MyDaysInterval->new(3);
$y0 = $birthday + $three_days; # 1969-04-05 02:38:00
# forbidden
$y1 = $birthday + DateTime->new(...); # croaks
$y2 = $birthday + $birthday; # croaks
Again, C<$birthday> is not modified by any of this arithmetic.
You can subtract any object I<from> a C<DateTime::Moonpig> object, but
not vice versa, if that object provides an C<as_seconds> method. It
will be interpreted as a time interval, and the result will be a new
C<DateTime::Moonpig> object:
$z2 = $birthday - $three_days; # 1969-03-30 02:38:00
# forbidden
$z3 = $three_days - $birthday; # croaks
If you have another object that represents a time, and that implements
an C<epoch> method that returns its value as seconds since the Unix
epoch, you may subtract it from a C<DateTime::Moonpig> object or vice
versa. The result is the number of seconds between the second and the
first operands. Since C<DateTime::Moonpig> implements C<epoch>, you
can subtract one C<DateTime::Moonpig> object from another to get the
number of seconds difference between them:
$x0 = $birthday + 10; # 1969-04-02 02:38:10
$z4 = $x0 - $birthday; # 10
$z5 = $birthday - $x0; # -10
package Feb13; # Silly example
sub new {
my ($class) = @_;
bless [ "DUMMY" ] => $class;
}
sub epoch { return 1234567890 } # Feb 13 23:31:30 2009 UTC
package main;
my $feb13 = Feb13->new();
$feb13_dt = DateTime->new( year => 2009,
month => 2,
day => 13,
hour => 23,
minute => 31,
second => 30,
time_zone => "UTC",
);
$z6 = $birthday - $feb13; # -1258232010
$z7 = $birthday - $feb13_dt; # -1258232010
$z8 = $feb13 - $birthday; # 1258232010
# WATCH OUT - will NOT return 1258232010
$z9 = $feb13_dt - $birthday; # returns a DateTime::Duration object
In this last example, C<DateTime>'s overloading is respected, rather than
C<DateTime::Moonpig>'s, and we get back a C<DateTime::Duration> object that represents
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.
None of these subtractions will modify any of the argument objects.
=head3 C<interval_factory>
When two time objects are subtracted, the result is normally a number.
However, the numeric difference is first passed to the target object's
C<interval_factory> method, which has the option to transform it and
return an object (or something else) instead. The default
C<interval_factory> returns its argument unchanged. So for example,
$z0 = $x0 - $birthday; # 10
is actually returning the result of C<< $x0->interval_factory(10) >>, which is 10.
=head3 Absolute time, not calendar time
C<DateTime::Moonpig> C<plus> and C<minus> always do real-time calculations, never civil
calendar calculations. If your locality began observing daylight
savings on 2007-03-11, as most of the USA did, then:
$a_day = DateTime::Moonpig->new( year => 2007,
month => 3,
day => 11,
hour => 1,
minute => 0,
second => 0,
time_zone => "America/New_York",
);
$next_day = $a_day->plus(24*3600);
At this point C<$next_day> is exactly 24E<middot>3600 seconds ahead
of C<$a_day>. Because the civil calendar day for 2007-03-11 in New
York was only 23 hours long, C<$next_day> represents represents
( run in 0.222 second using v1.01-cache-2.11-cpan-0d8aa00de5b )