File Coverage

blib/lib/DateTime/Event/Easter.pm
Criterion Covered Total %
statement 157 172 91.2
branch 93 124 75.0
condition 8 18 44.4
subroutine 23 24 95.8
pod 8 12 66.6
total 289 350 82.5


line stmt bran cond sub pod time code
1             # -*- encoding: utf-8; indent-tabs-mode: nil -*-
2             #
3             # Perl DateTime extension for computing the dates for Easter and related feasts
4             # Copyright © 2003-2004, 2015, 2019 Rick Measham and Jean Forget, all rights reserved
5             #
6             # See the license in the embedded documentation below.
7             #
8             package DateTime::Event::Easter;
9              
10 14     14   2152296 use utf8;
  14         70  
  14         83  
11 14     14   11975 use DateTime;
  14         6228846  
  14         680  
12 14     14   8775 use DateTime::Set;
  14         628376  
  14         408  
13 14     14   117 use Carp;
  14         34  
  14         848  
14 14     14   90 use Params::Validate qw( validate SCALAR BOOLEAN OBJECT );
  14         36  
  14         744  
15              
16 14     14   79 use strict;
  14         34  
  14         255  
17 14     14   70 use warnings;
  14         27  
  14         485  
18 14         35401 use vars qw(
19             $VERSION @ISA @EXPORT @EXPORT_OK
20 14     14   65 );
  14         28  
