DateTime-Fiction-JRRTolkien-Shire
view release on metacpan or search on metacpan
lib/DateTime/Fiction/JRRTolkien/Shire.pm view on Meta::CPAN
my ( $self, @arg ) = @_;
return $self->add_duration( $self->_make_duration( @arg ) );
}
{
my $validate = Params::ValidationCompiler::validation_for(
name => '_check_add_duration_params',
name_is_optional => 1,
params => [
{ type => __t( 'Duration' ) },
],
);
sub add_duration {
my ( $self, @arg ) = @_;
my ( $dur ) = $validate->( @arg );
return $self->_add_duration( $dur );
}
sub subtract_duration {
my ( $self, @arg ) = @_;
my ( $dur ) = $validate->( @arg );
return $self->_add_duration( $dur->inverse() );
}
}
{
# The _offset arrays are accessed by
# @xx_offset[$self->is_leap_year][$forward][$holiday];
my @month_offset = (
[ # Not a leap year
[ 0, -2, -1, -2, 0, -3, -1 ], # Going backward
[ 0, 1, 3, 2, 0, 1, 2 ], # Going forward
],
[ # A leap year
[ 0, -2, -1, -2, -3, -4, -1 ], # Going backward
[ 0, 1, 4, 3, 2, 1, 2 ], # Going forward
],
);
my @week_offset = ( # Note that we only use indices 3 & 4
[ # Not a leap year
[ 0, 0, 0, -1, 0, 0, 0 ], # Going backward
[ 0, 0, 0, 1, 0, 0, 0 ], # Going forward
],
[ # A leap year
[ 0, 0, 0, -1, -2, 0, 0 ], # Going backward
[ 0, 0, 0, 2, 1, 0, 0 ], # Going forward
],
);
sub _add_duration {
my ( $self, $dur ) = @_;
# simple optimization (cribbed shamelessly from DateTime)
$dur->is_zero()
and return $self;
my %delta = $dur->deltas();
# This bit isn't quite right since DateTime::Infinite::Future -
# infinite duration should NaN (cribbed shamelessly from
# DateTime)
foreach my $val ( values %delta ) {
my $inf;
if ( $val == DateTime->INFINITY ) {
$inf = DateTime::Infinite::Future->new;
}
elsif ( $val == DateTime->NEG_INFINITY ) {
$inf = DateTime::Infinite::Past->new;
}
if ($inf) {
%$self = %$inf;
bless $self, ref $inf;
return $self;
}
}
$self->is_infinite()
and return $self;
if ( $delta{years} || $delta{months} || $delta{weeks} ) {
my $forward = $dur->is_forward_mode();
my $holiday = $self->holiday();
my $leap = $self->is_leap_year();
my $orig_rd = my $shire_rd = ( $self->local_rd_values() )[0] +
GREGORIAN_RATA_DIE_TO_SHIRE;
if ( my $months = delete $delta{months} ) {
$shire_rd +=
$month_offset[$leap][$forward][$holiday];
$holiday = 0; # No further adjustment needed
my ( $year, $day_of_year ) = __rata_die_to_year_day(
$shire_rd );
my ( $month, $day ) = __day_of_year_to_date( $year,
$day_of_year );
$month += $months - 1; # now zero-based
$year += POSIX::floor( $month / 12 );
$leap = __is_leap_year( $year );
$month = 1 + $month % 12; # now one-based again
$day_of_year = __date_to_day_of_year( $year, $month,
$day );
$shire_rd = __year_day_to_rata_die( $year, $day_of_year );
}
if ( my $weeks = delete $delta{weeks} ) {
$shire_rd += $week_offset[$leap][$forward][$holiday];
my ( $year, $day_of_year ) = __rata_die_to_year_day(
$shire_rd );
my ( $month, $day ) = __day_of_year_to_date( $year,
$day_of_year );
my $week = __week_of_year( $month, $day );
my $day_of_week = __day_of_week( $month, $day );
$week += $weeks - 1; # now zero-based
$year += POSIX::floor( $week / 52 );
$leap = __is_leap_year( $year );
$week = $week % 52;
$day_of_year = $week * 7 + $day_of_week;
$week > 25 # Still zero-based, remember
( run in 1.770 second using v1.01-cache-2.11-cpan-56fb94df46f )