NAME

Date::Set - Date set math

SYNOPSIS

NOTE: The API is VERY unstable. Please read the POD before upgrading from an earlier version.

use Date::Set;

my $interval = Date::Set->new('20010501')->as_months();
 print "This month: ". $interval. "\n\n";

    $interval = $interval->as_weeks;
print "Weeks this month: ". $interval."\n\n";

    #Offset syntax is subject to change. (as is everything else for now ;)
    $interval->offset( mode => 'begin', unit=>'days', value => [ 2, 3] );
 print "Tuesdays this month: ". $interval . "\n\n";

# TODO: add some examples of RRULE syntax.
#
 

DESCRIPTION

Date::Set is a module for date/time sets. It allows you to generate groups of dates, like "every Wednesday", and then find all the dates matching that pattern. It waits until you ask for a particular recurrence before calculating it.

If you want to understand the context of this module, look at IETF RFC 2445 (iCalendar), which specifies a particular syntax for describing recurring events.

It requires Date::ICal and Set::Infinite. If you don't need iCalendar functionality, use Set::Infinite instead.

METHODS

event

event( start =>$date , end => $date, default => 'full' );

Constructor. By default, with no arguments, returns a Date::Set that, by default, encompasses 'forever', that is: (-Inf .. Inf).

Takes a param hash with several optional parameters:

 default => ('empty'||'full')
         If you specify a default of 'full', the returned object encompasses all of time.
         If you specify a default of 'empty', the returned object encompasses no time at all.
         Current module behavior always defaults to 'full'.
         TODO: see if there are cases where we EVER want to use empty

start => Date::ICal

         If you specify a 'start' parameter, the returned set will be bounded to start on that date

end => Date::ICal

         If you specify an 'end' parameter, the returned set will be bounded to end on that date

 TODO: we have no idea what happens if you set default to 'empty' and also specify a start and/or end date.

print

TODO: We think this is an internal debugging method. but if so, it should be called _debug or at least _print. it gets used frequently in the code which makes it appear to be more critical than we think it is. --jesse & srl.

Otherwise, it should get proper documentation and be public. but if so, what would it do?

period

period( time => [time1, time2] )

or

period( start => Date::ICal,  end => Date::ICal )

This routine is a constructor. Returns an "empty" time period bounded by the dates specified when called in a scalar context.

dtstart

dtstart( start => time1 )

Returns set intersection [time1 .. Inf)

'dtstart' puts a limit on when the event starts. If the event already starts AFTER dtstart, it will not change.

dtend

dtend( end => time1 )

Returns set intersection (Inf .. time1]

'dtend' puts a limit on when the event finishes. If the event already finish BEFORE dtend, it will not change.

duration

duration( unit => 'months', duration => 10 )

All intervals are modified to 'duration'.

'unit' parameter can be years, months, days, weeks, hours, minutes, or seconds.

recur_by_rule

recur_by_rule ( period => date-set,  DTSTART => time,
    BYMONTH => [ list ],     BYWEEKNO => [ list ],
    BYYEARDAY => [ list ],   BYMONTHDAY => [ list ],
    BYDAY => [ list ],       BYHOUR => [ list ],
    BYMINUTE => [ list ],    BYSECOND => [ list ],
    BYSETPOS => [ list ],
    UNTIL => time, FREQ => freq, INTERVAL => n, COUNT => n,
    WKST => day,
    RRULE => rrule-string,
    include_dtstart => 1 )

All parameters may be upper or lower case.

Implements RRULE from RFC2445.

FREQ can be: SECONDLY MINUTELY HOURLY DAILY WEEKLY MONTHLY or YEARLY

WKST and BYDAY list may contain: SU MO TU WE TH FR SA. By default, weeks start on monday (MO)

BYxxx items must be array references (must be bracketed) if the list has more than one item:

BYMOHTH => 10                 or
BYMONTH => [ 10 ]             or
BYMONTH => [ 10, 11, 12 ]     or 
BYMONTH => [ qw(10 11 12) ]

but NOT:

BYMONTH => 10, 11, 12       #  NOT!

DTSTART value can be given explicitly, otherwise it will be taken from 'period' or from the set.

NOTE: "DTSTART" is *ALWAYS* included in the recurrence set, whether or not it matches the rule. Use "include_dtstart => 0" to override this.

NOTE: Some recurrences may give very big or even infinity sized sets. The currenct implementation does not detect some of these cases and they might crash your system.

NOTE: The RFC specifies that FREQ is *not* optional.

There are two operating modes: without 'period' it will filter out the rule from the set; with 'period' it will filter out the rule from the period, then add the list to the set.

