DateTime-Lite

 view release on metacpan or  search on metacpan

lib/DateTime/Lite/Duration.pm  view on Meta::CPAN

    {
        $self->{months} += $p{years} * 12;
    }
    if( exists( $p{weeks} ) )
    {
        $self->{days} += $p{weeks} * 7;
    }
    if( exists( $p{hours} ) )
    {
        $self->{minutes} += $p{hours} * 60;
    }

    if( exists( $p{end_of_month} ) )
    {
        unless( $p{end_of_month} =~ /^(?:wrap|limit|preserve)$/ )
        {
            return( $self->error( "Invalid end_of_month mode '$p{end_of_month}'. Must be 'wrap', 'limit', or 'preserve'." ) );
        }
        $self->{end_of_month} = $p{end_of_month};
    }

    $self->_normalise_nanoseconds;
    return( $self );
}

# NOTE: Accessors (absolute values, matching DateTime::Duration API)
sub nanoseconds { abs( ( $_[0]->in_units( 'nanoseconds', 'seconds' ) )[0] ) }
sub seconds     { abs( ( $_[0]->in_units( 'seconds', 'minutes' ) )[0] ) }
sub minutes     { abs( ( $_[0]->in_units( 'minutes', 'hours' ) )[0] ) }
sub hours       { abs( $_[0]->in_units( 'hours' ) ) }
sub days        { abs( ( $_[0]->in_units( 'days', 'weeks' ) )[0] ) }
sub weeks       { abs( $_[0]->in_units( 'weeks' ) ) }
sub months      { abs( ( $_[0]->in_units( 'months', 'years' ) )[0] ) }
sub years       { abs( $_[0]->in_units( 'years' ) ) }

# NOTE: Duration arithmetic
sub add
{
    my $self = shift( @_ );
    return( $self->add_duration( $self->_duration_from_args( @_ ) ) );
}

sub add_duration
{
    my $self = shift( @_ );
    my $dur  = shift( @_ );

    unless( Scalar::Util::blessed( $dur ) && $dur->isa( 'DateTime::Lite::Duration' ) )
    {
        return( $self->error( "Argument to add_duration() must be a DateTime::Lite::Duration object." ) );
    }

    foreach my $unit ( @UNITS )
    {
        $self->{ $unit } += $dur->{ $unit };
    }
    $self->_normalise_nanoseconds;
    return( $self );
}

sub calendar_duration
{
    my $self = shift( @_ );
    return( ref( $self )->new(
        months => $self->{months},
        days   => $self->{days},
        end_of_month => $self->{end_of_month},
    ) );
}

sub clock_duration
{
    my $self = shift( @_ );
    return( ref( $self )->new(
        minutes     => $self->{minutes},
        seconds     => $self->{seconds},
        nanoseconds => $self->{nanoseconds},
    ) );
}

sub clone { bless( { %{ $_[0] } }, ref( $_[0] ) ) }

# NOTE: Class method: comparison
sub compare
{
    my $class = ref( $_[0] ) ? undef : shift( @_ );
    my( $d1, $d2 ) = @_;

    foreach my $unit ( @UNITS )
    {
        my $cmp = $d1->{ $unit } <=> $d2->{ $unit };
        return( $cmp ) if( $cmp );
    }
    return(0)
}

# delta_* return the raw (signed) internal values

sub delta_days        { $_[0]->{days} }
sub delta_minutes     { $_[0]->{minutes} }
sub delta_months      { $_[0]->{months} }
sub delta_nanoseconds { $_[0]->{nanoseconds} }
sub delta_seconds     { $_[0]->{seconds} }

sub deltas
{
    my $self = shift( @_ );
    return( map { $_ => $self->{ $_ } } @UNITS );
}

sub end_of_month_mode { return( $_[0]->{end_of_month} ); }

