NAME
DateTimeX::Seinfeld - Calculate Seinfeld chain length
VERSION
This document describes version 1.000 of DateTimeX::Seinfeld, released January 11, 2014.
SYNOPSIS
use DateTimeX::Seinfeld;
my $seinfeld = DateTimeX::Seinfeld->new(
start_date => $starting_datetime,
increment => { weeks => 1 },
);
my $chains = $seinfeld->find_chains( \@list_of_datetimes );
say "Longest chain: $chains->{longest}{length}";
say "First event in longest chain: $chains->{longest}{start_event}";
say "The current chain may continue"
if $chains->{last}{end_period}
>= $seinfeld->period_containing( DateTime->now );
DESCRIPTION
DateTimeX::Seinfeld calculates the maximum Seinfeld chain length from a sorted list of DateTime objects.
The term "Seinfeld chain" comes from advice attributed to comedian Jerry Seinfeld. He got a large year-on-one-page calendar and marked a big red X on every day he wrote something. The chain of continuous X's gave him a sense of accomplishment and helped motivate him to write every day. (Source: http://lifehacker.com/281626/jerry-seinfelds-productivity-secret)
This module calculates the length of the longest such chain of consecutive days. However, it generalizes the concept; instead of having to do something every day, you can make it every week, or every month, or any other period that can be defined by a DateTime::Duration.
Some definitions: period is the time period during which some event must occur in order to keep the chain from breaking. More than one event may occur in a single period, but the period is only counted once.
ATTRIBUTES
start_date
This is the DateTime (or a hashref acceptable to DateTime->new
) of the beginning of the first period. All events passed to find_chains
must be greater than or equal to this value. (required)
increment
This is the DateTime::Duration (or a hashref acceptable to DateTime::Duration->new
) giving the length of each period. (required)
skip
This is a CodeRef that allows you to skip specified periods. It is called with one argument, the DateTime at which the period begins. If the CodeRef returns a true value, any events taking place during this period are instead considered to take place in the next period. (The CodeRef must not modify the DateTime object it was given.) (optional)
For example, to skip Sundays:
skip => sub { shift->day_of_week == 7 }
Using skip
does not change the start time of the next period (as reported by period_containing
, start_period
, or end_period
). The idea is that events will not normally occur during skipped periods (or you probably shouldn't be skipping them). This means that it is possible for an event to be less than the start time of the period containing it.
METHODS
find_chains
$info = $seinfeld->find_chains( \@events );
$info = $seinfeld->find_chains( \@events, $info ); # continue search
This calculates Seinfeld chains from the events in @events
(an array of DateTime objects which must be sorted in ascending order). Note that you must pass an array reference, not a list.
The return value is a hashref describing the results.
Two keys describe the number of periods. total_periods
is the number of periods between the start_date
and $info->{last}{end_period}
. marked_periods
is the number of periods that contained at least one event. If marked_periods
equals total_periods
, then the events form a single chain of the same length.
Two keys describe the chains: last
(the last chain of events found) and longest
(the longest chain found). These may be the same chain (in which case the values will be references to the same hash). If there are multiple chains of the same length, longest
will be the first such chain. The value of each key is a hashref describing that chain with the following keys:
start_period
-
The DateTime of the start of the period containg the first event of the chain.
end_period
-
The DateTime of the start of the period where the chain broke (i.e. the first period that didn't contain an event). If this is greater than or equal to the period containing the current date (see "period_containing"), then the chain may still be extended. Otherwise, the chain is already broken, and a future event would start a new chain.
start_event
-
The DateTime of the first event in the chain (this is the same object that appeared in
@events
, not a clone). end_event
-
The DateTime of the last event in the chain (again, the same object that appeared in
@events
). length
-
The number of periods in the chain.
num_events
-
The number of events in the chain. This can never be less than
length
, but it can be more (if multiple events occurred in one period).
Note: If @events
is empty, then last
and longest
will not exist in the hash. Otherwise, there will always be at least one chain, even if only of length 1.
If you are monitoring an ongoing sequence of events, it would be wasteful to have to start each search from the first event. Instead, you can pass the hashref returned by the first search to find_chains
, along with just the new events. The hashref you pass will be modified (the same hashref will be returned). To simplify this, it is not necessary that last
and longest
reference the same hash if they are the same chain. If they have the same start_period
, then find_chains
will link them automatically (by setting $info->{longest} = $info->{last}
). When continuing a search, the start_date
is ignored. Instead, the search resumes from $info->{last}{end_period}
.
The only fields that you must supply in order to continue a calculation are start_period
, end_period
, & length
in $info->{last}
, and start_period
& length
in $info->{longest}
. However, any field that you don't supply can't be expected to hold valid data afterwards.
When continuing a calculation, @events
should not include any dates before $info->{last}{end_event}
. If you disregard this rule, any events less than $info->{last}{end_period}
are considered to have occurred in the previous period (even if they actually occurred in an even earlier period).
period_containing
$start = $seinfeld->period_containing( $date );
Returns the DateTime at which the period containing $date
(a DateTime) begins.
Note: If $date
occurs during a period that is skipped, then $start
will be greater than $date
. Otherwise, $start
is always less than or equal to $date
.
DIAGNOSTICS
start_date (%s) must be before first date (%s)
-
You must not pass an event to
find_chains
that occurs before thestart_date
of the first period.
CONFIGURATION AND ENVIRONMENT
DateTimeX::Seinfeld requires no configuration files or environment variables.
DEPENDENCIES
DateTimeX::Seinfeld requires Moose, namespace::autoclean, MooseX::Types::DateTime, MooseX::Types::Moose, and Perl 5.10.0 or later.
INCOMPATIBILITIES
None reported.
BUGS AND LIMITATIONS
No bugs have been reported.
AUTHOR
Christopher J. Madsen <perl AT cjmweb.net>
Please report any bugs or feature requests to <bug-DateTimeX-Seinfeld AT rt.cpan.org>
or through the web interface at http://rt.cpan.org/Public/Bug/Report.html?Queue=DateTimeX-Seinfeld.
You can follow or contribute to DateTimeX-Seinfeld's development at https://github.com/madsen/datetimex-seinfeld.
COPYRIGHT AND LICENSE
This software is copyright (c) 2014 by Christopher J. Madsen.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
DISCLAIMER OF WARRANTY
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENSE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.