The datatype for 'period' is Date-Set.

exclude_by_rule

exclude_by_rule ( period => date-set, DTSTART => time,
    BYMONTH => [ list ],     BYWEEKNO => [ list ],
    BYYEARDAY => [ list ],   BYMONTHDAY => [ list ],
    BYDAY => [ list ],       BYHOUR => [ list ],
    BYMINUTE => [ list ],    BYSECOND => [ list ],
    BYSETPOS => [ list ],
    UNTIL => time, FREQ => freq, INTERVAL => n, COUNT => n,
    WKST => day )

Implements EXRULE (exclusion-rule) from RFC2445.

'period' is optional.

recur_by_date

recur_by_date( list => [time1, time2, ...] )

Adds the (scalar) list to the set, or creates a new list.

This Date::Set will recur on each item of the list provided. This method lets you add items to a set of dates. If you call it multiple times, entries from previous calls will be preserved. If you need to delete them again, use exclude_by_date.

exclude_by_date

exclude_by_date( list => [time1, time2, ...] )

Removes each element of the list from the set.

occurrences

occurrences( period => date-set )

Returns the occurrences for a given period. In other words, "when does this event occur during the given period?"

next_year, next_month, next_week, next_day, next_hour, next_minute, next_day ($date_set)

this_year, this_month, this_week, this_day, this_hour, this_minute, this_day ($date_set)

prev_year, prev_month, prev_week, prev_day, prev_hour, prev_minute, prev_day ($date_set)

next_month( date-set ) 
this_year ( date-set )    # [20010101..20020101)

Returns the next/prev/this unit of time for a given period.

It answers questions like, "when is next month for the given period?", "which years are covered by this period?"

TODO: explain this and give more examples, cookbook-style.

as_years, as_months, as_weeks, as_days, as_hours, as_minutes, as_days ($date_set)

as_months( date-set ) 
as_weeks ( date-set ) 

Returns the given period in a 'unit of time' form.

It answers questions like, "which months we have in this period?", "which years are covered by this period?"

TODO: explain this and give more examples, cookbook-style.

NEW API

Not all of this has been properly implemented or solidified yet. We're assuming Date::Set deals with ICal dates. This still feels a bit weird

Date::Set->new($arg)

Creates a new Date::Set.

$arg can be a string, another Date::Set::ICal object, a Date::ICal object, or a Set::Infinite::Element_Inf object.

start_date ([$date])

sets or gets the starting date(/time?) of the set. If called with no argument, gets the current value; otherwise, sets it.

end_date ([$date])

sets or gets the ending date(/time?) of the set. If called with no argument, gets the current value; otherwise, sets it.

duration (duration_before, duration_after ?)

period (= start_date + end_date, or start_date + duration_after, or duration_before + end_date)

dates_by_rule

date_by_rule or include_dates

huh?

include_period (=union) (syntactic sugar for Set::Infinite::union)

exclude_period (=complement) (syntactic sugar for Set::Infinite::complement)

overlapping_periods_with ($set)

(syntactic sugar for Set::Infinite::intersection) 

Returns a Date::Set of the overlaps between $self and another Date::Set.

This can be thought of as "conflicting periods with" or "common periods with", depending on the scheduling application. Free/busy times are more easily thought of as "common periods free", where events are more easily thought of as "periods that conflict with one another" if you've overscheduled.

overlaps_with ( $set )

(syntactic sugar for Set::Infinite::intersects) 

Returns true if $self overlaps with $set, a Date::Set. Otherwise returns false.

add_days, add_weeks, add_years ... (=offset)

as_list (=list)

(?) (=iterate)

final_occurrence_before ($date)

$date should be a Date::ICal.
Last occurrence before today; 
returns a single-instance Date::Set ("tuesday, 21 january 2000 from 3pm to 4pm")

rule(...)->before(today)->last; 

first_occurrence_after ($date)

$date should be a Date::ICal.
Next occurrence; 
returns a single-instance Date::Set ("tuesday, 21 january 2000 from 3pm to 4pm")

rule(...)->after(now)->first; 

INHERITED METHODS

These methods are inherited from Set::Infinite.

Logic

$logic = $a->intersects($b);
$logic = $a->contains($b);
$logic = $a->is_null;

Set

$i = $a->union($b);     
$i = $a->intersection($b);
$i = $a->complement;

Note: 'unit' parameter can be years, months, days, weeks, hours, minutes, or seconds.

BUGS

'duration' and 'period' methods may change in future versions, to generate open-ended sets.

AUTHOR

Flavio Soibelmann Glock <fglock@pucrs.br> with the Reefknot team.