21              
22             require Exporter;
23              
24             @ISA = qw(Exporter);
25              
26             @EXPORT_OK = qw(easter);
27             $VERSION = '1.06';
28              
29             sub new {
30 33     33 0 22631 my $class = shift;
31 33         895 my %args = validate( @_,
32             { easter => { type => SCALAR, default => 'western', optional => 1, regex => qr/^(western|eastern)$/i },
33             day => { type => SCALAR, default => 'sunday' , optional => 1 },
34             as => { type => SCALAR, default => 'point' , optional => 1 },
35             }
36             );
37            
38 33         276 my %self;
39             my $offset;
40 33 100       439 if ($args{day} =~ /^fat/i) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
41 1         2 $offset = -47;
42             }
43             elsif ($args{day} =~ /^ash/i) {
44             # First day of lent. Lent lasts for 40 days, excluding sundays.
45             # This translates to a 46-day duration, including sundays.
46 1         3 $offset = -46;
47             }
48             elsif ($args{day} =~ /^ascension/i) {
49 1         2 $offset = 39;
50             }
51             elsif ($args{day} =~ /^pentecost/i) {
52 1         3 $offset = 49;
53             }
54             elsif ($args{day} =~ /^trinity/i) {
55 1         2 $offset = 56;
56             }
57             elsif ($args{day} =~ /^palm/i) {
58 1         3 $offset = -7;
59             } elsif ($args{day} =~ /saturday/i) {
60 3         4 $offset = -1;
61             } elsif ($args{day} =~ /friday/i) {
62 3         5 $offset = -2;
63             } elsif ($args{day} =~ /thursday/i) {
64 3         5 $offset = -3;
65             } elsif ($args{day} =~ /^\-?\d+$/i) {
66 4         9 $offset = $args{day};
67 4 50 33     25 if ($offset < -80 || $offset > 250) {
68 0         0 croak "The number of days must be between -80 and 250";
69             }
70             } else {
71 14         33 $offset = 0;
72             }
73 33         188 $self{offset} = DateTime::Duration->new(days=>$offset);
74 33         3740 $self{easter} = lc $args{easter};
75            
76 33 100       213 if ($self{easter} eq 'eastern') {
77 3         1551 require DateTime::Calendar::Julian;
78             }
79              
80             # Set to return points or spans
81 33 50       2619 die("Argument 'as' must be 'point' or 'span'.") unless $args{as} =~ /^(point|span)s?$/i;
82 33         393 $self{as} = lc $1;
83              
84 33         146 return bless \%self, $class;
85            
86             }
87              
88              
89             sub following {
90 485     485 1 255725 my $self = shift;
91 485         817 my $dt = shift;
92              
93 485         891 my $class = ref($dt);
94 485 100 66     2245 if ($self->{easter} eq 'eastern' && $class ne 'DateTime::Calendar::Julian') {
    50          
95 303 50       1288 croak ("Dates need to be datetime objects") unless ($dt->can('utc_rd_values'));
96 303         908 $dt = DateTime::Calendar::Julian->from_object(object=>$dt);
97             } elsif ($class ne 'DateTime') {
98 0 0       0 croak ("Dates need to be datetime objects") unless ($dt->can('utc_rd_values'));
99 0         0 $dt = DateTime->from_object(object=>$dt);
100             }
101              
102 485         184043 my $easter_this_year = $self->_easter($dt->year)+$self->{offset};
103              
104             my $easter = ($easter_this_year > $dt)
105             ? $easter_this_year
106 485 50       212534 : $self->_easter($dt->year+1)+$self->{offset};
107              
108 485 100       207627 $easter = $class->from_object(object=>$easter) if (ref($easter) ne $class);
109 485 100       161905 return ($self->{as} eq 'span')
110             ? _tospan($easter)
111             : $easter;
112             }
113              
114             sub previous {
115 31     31 1 1565 my $self = shift;
116 31         44 my $dt = shift;
117            
118 31         53 my $class = ref($dt);
119 31 50 33     127 if ($self->{easter} eq 'eastern' && $class ne 'DateTime::Calendar::Julian') {
    50          
120 0 0       0 croak ("Dates need to be datetime objects") unless ($dt->can('utc_rd_values'));
121 0         0 $dt = DateTime::Calendar::Julian->from_object(object=>$dt);
122             } elsif ($class ne 'DateTime') {
123 0 0       0 croak ("Dates need to be datetime objects") unless ($dt->can('utc_rd_values'));
124 0         0 $dt = DateTime->from_object(object=>$dt);
125             }
126              
127 31         86 my $easter_this_year = $self->_easter($dt->year)+$self->{offset};
128              
129             my $easter = ($easter_this_year->ymd lt $dt->ymd)
130             ? $easter_this_year
131 31 100       21509 : $self->_easter($dt->year-1)+$self->{offset};
132              
133              
134 31 50       2123 $easter = $class->from_object(object=>$easter) if (ref($easter) ne $class);
135 31 100       162 return ($self->{as} eq 'span')
136             ? _tospan($easter)
137             : $easter;
138             }
139              
140             sub closest {
141 4     4 1 1871 my $self = shift;
142 4         7 my $dt = shift;
143              
144 4         46 my $class = ref($dt);
145 4 50       12 if ($class ne 'DateTime') {
146 0 0       0 croak ("Dates need to be datetime objects") unless ($dt->can('utc_rd_values'));
147 0         0 $dt = DateTime->from_object(object=>$dt);
148             }
149              
150 4 100       10 if ($self->is($dt)) {
151 1         25 my $easter = $dt->clone->truncate(to=>'day');
152 1 50       254 $easter = $class->from_object(object=>$easter) if (ref($easter) ne $class);
153 1 50       6 return ($self->{as} eq 'span')
154             ? _tospan($easter)
155             : $easter;
156             }
157 3         70 my $following_easter = $self->following($dt);
158 3         8 my $following_delta = $following_easter - $dt;
159 3         726 my $previous_easter = $self->previous($dt);
160            
161 3 100       7 my $easter = ($previous_easter + $following_delta < $dt)
162             ? $following_easter
163             : $previous_easter;
164 3 50       2786 $easter = $class->from_object(object=>$easter) if (ref($easter) ne $class);
165 3 50       25 return ($self->{as} eq 'span')
166             ? _tospan($easter)
167             : $easter;
168             }
169              
170             sub is {
171 14     14 1 30 my $self = shift;
172 14         25 my $dt = shift;
173              
174 14         27 my $class = ref($dt);
175 14 50       42 if ($class ne 'DateTime') {
176 0 0       0 croak ("Dates need to be datetime objects") unless ($dt->can('utc_rd_values'));
177 0         0 $dt = DateTime->from_object(object=>$dt);
178             }
179              
180 14 100       39 if ($self->{easter} eq 'eastern') {
181 4         14 $dt = DateTime::Calendar::Julian->from_object(object=>$dt)
182             }
183              
184 14         2501 my $easter_this_year = $self->_easter($dt->year)+$self->{offset};
185              
186 14 100       5131 return ($easter_this_year->ymd eq $dt->ymd) ? 1 : 0;
187             }
188              
189             sub as_list {
190 12     12 1 2069 my $self = shift;
191 12         305 my %args = validate( @_,
192             { from => { type => OBJECT },
193             to => { type => OBJECT },
194             inclusive => { type => SCALAR, default => 0 },
195             }
196             );
197            
198             # Make sure our args are in the right order
199 12         113 ($args{from}, $args{to}) = sort ($args{from}, $args{to});
200            
201 12         1209 my @set = ();
202            
203 12 100       136 if ($args{inclusive}) {
204 5 50       37 if ($self->is($args{from})) {
205             push @set, ($self->{as} eq 'span')
206             ? _tospan($args{from})
207 5 100       189 : $args{from};
208             }
209 5 50       8925 if ($self->is($args{to})) {
210             push @set, ($self->{as} eq 'span')
211             ? _tospan($args{to})
212 5 100       166 : $args{to};
213             }
214             }
215            
216 12         8939 my $checkdate = $args{from};
217              
218 12         52 while ($checkdate < $args{to}) {
219 137         16848 my $check_obj = $self->following($checkdate);
220 137 100       239415 $checkdate = ($self->{as} eq 'span')
221             ? $check_obj->start
222             : $check_obj;
223 137 100       4725 push(@set, $check_obj) if ($checkdate < $args{to});
224             }
225            
226             return ($self->{as} eq 'span')
227 12 100       1791 ? sort { $a->start cmp $b->start} @set
  222         25012  
228             : sort @set;
229             }
230              
231             sub as_old_set {
232 0     0 0 0 my $self = shift;
233 0         0 return DateTime::Set->from_datetimes( dates => [ $self->as_list(@_) ] );
234             }
235             sub as_set {
236 9     9 1 9829 my $self = shift;
237 9         37 my %args = @_;
238 9         27 my %args_1 = @_;
239 9 100 66     55 if (exists $args{inclusive}) {
    100          
240             croak("You must specify both a 'from' and a 'to' datetime")
241             unless ref($args{to}) =~ /DateTime/
242 3 50 33     37 and ref($args{from}) =~ /DateTime/;
243 3 100       24 if ($self->{as} eq 'point') {
244 1 50       4 if ($args{inclusive}) {
245 1         2 $args{start} = delete $args{from};
246 1         3 $args{end} = delete $args{to};
247             } else {
248 0         0 $args{after} = delete $args{from};
249 0         0 $args{before} = delete $args{to};
250             }
251 1         2 delete $args{inclusive};
252             }
253             }
254             elsif (exists $args{from} or exists $args{to}) {
255             croak("You must specify both a 'from' and a 'to' datetime")
256             unless ref($args{to}) =~ /DateTime/
257 3 50 33     34 and ref($args{from}) =~ /DateTime/;
258 3 100       29 if ($self->{as} eq 'point') {
259 1         3 $args{after} = delete $args{from};
260 1         11 $args{before} = delete $args{to};
261             }
262             }
263 9 100       36 if ($self->{as} eq 'span') {
264 6 100       31 $args_1{from} = delete $args_1{after} if exists $args_1{after};
265 6 100       23 $args_1{to} = delete $args_1{before} if exists $args_1{before};
266 6         34 my @list = $self->as_list(%args_1);
267 6         773 return DateTime::SpanSet->from_spans( spans => [ @list ] );
268             }
269             else {
270             return DateTime::Set->from_recurrence(
271 52 100   52   16692 next => sub { return $_[0] if $_[0]->is_infinite; $self->following( $_[0] ) },
  49         209  
272 4 100   4   4145 previous => sub { return $_[0] if $_[0]->is_infinite; $self->previous( $_[0] ) },
  1         6  
273 3         28 %args
274             );
275             }
276             }
277              
278             sub as_span {
279 1     1 1 581 my $self = shift;
280 1         4 $self->{as} = 'span';
281 1         2 return $self;
282             }
283              
284             sub as_point {
285 1     1 1 7191 my $self = shift;
286 1         3 $self->{as} = 'point';
287 1         3 return $self;
288             }
289              
290             sub _tospan {
291 116     116   466 return DateTime::Span->from_datetime_and_duration(
292             start => $_[0],
293             hours => 24,
294             );
295             }
296              
297             sub _easter {
298 1020     1020   41589 my $self = shift;
299 1020         1518 my $year = shift;
300 1020 100       2929 return ($self->{easter} eq 'eastern')
301             ? eastern_easter($year)
302             : western_easter($year);
303             }
304              
305             sub western_easter {
306 588     588 0 153077 my $year = shift;
307 588 50       3011 croak "Year value '$year' should be numeric." if $year!~/^\-?\d+$/;
308            
309 588         1209 my $golden_number = $year % 19;
310             #quasicentury is so named because its a century, only its
311             # the number of full centuries rather than the current century
312 588         1293 my $quasicentury = int($year / 100);
313 588         1389 my $epact = ($quasicentury - int($quasicentury/4) - int(($quasicentury * 8 + 13)/25) + ($golden_number*19) + 15) % 30;
314 588         1322 my $interval = $epact - int($epact/28)*(1 - int(29/($epact+1)) * int((21 - $golden_number)/11) );
315 588         1158 my $weekday = ($year + int($year/4) + $interval + 2 - $quasicentury + int($quasicentury/4)) % 7;
316            
317 588         823 my $offset = $interval - $weekday;
318 588         956 my $month = 3 + int(($offset+40)/44);
319 588         1020 my $day = $offset + 28 - 31* int($month/4);
320            
321 588         1658 return DateTime->new(year=>$year, month=>$month, day=>$day);
322             }
323             *easter = \&western_easter; #alias so people can call 'easter($year)' externally
324              
325             sub eastern_easter {
326 610     610 0 936 my $year = shift;
327 610 50       2977 croak "Year value '$year' should be numeric." if $year!~/^\-?\d+$/;
328            
329 610         1187 my $golden_number = $year % 19;
330              
331 610         1031 my $interval = ($golden_number * 19 + 15) % 30;
332 610         1432 my $weekday = ($year + int($year/4) + $interval) % 7;
333            
334 610         962 my $offset = $interval - $weekday;
335 610         1166 my $month = 3 + int(($offset+40)/44);
336 610         1055 my $day = $offset + 28 - 31* int($month/4);
337              
338 610         1744 return DateTime::Calendar::Julian->new(year=>$year, month=>$month, day=>$day);
339             }
340              
341             # Ending a module with an unspecified number, which could be zero, is wrong.
342             # Therefore the custom of ending a module with a boring "1".
343             # Instead of that, end it with some verse.
344             q{
345             Il reviendra z-à Pâques, mironton mironton mirontaine,
346             Il reviendra z-à Pâques
347             Ou à la Trinité.
348             Ou à la Trinité...
349             };
350             __END__
351              
352             =encoding utf-8
353              
354             =head1 NAME
355              
356             DateTime::Event::Easter - Returns Easter events for DateTime objects
357              
358             =head1 SYNOPSIS
359              
360             use DateTime::Event::Easter;
361            
362             $dt = DateTime->new( year => 2002,
363             month => 3,
364             day => 31,
365             );
366            
367            
368             $easter_sunday = DateTime::Event::Easter->new();
369              
370             $previous_easter_sunday = $easter_sunday->previous($dt);
371             # Sun, 15 Apr 2001 00:00:00 UTC
372            
373             $following_easter_sunday = $easter_sunday->following($dt);
374             # Sun, 20 Apr 2003 00:00:00 UTC
375            
376             $closest_easter_sunday = $easter_sunday->closest($dt);
377             # Sun, 31 Mar 2002 00:00:00 UTC
378            
379             $is_easter_sunday = $easter_sunday->is($dt);
380             # 1
381            
382             $palm_sunday = DateTime::Event::Easter->new(day=>'Palm Sunday');
383              
384              
385             $dt2 = DateTime->new( year => 2006,
386             month => 4,
387             day => 30,
388             );
389            
390             $set = $palm_sunday->as_set (from => $dt, to => $dt2, inclusive => 1);
391             @list = $palm_sunday->as_list(from => $dt, to => $dt2, inclusive => 1);
392             # Sun, 13 Apr 2003 00:00:00 UTC
393             # Sun, 04 Apr 2004 00:00:00 UTC
394             # Sun, 20 Mar 2005 00:00:00 UTC
395             # Sun, 09 Apr 2006 00:00:00 UTC
396            
397             $datetime_set = $palm_sunday->as_set;
398             # A set of every Palm Sunday ever. See C<DateTime::Set> for more information.
399              
400             =head1 DESCRIPTION
401              
402             The DateTime::Event::Easter module returns Easter events for DateTime
403             objects. From a given datetime, it can tell you the previous, the
404             following and the closest Easter event. The 'is' method will tell you if
405             the given DateTime is an Easter Event.
406              
407             Easter Events can be Fat Tuesday, Ash Wednesday, Palm Sunday, Maundy
408             Thursday, Good Friday, Black Saturday, Easter Sunday, Ascension,
409             Pentecost and Trinity Sunday. If that's not enough, the module will
410             also accept an offset so you can get the date for Quasimodo (the next
411             sunday after Easter Sunday) by passing 7.
412              
413              
414             =head1 BACKGROUND
415              
416             Easter Sunday is the Sunday following the first full moon on or
417             following the Official Vernal Equinox. The Official Vernal Equinox is
418             March 21st. Easter Sunday is never on the full moon. Thus the earliest
419             Easter can be is March 22nd.
420              
421             In the orthodox world, although they now use the Gregorian Calendar
422             rather than the Julian, they still take the first full moon on or after the
423             Julian March 21st. As the Julian calendar is slowly getting further and
424             further out of sync with the Gregorian, the first full moon after this
425             date can be a completely different one than for the western Easter. This
426             is why the Orthodox churches celebrate Easter later than western
427             churches.
428              
429             =head1 CONSTRUCTOR
430              
431             This class accepts the following options to its 'new' constructor:
432              
433             =over 4
434              
435             =item * easter => ([western]|eastern)
436              
437             DateTime::Event::Easter understands two calculations for Easter. For
438             simplicity we've called them 'western' and 'eastern'.
439              
440             Western Easter is the day celebrated by the Catholic and Protestant
441             churches. It falls on the first Sunday after the first Full
442             Moon on or after March 21st.
443              
444             Eastern Easter, as celebrated by the Eastern Orthodox Churches similarly
445             falls on the first Sunday after the first Full Moon on or after
446             March 21st. However Eastern Easter uses March 21st in the Julian
447             Calendar.
448              
449             By default this module uses the Western Easter. Even if you pass a
450             Julian DateTime to the module, you'll get back Western Easter unless you
451             specifically ask for Eastern.
452              
453             If this parameter is not supplied, the western Easter will be used.
454              
455             =item * day => ([Easter Sunday]|Palm Sunday|Maundy Thursday|Good Friday|Black
456             Saturday|Fat Tuesday|Ash Wednesday|Ascension|Pentecost|Trinity Sunday|I<n>)
457              
458             When constructed with a day parameter, the method can return associated
459             Easter days other than Easter Sunday. The constructor also allows an
460             integer to be passed here as an offset. For example, Maundy Thursday is
461             the same as an offset of -3 (Three days before Easter Sunday)
462             The numeric offset must be in the -80 .. +250 interval.
463              
464             When constructed without a day parameter, the method uses the date for
465             Easter Sunday (which is the churches' official day for 'Easter', think
466             of it a 'Easter Day' if you want)
467              
468             This parameter also allows the following abreviations: day =>
469             ([Sunday]|Palm|Thursday|Friday|Saturday|Fat|Ash|Ascension|Pentecost|Trinity)
470              
471             =item * as => ([point]|span)
472              
473             By default, all returns are single points in time. Namely they are the
474             moment of midnight for the day in question. If you want Easter 2003 then
475             you actually get back midnight of April 20th 2003. If you specify
476             C<< as => 'span' >> in your constructor, you'll now receive 24 hour spans
477             rather than moments (or 'points'). I<See also the C<as_span> and C<as_point>
478             methods below>
479              
480             =back
481              
482             =head1 METHODS
483              
484             For all these methods, unless otherwise noted, $dt is a plain vanila
485             DateTime object or a DateTime object from any DateTime::Calendar module
486             that can handle calls to from_object and utc_rd_values (which should be
487             all of them, but there's nothing stopping someone making a bad egg).
488              
489             This class offers the following methods.
490              
491             =over 4
492              
493             =item * following($dt)
494              
495             Returns the DateTime object for the Easter Event after $dt. This will
496             not return $dt.
497              
498             =item * previous($dt)
499              
500             Returns the DateTime object for the Easter Event before $dt. This will
501             not return $dt.
502              
503             =item * closest($dt)
504              
505             Returns the DateTime object for the Easter Event closest to $dt. This
506             will return midnight of $dt if $dt is the Easter Event.
507              
508             =item * is($dt)
509              
510             Return positive (1) if $dt is the Easter Event, otherwise returns false
511             (0)
512              
513             =item * as_list(from => $dt, to => $dt2, inclusive => I<([0]|1)>)
514              
515             Returns a list of Easter Events between I<to> and I<from>.
516              
517             If the optional I<inclusive> parameter is true (non-zero), the to and
518             from dates will be included if they are the Easter Event.
519              
520             If you do not include an I<inclusive> parameter, we assume you do not
521             want to include these dates (the same behaviour as supplying a false
522             value)
523              
524              
525             =item * as_set()
526              
527             Returns a DateTime::Set of Easter Events.
528              
529             In the past this method used the same syntax as 'as_list' above. However
530             we now allow both the above syntax as well as the full options allowable
531             when creating sets with C<DateTime::Set>. This means you can call
532             C<< $datetime_set = $palm_sunday->as_set; >> and it will return a
533             C<DateTime::Set> of all Palm Sundays. See C<DateTime::Set> for more information.
534              
535              
536             =item * as_span()
537              
538             This method switches output to spans rather than points. See the 'as' attribute
539             of the constructor for more information. The method returns the object for easy
540             chaining.
541              
542             =item * as_point()
543              
544             This method switches output to points rather than spans. See the 'as' attribute
545             of the constructor for more information. The method returns the object for easy
546             chaining.
547              
548              
549             =back
550              
551             =head1 EXPORTS
552              
553             This class does not export any methods by default, however the following
554             exports are supported.
555              
556             =over 4
557              
558             =item * easter($year)
559              
560             Given a Gregorian year, this method will return a DateTime object for
561             Western Easter Sunday in that year.
562              
563             =back
564              
565             =head1 BUGS AND PROBLEMS FOR SPANS
566              
567             =head2 Inclusion and exclusion of <from> and C<to> dates in lists and sets
568              
569             If you build a list or a set of spans and if the C<from> or C<to> limits
570             coincide with the requested Easter event, the result may be different
571             from what you expect. For example, you ask for Easter sundays between
572             2017-04-16T21:43:00 and 2020-04-12T12:34:00.
573              
574             The inclusive list or set will be:
575              
576             2017-04-16T00:00:00 to 2017-04-16T23:59:59
577             2018-04-01T00:00:00 to 2018-04-01T23:59:59
578             2019-04-21T00:00:00 to 2019-04-21T23:59:59
579             2020-04-12T00:00:00 to 2020-04-12T23:59:59
580              
581             and not:
582              
583             2017-04-16T21:43:00 to 2017-04-16T23:59:59
584             2018-04-01T00:00:00 to 2018-04-01T23:59:59
585             2019-04-21T00:00:00 to 2019-04-21T23:59:59
586             2020-04-12T00:00:00 to 2020-04-12T12:34:00
587              
588             The exclusive list or set will be:
589              
590             2018-04-01T00:00:00 to 2018-04-01T23:59:59
591             2019-04-21T00:00:00 to 2019-04-21T23:59:59
592              
593             and not:
594              
595             2017-04-16T21:43:01 to 2017-04-16T23:59:59
596             2018-04-01T00:00:00 to 2018-04-01T23:59:59
597             2019-04-21T00:00:00 to 2019-04-21T23:59:59
598             2020-04-12T00:00:00 to 2020-04-12T12:35:59
599              
600             Remarks and patches welcome.
601              
602             Note for pedants: the hour C<21:43:01> should actually be
603             21 hours, 43 minutes, zero seconds and 1 nanosecond.
604             Likewise, all the times above ending with C<:59> include
605             999_999_999 nanoseconds.
606              
607             =head2 Interaction of spans with timezones
608              
609             It may happen that Palm sunday or Easter sunday coincide
610             with DST "spring forward" day (for Northern countries). I have not
611             checked what happens in this case for spans: a bit more than one day
612             for exactly 24 hours or exactly one day which gives 23 hours?
613             A similar question exists for DST "fall backward" day in the Southern
614             countries.
615              
616             Also, since you can use a numeric C<day> offset up to 250, you can reach
617             the Northern "fall backwards" and the Southern "spring forward" days, where
618             the same problem will happen in reverse.
619              
620             =head2 Building a spanset
621              
622             For the moment, when building a set with the C<< as => 'set' >> option,
623             the C<from> and C<to> dates are required and thus the set must be a finite set.
624              
625             =head1 THE SMALL PRINT
626              
627             =head2 REFERENCES
628              
629             =over 4
630              
631             =item * L<https://github.com/houseabsolute/DateTime.pm/wiki> - The official wiki
632             of the DateTime project
633              
634             =item * L<https://www.tondering.dk/claus/calendar.html> - Claus Tøndering's
635             calendar FAQ
636              
637             =item * I<Calendrical Calculations> (Third or Fourth Edition) by Nachum Dershowitz and
638             Edward M. Reingold, Cambridge University Press, see
639             L<http://www.calendarists.com>
640             or L<https://www.cambridge.org/us/academic/subjects/computer-science/computing-general-interest/calendrical-calculations-ultimate-edition-4th-edition?format=PB&isbn=9781107683167>,
641             ISBN 978-0-521-70238-6 for the third edition.
642              
643             =item * I<La saga des calendriers>, by Jean Lefort, published by I<Pour la Science>, ISBN 2-90929-003-5
644              
645             =item * I<Le Calendrier>, by Paul Couderc, published by I<Presses universitaires de France> (I<Que sais-je ?>), ISBN 2-13-036266-4
646              
647             =back
648              
649             =head2 SUPPORT
650              
651             Support for this module, and for all DateTime modules will be given
652             through the DateTime mailing list - datetime@perl.org.
653              
654             Bugs should be reported through rt.cpan.org.
655              
656             =head2 AUTHOR
657              
658             Rick Measham <rickm@cpan.org>
659              
660             Co-maintainer Jean Forget <jforget@cpan.org>
661              
662             =head2 CREDITS
663              
664             Much help from the DateTime mailing list, especially from:
665              
666             B<Eugene van der Pijll> - who pointed out flaws causing errors on
667             gregorian years with no eastern easter (like 35000) and who came up with
668             a patch to make the module accept any calendar's DateTime object
669              
670             B<Dave Rolsky> - who picked nits, designed DateTime itself and leads the project
671              
672             B<Martin Hasch> - who pointed out the posibility of memory leak with an early beta
673              
674             B<Joe Orost> and B<Andreas König> - for RT tickets about the POD documentation
675              
676             B<Frank Wiegand> and B<Slaven Rezić> - for patches fixing the POD problems
677              
678             =head2 COPYRIGHT
679              
680             © Copyright 2003, 2004, 2015, 2019 Rick Measham and Jean Forget. All
681             rights reserved. This program is free software; you can redistribute
682             it and/or modify it under the same terms as Perl itself: GNU
683             Public License version 1 or later and Perl Artistic License.
684              
685             The full text of the license can be found in the F<LICENSE> file
686             included with this module or at
687             L<https://dev.perl.org/licenses/artistic.html>
688             and L<https://www.gnu.org/licenses/gpl-1.0.html>.
689              
690             Here is the summary of GPL:
691              
692             This program is free software; you can redistribute it and/or modify
693             it under the terms of the GNU General Public License as published by
694             the Free Software Foundation; either version 1, or (at your option)
695             any later version.
696              
697             This program is distributed in the hope that it will be useful, but
698             WITHOUT ANY WARRANTY; without even the implied warranty of
699             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
700             General Public License for more details.
701              
702             You should have received a copy of the GNU General Public License
703             along with this program; if not, see <https://www.gnu.org/licenses/> or
704             write to the Free Software Foundation, Inc., L<https://fsf.org>.
705              
706             =head2 SEE ALSO
707              
708             L<DateTime>, L<DateTime::Calendar::Julian>, perl(1)
709              
710             L<https://metacpan.org/search?q=easter> which gives L<Date::Easter>, L<Date::Calc> and L<Date::Pcalc>
711              
712             https://github.com/houseabsolute/DateTime.pm/wiki