# Error handling: same pattern as Locale::Unicode / Module::Generic
sub error
{
    my $self = shift( @_ );
    if( @_ )
    {
        require DateTime::Lite::Exception;
        my $msg = join( '', map( ( ref( $_ ) eq 'CODE' ) ? $_->() : $_, @_ ) );
        my $e = DateTime::Lite::Exception->new({

lib/DateTime/Lite/Duration.pm  view on Meta::CPAN

=encoding utf8

=head1 NAME

DateTime::Lite::Duration - Duration objects for use with DateTime::Lite

=head1 SYNOPSIS

    use DateTime::Lite::Duration;

    my $dur = DateTime::Lite::Duration->new(
        years   => 1,
        months  => 6,
        days    => 15,
        hours   => 3,
        minutes => 10,
        seconds => 30,
    ) || die( DateTime::Lite::Duration->error );

    my $dur = DateTime::Lite::Duration->new(
        years       => 1,
        months      => 6,
        weeks       => 2,
        days        => 15,
        hours       => 3,
        minutes     => 10,
        seconds     => 30,
        nanoseconds => 500_000_000,
        end_of_month => 'limit',  # 'wrap' (default), 'limit', 'preserve'
    ) || die( DateTime::Lite::Duration->error );

    # Introspection
    printf( "Years: %d, Months: %d\n", $dur->years, $dur->months );

    # Use with DateTime::Lite
    $dt->add_duration( $dur );
    $dt->subtract_duration( $dur );
    my $diff = $dt1->subtract_datetime( $dt2 );  # returns a Duration

    # Absolute-value accessors (strip larger units)
    $dur->years;         # full years
    $dur->months;        # months after stripping years
    $dur->weeks;         # full weeks
    $dur->days;          # days after stripping weeks
    $dur->hours;         # full hours
    $dur->minutes;       # minutes after stripping hours
    $dur->seconds;       # seconds after stripping minutes
    $dur->nanoseconds;   # nanoseconds after stripping seconds

    # Signed raw delta accessors
    $dur->delta_months;       # signed months (years * 12 + months)
    $dur->delta_days;         # signed days
    $dur->delta_minutes;      # signed minutes (hours * 60 + minutes)
    $dur->delta_seconds;      # signed seconds
    $dur->delta_nanoseconds;  # signed nanoseconds

    # All signed components at once
    my %d = $dur->deltas;  # keys: months days minutes seconds nanoseconds

    # Calendar / clock sub-durations
    my $cal   = $dur->calendar_duration;  # months + days only
    my $clock = $dur->clock_duration;     # minutes + seconds + nanoseconds only

    # Arithmetic on durations
    $dur->add( months => 1, days => 7 );
    $dur->subtract( hours => 2 );
    $dur->add_duration( $other_dur );
    $dur->subtract_duration( $other_dur );

    my $inverse = $dur->inverse;                          # negate all components
    my $neg     = $dur->inverse( end_of_month => 'wrap' );

    # Conversion
    # Express as a combination of requested units:
    my ( $h, $m ) = $dur->in_units( 'hours', 'minutes' );  # e.g. (3, 10)
    my $total_min = $dur->in_units('minutes');             # scalar form

    # Comparison
    my $cmp = DateTime::Lite::Duration->compare( $dur1, $dur2 );  # -1, 0, 1

    # Predicates
    $dur->is_positive;          # true if any component > 0, none < 0
    $dur->is_negative;          # true if any component < 0, none > 0
    $dur->is_zero;              # true if all components are 0
    $dur->is_wrap_mode;         # end_of_month eq 'wrap'
    $dur->is_limit_mode;        # end_of_month eq 'limit'
    $dur->is_preserve_mode;     # end_of_month eq 'preserve'
    $dur->end_of_month_mode;    # 'wrap', 'limit', or 'preserve'

    # Cloning
    my $copy = $dur->clone;

    # Constants
    DateTime::Lite::Duration::MAX_NANOSECONDS();  # 1_000_000_000

    # Error handling
    my $dur2 = DateTime::Lite::Duration->new( %bad_args ) ||
        die( DateTime::Lite::Duration->error );
    $dur->fatal(1);  # make all errors fatal (die instead of warn+return undef)
    my $err = $dur->error;

=head1 VERSION

    v0.1.0

=head1 DESCRIPTION

C<DateTime::Lite::Duration> is a lightweight port of L<DateTime::Duration>, used exclusively with L<DateTime::Lite>. It stores durations in five independent "buckets": C<months>, C<days>, C<minutes>, C<seconds>, and C<nanoseconds>.

The month/day buckets are C<calendar> units, whose real length depends on the date to which the duration is applied. The minute/second/nanosecond buckets are absolute (C<clock>) units.

Unlike L<DateTime>, C<DateTime::Lite> never calls C<die()> unexpectedly.

Errors set an exception object accessible via C<< $dur->error >> and return C<undef> in scalar context, or an empty list in list context. In chaining (object context), it returns a dummy object (C<DateTime::Lite::Null>) to avoid the typical C<Can't c...

=head1 CONSTRUCTOR

=head2 new( %args )

Accepted keys:

=over 4

=item C<years>, C<months>

Calendar time. C<years> is converted to months on construction (1 year = 12 months).

=item C<weeks>, C<days>

Calendar time. C<weeks> is converted to days (1 week = 7 days).

=item C<hours>, C<minutes>

Clock time. C<hours> is converted to minutes (1 hour = 60 minutes).

=item C<seconds>, C<nanoseconds>

Clock time.

=item C<end_of_month>

How to handle month-end arithmetic when adding/subtracting months. One of C<wrap> (default), C<limit>, or C<preserve>.

=back

=head1 METHODS

=head2 Accessors (absolute values)

C<years>, C<months>, C<weeks>, C<days>, C<hours>, C<minutes>, C<seconds>, C<nanoseconds> all return the absolute (unsigned) portion of the duration in the given unit, after stripping larger units. For instance C<months()> returns the months component...

=head2 Signed delta accessors

C<delta_months>, C<delta_days>, C<delta_minutes>, C<delta_seconds>, C<delta_nanoseconds> return the raw signed internal values.

=head2 calendar_duration / clock_duration

Return new duration objects containing only the calendar (months, days) or clock (minutes, seconds, nanoseconds) components respectively.

=head2 clone

Returns a shallow copy.

=head2 compare( $dur1, $dur2 )

Class method. Compares two durations unit by unit. Returns -1, 0, or 1.

=head2 deltas

Returns a hash of all five raw signed values.

=head2 end_of_month_mode

Returns C<wrap>, C<limit>, or C<preserve>.

=head2 in_units( @units )

Returns the duration expressed as a combination of the given units.

For example: C<< $dur->in_units( 'hours', 'minutes' ) >> returns C<(3, 10)> for a 190-minute duration. If only one unit is requested, returns a scalar.

=head2 inverse

    $dur->inverse;
    $dur->inverse( end_of_month => $mode );

Returns a new duration with all components negated. Optionally overrides C<end_of_month>.

=head2 is_negative / is_positive / is_zero

Predicate methods.

=head2 is_wrap_mode / is_limit_mode / is_preserve_mode

Return true if the C<end_of_month> mode matches.

=head2 add

    $dur->add( months => 1, days => 15 );

Adds a duration specified as key-value pairs (same keys as L</new>) to this duration in-place. Returns C<$self>.

=head2 add_duration

    $dur->add_duration( $other_dur );

Adds another L<DateTime::Lite::Duration> object to this duration in-place. Returns C<$self>.

=head2 subtract

    $dur->subtract( days => 1 );

Subtracts a duration specified as key-value pairs from this duration in-place. Equivalent to adding the inverse. Returns C<$self>.

=head2 subtract_duration

    $dur->subtract_duration( $other_dur );



( run in 1.919 second using v1.01-cache-2.11-cpan-39bf76dae61 )