NAME

Date::ICal::Duration - durations in iCalendar format, for math purposes.

VERSION

$Revision: 1.61 $

SYNOPSIS

    use Date::ICal::Duration;
    $d = Date::ICal::Duration->new( ical => '-P1W3DT2H3M45S' );
    $d = Date::ICal::Duration->new( weeks => 1, 
                                    days => 1,
                                    hours => 6,
                                    minutes => 15,
                                    seconds => 45); 
    # a one hour duration, without other components
    $d = Date::ICal::Duration->new( seconds => "3600"); 
    # Read-only accessors:
    $d->weeks;
    $d->days;
    $d->hours;
    $d->minutes;
    $d->seconds;
    $d->sign;
    # TODO: Resolve sign() discussion from rk-devel and update synopsis.
    
    $d->as_seconds ();   # returns just seconds
    $d->as_elements ();  # returns a hash of elements, like the accessors above
    $d->as_ical();       # returns an iCalendar duration string
    
=head1 DESCRIPTION

This is a trivial class for representing duration objects, for doing math in Date::ICal

AUTHOR

Rich Bowen, and the Reefknot team (www.reefknot.org)

Last touched by $Author: rbowen $

METHODS

Date::ICal::Duration has the following methods available:

new

A new Date::ICal::Duration object can be created with an iCalendar string :

    my $ical = Date::ICal::Duration->new ( ical => 'P3W2D' );
    # 3 weeks, 2 days, positive direction
    my $ical = Date::ICal::Duration->new ( ical => '-P6H3M30S' );
    # 6 hours, 3 minutes, 30 seconds, negative direction
    
Or with a number of seconds:
    my $ical = Date::ICal::Duration->new ( seconds => "3600" );
    # one hour positive

Or, better still, create it with components

    my $date = Date::ICal::Duration->new ( 
                           weeks => 6, 
                           days => 2, 
                           hours => 7,
                           minutes => 15,
                           seconds => 47,
                           sign => "+"
                           );

The sign defaults to "+", but "+" and "-" are legal values. =cut

#}}}

#{{{ sub new

sub new { my ($class, %args) = @_; my $verified = {}; my $self = {}; bless $self, $class;

    my $seconds_only = 1;    # keep track of whether we were given length in seconds only
    $seconds_only = 0 unless (defined $args{'seconds'}); 
    # If one of the attributes is negative, then they all must be
    # negative. Otherwise, we're not sure what this means.
    foreach (qw(hours minutes seconds days weeks)) {
        if (defined($args{$_}) )   {
            # make sure this argument is all digits, optional - sign
            if ($args{$_} =~ m/-?[0-9]+$/) { 
                if ($args{$_} < 0) {
                    $args{sign} = '-';
                    $args{$_} = abs($args{$_});
                }
                $verified->{$_} = $args{$_};
                unless ($_ eq 'seconds') {
                    $seconds_only = 0;
                }
            } else {
                carp ("Parameter $_ contains non-numeric value " . $args{$_} . "\n");
            }
        }
    }
    if (defined ($args{sign}) ) {
        # make sure this argument + or -
        if ($args{sign} =~ m/[+-]/) {
            # if so, assign it
            $self->{sign} = ($args{sign} eq "+") ? 1 : -1;
            $verified->{sign} = ($args{sign} eq "+") ? '+' : '-';
        } else {
            carp ("Parameter sign contains a value other than + or - : "
                . $args{sign} . "\n");
        }
        
    }
    # If a number is given, convert it to hours, minutes, and seconds,
    # but *don't* extract days -- we want it to represent an absolute
    # amount of time, regardless of timezone
    if ($seconds_only) { # if we were given an integer time_t
        $self->_set_from_seconds($args{'seconds'});
    } elsif (defined ($args{'ical'}) ) {   
        # A standard duration string
        #warn "setting from ical\n";
        $self->_set_from_ical($args{'ical'});
    } elsif (not $seconds_only) {
        #warn "setting from components";
        #use Data::Dumper; warn Dumper $verified;
        $self->_set_from_components($verified);
    }
    
    return undef unless %args;
   
    return $self;
}

#}}}

# Accessors {{{

sign, weeks, days, hours, minutes, seconds

Read-only accessors for the elements of the object.

as_seconds

Returns the duration in raw seconds.

WARNING -- this folds in the number of days, assuming that they are always 86400 seconds long (which is not true twice a year in areas that honor daylight savings time). If you're using this for date arithmetic, consider using the add() method from a Date::ICal object, as this will behave better. Otherwise, you might experience some error when working with times that are specified in a time zone that observes daylight savings time.

as_days

    $days = $duration->as_days;

Returns the duration as a number of days. Not to be confused with the days method, this method returns the total number of days, rather than mod'ing out the complete weeks. Thus, if we have a duration of 33 days, weeks will return 4, days will return 5, but as_days will return 33.

Note that this is a lazy convenience function which is just weeks*7 + days.

as_ical

Return the duration in an iCalendar format value string (e.g., "PT2H0M0S")

as_elements

Returns the duration as a hashref of elements.

INTERNALS

head2 GENERAL MODEL

Internally, we store 3 data values: a number of days, a number of seconds (anything shorter than a day), and a sign (1 or -1). We are assuming that a day is 24 hours for purposes of this module; yes, we know that's not completely accurate because of daylight-savings-time switchovers, but it's mostly correct. Suggestions are welcome.

NOTE: The methods below SHOULD NOT be relied on to stay the same in future versions.

_set_from_ical ($self, $duration_string)

Converts a RFC2445 DURATION format string to the internal storage format.

_parse_ical_string ($string)

Regular expression for parsing iCalendar into usable values.

_set_from_components ($self, $hashref)

Converts from a hashref to the internal storage format. The hashref can contain elements "sign", "weeks", "days", "hours", "minutes", "seconds".

_set_from_ical ($self, $num_seconds)

Sets internal data storage properly if we were only given seconds as a parameter.

$self->_hms();

Return an arrayref to hours, minutes, and second components, or undef if nsecs is undefined. If given an arrayref, computes the new nsecs value for the duration.

$self->_wd()

Return an arrayref to weeks and day components, or undef if ndays is undefined. If Given an arrayref, computs the new ndays value for the duration.