Date-Range-1.41/000700 000766 000024 00000000000 11660362503 013473 5ustar00tonystaff000000 000000 Date-Range-1.41/Changes000644 000766 000024 00000001112 11660362462 014777 0ustar00tonystaff000000 000000 Revision history for Perl library Date::Range 1.41 Tue 15 Nov 2011 - fix precedence bug in equals() (Zhen Xie) 1.40 Mon Jan 16 2006 - Doc tweaks 1.3 Tue Sep 6 18:36:34 UTC 2005 - Add reference to Time::Piece::Range 1.2 Wed Nov 19 2003 - add gap() and abuts() methods 1.1 Wed Jul 16 2003 - fix bug with overlapping ranges (Kerrin Pine) 1.0 Tue Feb 25 2003 - abstract Date::Simple code to make it easier to subclass 0.9 2002-03-19 - fixed bug in 'includes' reported by Ed Hiar 0.8 2002-03-07 - added "dates" method 0.01 2001-07-07 - original version Date-Range-1.41/lib/000700 000766 000024 00000000000 11660362503 014241 5ustar00tonystaff000000 000000 Date-Range-1.41/Makefile.PL000644 000766 000024 00000000602 10307360263 015453 0ustar00tonystaff000000 000000 use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( NAME => 'Date::Range', VERSION_FROM => 'lib/Date/Range.pm', ABSTRACT_FROM => 'lib/Date/Range.pm', PREREQ_PM => { 'Date::Simple' => 0.03, 'Test::More' => 0.04, }, AUTHOR => 'Tony Bowden ', ); Date-Range-1.41/MANIFEST000644 000766 000024 00000000164 10362703536 014642 0ustar00tonystaff000000 000000 Changes lib/Date/Range.pm Makefile.PL MANIFEST This list of files META.yml README t/01.t t/pod-coverage.t t/pod.t Date-Range-1.41/META.yml000600 000766 000024 00000001070 11660362503 014744 0ustar00tonystaff000000 000000 --- #YAML:1.0 name: Date-Range version: 1.41 abstract: work with a range of dates author: - Tony Bowden license: unknown distribution_type: module configure_requires: ExtUtils::MakeMaker: 0 build_requires: ExtUtils::MakeMaker: 0 requires: Date::Simple: 0.03 Test::More: 0.04 no_index: directory: - t - inc generated_by: ExtUtils::MakeMaker version 6.56 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 Date-Range-1.41/README000644 000766 000024 00000006437 10362703533 014377 0ustar00tonystaff000000 000000 NAME Date::Range - work with a range of dates SYNOPSIS use Date::Range; my $range = Date::Range->new($date1, $date2); my $earliest = $range->start; my $latest = $range->end; my $days = $range->length; if ($range->includes($date3)) { ... } if ($range->includes($range2)) { ... } if ($range->overlaps($range2)) { my $range3 = $range->overlap($range2); } foreach my $date ($range->dates) { ... } DESCRIPTION Quite often, when dealing with dates, we don't just want to know information about one particular date, but about a range of dates. For example, we may wish to know whether a given date is in a particular range, or what the overlap is between one range and another. This module lets you ask such questions. METHODS new() my $range = Date::Range->new($date1, $date2); A range object is instantiated with two dates, which do not need to be in chronological order (we'll sort all that out internally). These dates must be instances of the correct object. See want_class() below. want_class The class of which we expect the date objects to be objects. By default this is Date::Simple, but this could be any other date class. See Time::Piece::Range for an example of a subclass that uses a different date class. start / end / length my $earliest = $range->start; my $latest = $range->end; my $days = $range->length; These methods allow you retrieve the start and end dates of the range, and the number of days in the range. equals if ($range1->equals($range2)) { } This tells you if two ranges are the same - i.e. start and end at the same dates. includes if ($range->includes($date3)) { ... } if ($range->includes($range2)) { ... } These methods tell you if a given range includes a given date, or a given range. overlaps / overlap if ($range->overlaps($range2)) { my $range3 = $range->overlap($range2); } These methods let you know whether one range overlaps another or not, and access this overlap range. gap my $range3 = $range->gap($range2); This returns a new range representing the gap between two other ranges. abuts if ($range->abuts($range2)) { ... } This tells you whether or not two ranges are contiguous - i.e. there is no gap between them, but they do not overlap. dates foreach my $date ($range->dates) { ... } This returns a list of each date in the range as a Date::Simple object. AUTHOR Tony Bowden, based heavily on Martin Fowler's "Analysis Patterns 2" discussion and code at http://www.martinfowler.com/ap2/range.html BUGS and QUERIES Please direct all correspondence regarding this module to: bug-Date-Range@rt.cpan.org COPYRIGHT AND LICENSE Copyright (C) 2001-2006 Tony Bowden. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Date-Range-1.41/t/000700 000766 000024 00000000000 11660362503 013736 5ustar00tonystaff000000 000000 Date-Range-1.41/t/01.t000644 000766 000024 00000011056 11660362411 014356 0ustar00tonystaff000000 000000 #!/usr/bin/perl -w use strict; use Test::More tests => 56; use Date::Simple; use Date::Range; my $date1 = Date::Simple->new(2000,12,31); my $date2 = $date1->next; my $date3 = $date2->next; eval { my $range = Date::Range->new() }; ok($@, "Can't create a range with no dates"); eval { my $range = Date::Range->new($date1) }; ok($@, "Can't create a range with one date"); eval { my $range = Date::Range->new($date1, $date2, $date3) }; ok($@, "Can't create a range with three dates"); eval { my $range = Date::Range->new("2001-01-01", "2001-02-02") }; ok($@, "Can't create a range with strings"); { ok(my $range = Date::Range->new($date1, $date1), "Create an single day range"); is($range->start, $range->end, "Start and end on same date"); is($range->length, 1, "1 day long"); is($range->dates, 1, "So 1 date in 'dates'"); } ok(my $range1 = Date::Range->new($date1, $date2), "Create a range"); is($range1->start, $date1, "Starts OK"); is($range1->end, $date2, "Starts OK"); is($range1->length, 2, "2 days long"); my @dates = $range1->dates; is(@dates, 2, "So 2 date in 'dates'"); is($dates[0], $range1->start, "Starts at start"); is($dates[1], $range1->end, "And ends at end"); ok(my $range2 = Date::Range->new($date2, $date1), "Create a range in wrong order"); is($range2->start, $date1, "Starts OK"); is($range2->end, $date2, "Starts OK"); is($range2->length, 2, "1 days long"); ok($range1->equals($range2), "Range 1 and 2 are equal"); ok(my $range3 = Date::Range->new($date1, $date3), "Longer Range"); is($range3->length, 3, "3 days long"); ok(!$range3->includes($date1 - 1), "Range doesn't include early day"); ok($range3->includes($date1), "Range includes first day"); ok($range3->includes($date2), "Range includes middle day"); ok($range3->includes($date3), "Range includes last day"); ok(!$range3->includes($date3 + 1), "Range doesn't includes later day"); ok($range3->includes($range1), "Range includes first range"); ok($range3->includes($range2), "Range includes second range"); ok($range3->includes($range3), "Range includes itself"); #------------------------------------------------------------------------- # Test overlaps #------------------------------------------------------------------------- { my $range = Date::Range->new($date3, $date2); ok($range->overlaps($range1), "The ranges overlap the other way"); ok(my $overlap = $range->overlap($range1), "Get that overlap"); is($overlap->start, $date2, "Starts on day2"); is($overlap->end, $date2, "Ends on day2"); } { my $range = Date::Range->new($date2, $date3); ok($range->overlaps($range3), "The ranges overlap"); ok(my $overlap = $range->overlap($range3), "Get that overlap"); is($overlap->start, $date2, "Starts on day2"); is($overlap->end, $date3, "Ends on day3"); } { # ranges overlap my $planrange = Date::Range->new(map Date::Simple->new($_), '2003-03-08', '2003-07-15'); my $billrange = Date::Range->new(map Date::Simple->new($_), '2003-03-21', '2003-04-20'); ok $billrange->overlaps($planrange), "Overlaps one way"; ok $planrange->overlaps($billrange), "and the other..."; } #------------------------------------------------------------------------- # Test Gap / abuts #------------------------------------------------------------------------- { my $jan = Date::Range->new( map Date::Simple->new($_), '2004-01-01', '2004-01-31' ); my $mar = Date::Range->new( map Date::Simple->new($_), '2004-03-01', '2004-03-31' ); my $feb = $jan->gap($mar) or die "Can't get gap"; isa_ok $feb => 'Date::Range'; is $feb->start, "2004-02-01", "Starts start Feb"; is $feb->end, "2004-02-29", "Ends end Feb"; my $feb2 = $mar->gap($jan); ok $feb2->equals($feb), "Gap works either way around"; my $fj = $jan->gap($feb); ok !$jan->gap($feb), "Jan has no gap to Feb"; ok !$feb->gap($mar), "Feb has no gap to Mar"; ok !$jan->abuts($jan), "Abuts J/J - no"; ok $jan->abuts($feb), "Abuts J/F - yes"; ok !$jan->abuts($mar), "Abuts J/M - no"; ok $feb->abuts($jan), "Abuts F/J - yes"; ok !$feb->abuts($feb), "Abuts F/F - no"; ok $feb->abuts($mar), "Abuts F/M - yes"; ok !$mar->abuts($jan), "Abuts M/J - no"; ok $mar->abuts($feb), "Abuts M/F - yes"; ok !$mar->abuts($mar), "Abuts M/M - no"; } #------------------------------------------------------------------------- # equals #------------------------------------------------------------------------- { # regression: ranges with same start date were equal my $jan = Date::Range->new( map Date::Simple->new($_), '2011-01-01', '2011-01-31' ); my $year = Date::Range->new( map Date::Simple->new($_), '2011-01-01', '2011-12-31' ); ok !$jan->equals($year); } Date-Range-1.41/t/pod-coverage.t000644 000766 000024 00000000241 10307360250 016477 0ustar00tonystaff000000 000000 use Test::More; eval "use Test::Pod::Coverage 1.00"; plan skip_all => "Test::Pod::Coverage 1.00 required for testing POD coverage" if $@; all_pod_coverage_ok(); Date-Range-1.41/t/pod.t000644 000766 000024 00000000201 10307360250 014702 0ustar00tonystaff000000 000000 use Test::More; eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; all_pod_files_ok(); Date-Range-1.41/lib/Date/000700 000766 000024 00000000000 11660362503 015116 5ustar00tonystaff000000 000000 Date-Range-1.41/lib/Date/Range.pm000644 000766 000024 00000012556 11660362472 016540 0ustar00tonystaff000000 000000 package Date::Range; =head1 NAME Date::Range - work with a range of dates =head1 SYNOPSIS use Date::Range; my $range = Date::Range->new($date1, $date2); my $earliest = $range->start; my $latest = $range->end; my $days = $range->length; if ($range->includes($date3)) { ... } if ($range->includes($range2)) { ... } if ($range->overlaps($range2)) { my $range3 = $range->overlap($range2); } foreach my $date ($range->dates) { ... } =head1 DESCRIPTION Quite often, when dealing with dates, we don't just want to know information about one particular date, but about a range of dates. For example, we may wish to know whether a given date is in a particular range, or what the overlap is between one range and another. This module lets you ask such questions. =cut $VERSION = '1.41'; use strict; use Carp; =head1 METHODS =head2 new() my $range = Date::Range->new($date1, $date2); A range object is instantiated with two dates, which do not need to be in chronological order (we'll sort all that out internally). These dates must be instances of the correct object. See want_class() below. =head2 want_class The class of which we expect the date objects to be objects. By default this is L, but this could be any other date class. See L for an example of a subclass that uses a different date class. =cut sub new { my $that = shift; my $class = ref($that) || $that; my @dates = sort { $a <=> $b } grep UNIVERSAL::isa($_ => $class->want_class), @_; croak "You must create a range from two date objects" unless (@dates == 2); my $self = bless { _start => $dates[0], _end => $dates[1], }, $class; return $self; } sub want_class { 'Date::Simple' } =head2 start / end / length my $earliest = $range->start; my $latest = $range->end; my $days = $range->length; These methods allow you retrieve the start and end dates of the range, and the number of days in the range. =cut sub start { $_[0]->{_start} } sub end { $_[0]->{_end} } sub length { (int ($_[0]->end - $_[0]->start) / $_[0]->_day_length) +1 } sub _day_length { 1 } =head2 equals if ($range1->equals($range2)) { } This tells you if two ranges are the same - i.e. start and end at the same dates. =cut sub equals { my ($self, $check) = @_; return unless UNIVERSAL::isa($check => 'Date::Range'); return ($self->start == $check->start and $self->end == $check->end); } =head2 includes if ($range->includes($date3)) { ... } if ($range->includes($range2)) { ... } These methods tell you if a given range includes a given date, or a given range. =cut sub includes { my ($self, $check) = @_; if (UNIVERSAL::isa($check => 'Date::Range')) { return $self->includes($check->start) && $self->includes($check->end); } elsif ($check->isa($self->want_class)) { return $self->start <= $check && $check <= $self->end; } else { croak "Ranges can only include dates or ranges"; } } =head2 overlaps / overlap if ($range->overlaps($range2)) { my $range3 = $range->overlap($range2); } These methods let you know whether one range overlaps another or not, and access this overlap range. =cut sub overlaps { my ($self, $check) = @_; return unless UNIVERSAL::isa($check => 'Date::Range'); return $check->includes($self->start) || $check->includes($self->end) || $self->includes($check); } sub overlap { my ($self, $check) = @_; return unless UNIVERSAL::isa($check => 'Date::Range'); return unless $self->overlaps($check); my @dates = sort { $a <=> $b } $self->start, $self->end, $check->start, $check->end; return $self->new(@dates[1..2]); } =head2 gap my $range3 = $range->gap($range2); This returns a new range representing the gap between two other ranges. =cut sub gap { my ($self, $range) = @_; return if $self->overlaps($range); my @sorted = sort { $a->start <=> $b->start } ($self, $range); my $start = $sorted[0]->end + $self->_day_length; my $end = $sorted[1]->start - $self->_day_length; return if $start >= $end; return $self->new($start, $end); } =head2 abuts if ($range->abuts($range2)) { ... } This tells you whether or not two ranges are contiguous - i.e. there is no gap between them, but they do not overlap. =cut sub abuts { my ($self, $range) = @_; return ! ($self->overlaps($range) || $self->gap($range)); } =head2 dates foreach my $date ($range->dates) { ... } This returns a list of each date in the range as a Date::Simple object. =cut sub dates { my $self = shift; my @dates; my $start = $self->start; for (1..$self->length) { push @dates, $start; $start += $self->_day_length; } return @dates; } 1; =head1 AUTHOR Tony Bowden, based heavily on Martin Fowler's "Analysis Patterns 2" discussion and code at http://www.martinfowler.com/ap2/range.html =head1 BUGS and QUERIES Please direct all correspondence regarding this module to: bug-Date-Range@rt.cpan.org =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2006 Tony Bowden. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.