use
5.008004;
*__t
= \
&DateTime::Fiction::JRRTolkien::Shire::Types::t
;
fallback
=> 1,
'+'
=>
'_add_overload'
,
'-'
=>
'_subtract_overload'
,
'*'
=>
'_multiply_overload'
,
'<=>'
=>
'_compare_overload'
,
'cmp'
=>
'_compare_overload'
,
;
our
$VERSION
=
'0.903_01'
;
{
my
$validate
= Params::ValidationCompiler::validation_for(
name
=>
'_validation_for_new'
,
name_is_optional
=> 1,
params
=> {
years
=> {
type
=> __t(
'IntOrUndef'
) },
months
=> {
type
=> __t(
'IntOrUndef'
) },
weeks
=> {
type
=> __t(
'IntOrUndef'
) },
},
);
sub
new {
my
(
$class
,
%arg
) =
@_
;
$validate
->(
years
=>
$arg
{years},
months
=>
$arg
{months},
weeks
=>
$arg
{weeks},
);
$arg
{
$_
} ||= 0
foreach
qw{ years months weeks days }
;
my
$default_mode
;
(
my
$mode_specified
=
$arg
{end_of_month} ||
$arg
{holiday} )
or
$default_mode
= _compute_default_mode( \
%arg
);
my
$years
=
delete
$arg
{years};
my
$weeks
=
delete
$arg
{weeks};
if
(
defined
$arg
{holiday} ) {
defined
$arg
{end_of_month}
and Carp::croak(
q<You may not specify both end_of_month and holiday>
);
$arg
{end_of_month} = _map_holiday_mode(
delete
$arg
{holiday} );
}
defined
$arg
{end_of_month}
or
$arg
{end_of_month} =
$default_mode
;
return
bless
{
duration
=> DateTime::Duration->new(
%arg
),
mode_specified
=>
$mode_specified
,
weeks
=>
$weeks
,
years
=>
$years
,
},
ref
$class
||
$class
;
}
}
sub
add {
my
(
$self
,
@arg
) =
@_
;
return
$self
->add_duration( _make_duration(
@arg
) );
}
sub
add_duration {
my
(
$self
,
$dur
) =
@_
;
if
( _isa(
$dur
, __PACKAGE__ ) ) {
$self
->{weeks} +=
$dur
->{weeks};
$self
->{duration}->add_duration(
$dur
->{duration} );
}
elsif
( _isa(
$dur
,
'DateTime::Duration'
) ) {
$self
->{duration}->add_duration(
$dur
);
}
else
{
Carp::croak(
"Can not do arithmetic on $dur"
);
}
return
$self
;
}
sub
calendar_duration {
my
(
$self
) =
@_
;
return
$self
->new(
years
=>
$self
->delta_years(),
months
=>
$self
->delta_months(),
weeks
=>
$self
->delta_weeks(),
days
=>
$self
->delta_days(),
end_of_month
=>
$self
->end_of_month_mode(),
);
}
sub
clock_duration {
my
(
$self
) =
@_
;
return
$self
->new(
minutes
=>
$self
->delta_minutes(),
seconds
=>
$self
->delta_seconds(),
nanoseconds
=>
$self
->delta_nanoseconds(),
end_of_month
=>
$self
->end_of_month_mode(),
);
}
sub
clone {
my
(
$self
) =
@_
;
my
%clone
= %{
$self
};
$clone
{duration} =
$self
->{duration}->clone();
return
bless
\
%clone
,
ref
$self
;
}
sub
compare {
my
(
undef
,
$left
,
$right
,
$base
) =
@_
;
$base
||= DateTime::Fiction::JRRTolkien::Shire->now();
return
DateTime::Fiction::JRRTolkien::Shire->compare(
$base
->clone()->add_duration(
$left
),
$base
->clone()->add_duration(
$right
),
);
}
sub
delta_weeks {
my
(
$self
) =
@_
;
return
$self
->{weeks};
}
sub
delta_years {
my
(
$self
) =
@_
;
return
$self
->{years};
}
foreach
my
$method
(
qw{
delta_months delta_days delta_minutes delta_seconds
delta_nanoseconds
end_of_month_mode is_wrap_mode is_limit_mode is_preserve_mode
months days hours minutes seconds nanoseconds
}
) {
no
strict
qw{ refs }
;
*$method
=
sub
{
return
$_
[0]->{duration}->
$method
() };
}
sub
is_forward_mode {
return
$_
[0]->is_wrap_mode() ? 1 : 0 }
sub
is_backward_mode {
return
$_
[0]->is_wrap_mode() ? 0 : 1 }
sub
holiday_mode {
return
(
qw{ backward forward }
)[
$_
[0]->is_forward_mode() ] }
sub
deltas {
my
(
$self
) =
@_
;
return
(
$self
->{duration}->deltas(),
weeks
=>
$self
->{weeks},
years
=>
$self
->{years},
);
}
{
my
%on_side
=
map
{
$_
=> 1 }
qw{ years weeks }
;
sub
in_units {
my
(
$self
,
@units
) =
@_
;
my
@rslt
=
$self
->{duration}->in_units(
@units
);
foreach
my
$inx
( 0 ..
$#units
) {
$on_side
{
$units
[
$inx
]}
and
$rslt
[
$inx
] =
$self
->{
$units
[
$inx
]};
}
return
wantarray
?
@rslt
:
$rslt
[0];
}
}
sub
inverse {
my
(
$self
,
%arg
) =
@_
;
if
(
$arg
{holiday} ) {
$arg
{end_of_month} = _map_holiday_mode(
delete
$arg
{holiday} );
}
elsif
(
$arg
{end_of_month} ) {
}
elsif
(
$self
->{mode_specified} ) {
$arg
{end_of_month} =
$self
->end_of_month_mode();
}
else
{
my
%delta
=
$self
->deltas();
$arg
{end_of_month} = _compute_default_mode( \
%delta
, 1 );
}
my
%inverse
= %{
$self
};
$inverse
{weeks}
and
$inverse
{weeks} *= -1;
$inverse
{years}
and
$inverse
{years} *= -1;
$inverse
{duration} =
$self
->{duration}->inverse(
%arg
);
return
bless
\
%inverse
,
ref
$self
;
}
sub
is_negative {
my
(
$self
) =
@_
;
$self
->{weeks} > 0
and
return
0;
$self
->{years} > 0
and
return
0;
(
$self
->{weeks} ||
$self
->{years} )
and
return
$self
->{duration}->is_negative() ? 1 : 0;
return
$self
->{duration}->is_negative();
}
sub
is_positive {
my
(
$self
) =
@_
;
$self
->{weeks} < 0
and
return
0;
$self
->{years} < 0
and
return
0;
(
$self
->{weeks} ||
$self
->{years} )
and
return
$self
->{duration}->is_positive() ? 1 : 0;
return
$self
->{duration}->is_positive();
}
sub
is_zero {
my
(
$self
) =
@_
;
return
(
$self
->{duration}->is_zero() && 0 ==
$self
->{weeks} &&
$self
->{years} == 0 ) ? 1 : 0;
}
sub
multiply {
my
(
$self
,
$multiplier
) =
@_
;
$self
->{weeks} *=
$multiplier
;
$self
->{years} *=
$multiplier
;
$self
->{duration}->multiply(
$multiplier
);
return
$self
;
}
sub
subtract {
my
(
$self
,
@arg
) =
@_
;
return
$self
->subtract_duration( _make_duration(
@arg
) );
}
sub
subtract_duration {
my
(
$self
,
$dur
) =
@_
;
return
$self
->add_duration(
$dur
->inverse() );
}
sub
weeks {
my
(
$self
) =
@_
;
return
abs
$self
->{weeks};
}
sub
years {
my
(
$self
) =
@_
;
return
abs
$self
->{years};
}
sub
_add_overload {
my
(
$left
,
$right
,
$reverse
) =
@_
;
$reverse
and (
$left
,
$right
) = (
$right
,
$left
);
_isa(
$right
,
'DateTime::Fiction::JRRTolkien::Shire'
)
and
return
$right
->clone()->add_duration(
$left
);
return
$left
->clone()->add_duration(
$right
);
}
sub
_compare_overload {
Carp::croak(
'DateTime::Fiction::JRRTolkien::Shire::Duration does not overload comparison'
);
}
sub
_compute_default_mode {
my
(
$arg
,
$invert
) =
@_
;
my
$inx
= (
$arg
->{years} * 365 +
$arg
->{months} * 30 +
$arg
->{weeks} * 7 ) >= 0 ? 1 : 0;
$invert
and
$inx
= 1 -
$inx
;
return
(
qw{ preserve wrap }
)[
$inx
];
}
sub
_isa {
my
(
$obj
,
$class
) =
@_
;
return
Scalar::Util::blessed(
$obj
) &&
$obj
->isa(
$class
);
}
sub
_make_duration {
my
@arg
=
@_
;
if
( 1 ==
@arg
&& Scalar::Util::blessed(
$arg
[0] ) ) {
$arg
[0]->isa( __PACKAGE__ )
and
return
$arg
[0];
$arg
[0]->isa(
'DateTime::Duration'
)
and
return
__PACKAGE__->new(
$arg
[0]->deltas() );
}
return
__PACKAGE__->new(
@arg
);
}
{
my
%mode
= (
forward
=>
'wrap'
,
backward
=>
'preserve'
,
);
sub
_map_holiday_mode {
my
(
$m
) =
@_
;
my
$rslt
=
$mode
{
$m
}
or Carp::croak(
"Invalid holiday mode '$m'"
);
return
$rslt
;
}
}
sub
_multiply_overload {
my
(
$left
,
$right
) =
@_
;
return
$left
->clone()->multiply(
$right
);
}
sub
_subtract_overload {
my
(
$left
,
$right
,
$reverse
) =
@_
;
$reverse
and (
$left
,
$right
) = (
$right
,
$left
);
_isa(
$right
,
'DateTime::Fiction::JRRTolkien::Shire'
)
and Carp::croak(
'Can not subtract a DateTime::Fiction::JRRTolkien::Shire from a DateTime::Fiction::JRRTolkien::Shire::Duration'
);
return
$left
->clone()->subtract_duration(
$right
);
}
1;