File Coverage

blib/lib/Date/Lectionary/Day.pm
Criterion Covered Total %
statement 45 47 95.7
branch n/a
condition n/a
subroutine 16 16 100.0
pod n/a
total 61 63 96.8


line stmt bran cond sub pod time code
1             package Date::Lectionary::Day;
2              
3 1     1   1094 use v5.22;
  1         3  
4 1     1   5 use strict;
  1         2  
  1         22  
5 1     1   4 use warnings;
  1         2  
  1         24  
6              
7 1     1   4 use Moose;
  1         2  
  1         7  
8 1     1   6039 use MooseX::Aliases;
  1         1009  
  1         3  
9 1     1   47568 use Carp;
  1         2  
  1         65  
10 1     1   6 use Try::Tiny;
  1         1  
  1         46  
11 1     1   413 use Time::Piece;
  1         6439  
  1         4  
12 1     1   63 use Time::Seconds;
  1         2  
  1         51  
13 1     1   245 use Date::Advent;
  1         41481  
  1         34  
14 1     1   325 use Date::Easter;
  1         530  
  1         67  
15 1     1   6 use Date::Lectionary::Time qw(nextSunday prevSunday closestSunday);
  1         2  
  1         41  
16 1     1   5 use namespace::autoclean;
  1         2  
  1         7  
17 1     1   62 use Moose::Util::TypeConstraints;
  1         1  
  1         16  
18 1     1   2288 use File::Share ':all';
  1         5161  
  1         153  
19 1     1   63 use XML::LibXML;
  0            
  0            
20              
21             =head1 NAME
22              
23             Date::Lectionary::Day - Determines the Day in the Christian Liturgical Year
24              
25             =head1 VERSION
26              
27             Version 1.20170809
28              
29             =cut
30              
31             our $VERSION = '1.20170809';
32              
33             =head1 SYNOPSIS
34              
35             A helper object for Date::Lectionary to determine the liturgical name(s) and type for the given day according to a given lectionary.
36              
37             =cut
38              
39             enum 'DayType', [qw(fixedFeast moveableFeast Sunday noLect)];
40             enum 'LectionaryType', [qw(acna rcl)];
41             enum 'MultiLect', [qw(yes no)];
42             enum 'IncludeFeasts', [qw(yes no)];
43             no Moose::Util::TypeConstraints;
44              
45             =head1 SUBROUTINES/METHODS/ATTRIBUTES
46              
47             =head2 ATTRIBUTES
48              
49             =head3 date
50              
51             The Time::Piece object date given at object construction.
52              
53             =head3 lectionary
54              
55             An optional attribute given at object creation time. Valid values are 'acna' for the Anglican Church of North America lectionary and 'rcl' for the Revised Common Lectionary. This attribute defaults to 'acna' if no value is given.
56              
57             =head3 type
58              
59             Stores the type of liturgical day. 'fixedFeast' is returned for non-moveable feast days such as Christmas Day. 'moveableFeast' is returned for moveable feast days. Moveable feasts move to a Monday when they occure on a Sunday. 'Sunday' is returned for non-fixed feast Sundays of the liturgical year. 'noLect' is returned for days with no feast day or Sunday readings.
60              
61             =head3 name
62              
63             The name of the day in the lectionary. For noLect days a String representation of the day is returned as the name.
64              
65             =head3 alt
66              
67             The alternative name --- if one is given --- of the day in the lectionary. If there is no alternative name for the day, then the empty string will be returned.
68              
69             =head3 multiLect
70              
71             Returns 'yes' if the day has multiple services with readings associated with it. (E.g. Christmas Day, Easter, etc.) Returns 'no' if the day is a normal lectioanry day with only one service and one set of readings.
72              
73             =head3 subLects
74              
75             An ArrayRef of the names of the multiple services that occur on a multiLect day.
76              
77             =head3 includeFeasts
78              
79             If this is set to 'yes' --- the default value --- the module will include fixed and moveable feasts in its determination of which liturgical Sunday it is.
80              
81             If set to 'no', it will exclude fixed and moveable feasts. Excluding feasts is useful when using Date::Lectionary::Day in combination with a daily lectionary such as Date::Lectioary::Daily where a fixed feast such as The Transfiguration can conflict with determining the Sunday to use for the daily lectionary.
82              
83             =cut
84              
85             has 'date' => (
86             is => 'ro',
87             isa => 'Time::Piece',
88             required => 1,
89             );
90              
91             has 'type' => (
92             is => 'ro',
93             isa => 'DayType',
94             writer => '_setType',
95             init_arg => undef,
96             );
97              
98             has 'lectionary' => (
99             is => 'ro',
100             isa => 'LectionaryType',
101             default => 'acna',
102             );
103              
104             has 'displayName' => (
105             is => 'ro',
106             isa => 'Str',
107             writer => '_setDisplayName',
108             init_arg => undef,
109             alias => 'name',
110             );
111              
112             has 'altName' => (
113             is => 'ro',
114             isa => 'Str',
115             writer => '_setAltName',
116             init_arg => undef,
117             alias => 'alt',
118             );
119              
120             has 'commonName' => (
121             is => 'ro',
122             isa => 'Str',
123             writer => '_setCommonName',
124             init_arg => undef,
125             );
126              
127             has 'multiLect' => (
128             is => 'ro',
129             isa => 'MultiLect',
130             writer => '_setMultiLect',
131             init_arg => undef,
132             );
133              
134             has 'subLects' => (
135             is => 'ro',
136             isa => 'ArrayRef',
137             writer => '_setSubLects',
138             init_arg => undef,
139             );
140              
141             has 'includeFeasts' => (
142             is => 'ro',
143             isa => 'IncludeFeasts',
144             default => 'yes',
145             );
146              
147             =head2 BUILD
148              
149             Constructor for the Date::Lectionary object. Takes a Time::Piect object, C<date>, to create the object.
150              
151             =cut
152              
153             sub BUILD {
154             my $self = shift;
155              
156             my $advent = _determineAdvent( $self->date );
157             my $easter = _determineEaster( $advent->firstSunday->year + 1 );
158              
159             my %commonNameInfo =
160             _determineDay( $self->date, $self->lectionary, $self->includeFeasts, $advent, $easter );
161             $self->_setCommonName( $commonNameInfo{commonName} );
162             $self->_setDisplayName(
163             _determineDisplayName( $self->lectionary, $commonNameInfo{commonName} )
164             );
165             $self->_setAltName(
166             _determineAltName( $self->lectionary, $commonNameInfo{commonName} ) );
167              
168             $self->_setType( $commonNameInfo{type} );
169              
170             my %multiLectInfo =
171             _determineMultiLect( $self->lectionary, $commonNameInfo{commonName} );
172             $self->_setMultiLect( $multiLectInfo{multiLect} );
173             $self->_setSubLects( $multiLectInfo{multiNames} );
174             }
175              
176             =head2 _determineMultiLect
177              
178             Private method to determine if the day has multiple lectionary services and readings for the day.
179              
180             =cut
181              
182             sub _determineMultiLect {
183             my $tradition = shift;
184             my $commonName = shift;
185              
186             my $parser = XML::LibXML->new();
187             my $data_location;
188             my $lectionary;
189              
190             try {
191             $data_location =
192             dist_file( 'Date-Lectionary', 'date_lectionary_xref.xml' );
193             $lectionary = $parser->parse_file($data_location);
194             }
195             catch {
196             confess
197             "The lectionary cross reference file could not be found or parsed.";
198             };
199              
200             my $compiled_xpath;
201              
202             try {
203             $compiled_xpath = XML::LibXML::XPathExpression->new(
204             "/xref/day[\@multi=\"$commonName\"]/alt[\@type='$tradition']");
205             }
206             catch {
207             confess
208             "The XPATH expression to to query the cross reference database could not be compiled.";
209             };
210              
211             my @multiNames;
212              
213             try {
214             if ( $lectionary->exists($compiled_xpath) ) {
215             my @nodes = $lectionary->findnodes($compiled_xpath);
216             foreach my $node (@nodes) {
217             push( @multiNames, $node->textContent );
218             }
219             return ( multiLect => 'yes', multiNames => \@multiNames );
220             }
221             else {
222             return ( multiLect => 'no', multiNames => \@multiNames );
223             }
224             }
225             catch {
226             confess
227             "An unpected error occured while querying the cross reference database.";
228             };
229             }
230              
231             =head2 _determineDisplayName
232              
233             Private method to determine the unique display name for the day.
234              
235             =cut
236              
237             sub _determineDisplayName {
238             my $tradition = shift;
239             my $commonName = shift;
240              
241             my $parser = XML::LibXML->new();
242             my $data_location;
243             my $lectionary;
244              
245             try {
246             $data_location =
247             dist_file( 'Date-Lectionary', 'date_lectionary_xref.xml' );
248             $lectionary = $parser->parse_file($data_location);
249             }
250             catch {
251             confess
252             "The lectionary cross reference file could not be found or parsed.";
253             };
254              
255             my $compiled_xpath;
256             my $multi_xpath;
257              
258             try {
259             $compiled_xpath = XML::LibXML::XPathExpression->new(
260             "/xref/day[\@name=\"$commonName\"]/alt[\@type='$tradition']");
261             $multi_xpath =
262             XML::LibXML::XPathExpression->new(
263             "/xref/day[\@multi=\"$commonName\"]");
264             }
265             catch {
266             confess
267             "The XPATH expression to to query the cross reference database could not be compiled.";
268             };
269              
270             my $displayName;
271              
272             try {
273             $displayName = $lectionary->findvalue($compiled_xpath);
274              
275             if ( $lectionary->exists($multi_xpath) ) {
276             return $commonName;
277             }
278             elsif ( $displayName eq '' ) {
279             return $commonName;
280             }
281             else {
282             return $displayName;
283             }
284             }
285             catch {
286             confess
287             "An unpected error occured while querying the cross reference database.";
288             };
289              
290             }
291              
292             =head2 _determineAltName
293              
294             Private method to determine if the day has any alternative names for the day.
295              
296             =cut
297              
298             sub _determineAltName {
299             my $tradition = shift;
300             my $commonName = shift;
301              
302             my $parser = XML::LibXML->new();
303             my $data_location;
304             my $lectionary;
305              
306             try {
307             $data_location =
308             dist_file( 'Date-Lectionary', 'date_lectionary_xref.xml' );
309             $lectionary = $parser->parse_file($data_location);
310             }
311             catch {
312             confess
313             "The lectionary cross reference file could not be found or parsed.";
314             };
315              
316             my $compiled_xpath;
317             my $multi_xpath;
318              
319             try {
320             $compiled_xpath = XML::LibXML::XPathExpression->new(
321             "/xref/day[\@name=\"$commonName\"]/alt[\@type='$tradition-alt']");
322             }
323             catch {
324             confess
325             "The XPATH expression to to query the cross reference database could not be compiled.";
326             };
327              
328             my $altName;
329              
330             try {
331             $altName = $lectionary->findvalue($compiled_xpath);
332             return $altName;
333             }
334             catch {
335             confess
336             "An unpected error occured while querying the cross reference database.";
337             };
338              
339             }
340              
341             =head2 _determineAdvent
342              
343             Private method that takes a Time::Piece date object to returns a Date::Advent object containing the dates for Advent of the current liturgical year.
344              
345             =cut
346              
347             sub _determineAdvent {
348             my $date = shift;
349              
350             my $advent = undef;
351              
352             try {
353             $advent = Date::Advent->new( date => $date );
354             return $advent;
355             }
356             catch {
357             confess "Could not calculate Advent for the given date ["
358             . $date->ymd . "].";
359             };
360             }
361              
362             =head2 _determineEaster
363              
364             Private method that takes a four-digit representation of a Common Era year and calculates the date for Easter as a Time::Piece object.
365              
366             =cut
367              
368             sub _determineEaster {
369             my $easterYear = shift;
370              
371             my $easter = undef;
372              
373             try {
374             my ( $easterMonth, $easterDay ) = easter($easterYear);
375             $easter = Time::Piece->strptime(
376             $easterYear . "-" . $easterMonth . "-" . $easterDay, "%Y-%m-%d" );
377             return $easter;
378             }
379             catch {
380             confess "Could not calculate Easter for the year [" . $easterYear . "]";
381             };
382             }
383              
384             =head2 _determineFeasts
385              
386             Private method that takes the Time::Piece date given at construction and determines if the date is one of many feasts in the liturgical calendar. Feasts are taken from the Anglican Church in North America's revision of the revised common lectionary.
387              
388             =cut
389              
390             sub _determineFeasts {
391             my $date = shift;
392             my $lectionary = shift;
393              
394             my $yesterday = $date - ONE_DAY;
395              
396             my $yesterdayName;
397             if ( $yesterday->wday == 1 ) {
398             $yesterdayName = _buildMoveableDays( $yesterday, $lectionary );
399             }
400              
401             if ($yesterdayName) {
402             return ( commonName => $yesterdayName, type => 'moveableFeast' );
403             }
404              
405             my $fixedDayName = _buildFixedDays( $date, $lectionary );
406             if ($fixedDayName) {
407             return ( commonName => $fixedDayName, type => 'fixedFeast' );
408             }
409              
410             my $moveableDayName = _buildMoveableDays( $date, $lectionary );
411             if ( $moveableDayName && $date->wday != 1 ) {
412             return ( commonName => $moveableDayName, type => 'moveableFeast' );
413             }
414              
415             return ( commonName => undef, type => undef );
416             }
417              
418             =head2 _buildMoveableDays
419              
420             Private method that takes the Time::Piece date given at construction and determines if the date is one of many moveable feasts in the liturgical calendar. Feasts are taken from the Anglican Church in North America's revision of the revised common lectionary.
421              
422             =cut
423              
424             sub _buildMoveableDays {
425             my $date = shift;
426             my $lectionary = shift;
427              
428             #Moveable holidays in January
429             if ( $date->mon == 1 ) {
430             if ( $date->mday == 18 && $lectionary eq 'acna' ) {
431             return "Confession of St. Peter";
432             }
433             if ( $date->mday == 25 && $lectionary eq 'acna' ) {
434             return "Conversion of St. Paul";
435             }
436             }
437              
438             #Moveable holidays in February
439             elsif ( $date->mon == 2 ) {
440             if ( $date->mday == 2 ) {
441             return "The Presentation of Christ in the Temple";
442             }
443             if ( $date->mday == 24 && $lectionary eq 'acna' ) {
444             return "St. Matthias";
445             }
446             }
447              
448             #Moveable holidays in March
449             elsif ( $date->mon == 3 ) {
450             if ( $date->mday == 19 && $lectionary eq 'acna' ) {
451             return "St. Joseph";
452             }
453             if ( $date->mday == 25 ) {
454             return "The Annunciation";
455             }
456             }
457              
458             #Moveable holidays in April
459             elsif ( $date->mon == 4 ) {
460             if ( $date->mday == 25 && $lectionary eq 'acna' ) {
461             return "St. Mark";
462             }
463             }
464              
465             #Moveable holidays in May
466             elsif ( $date->mon == 5 ) {
467             if ( $date->mday == 1 && $lectionary eq 'acna' ) {
468             return "St. Philip & St. James";
469             }
470             if ( $date->mday == 31 ) {
471             return "The Visitation";
472             }
473             }
474              
475             #Moveable holidays in June
476             elsif ( $date->mon == 6 ) {
477             if ( $date->mday == 11 && $lectionary eq 'acna' ) {
478             return "St. Barnabas";
479             }
480             if ( $date->mday == 24 && $lectionary eq 'acna' ) {
481             return "Nativity of St. John the Baptist";
482             }
483             if ( $date->mday == 29 && $lectionary eq 'acna' ) {
484             return "St. Peter & St. Paul";
485             }
486             }
487              
488             #Moveable holidays in July
489             elsif ( $date->mon == 7 ) {
490             if ( $date->mday == 22 && $lectionary eq 'acna' ) {
491             return "St. Mary of Magdala";
492             }
493             if ( $date->mday == 25 && $lectionary eq 'acna' ) {
494             return "St. James";
495             }
496             }
497              
498             #Moveable holidays in August
499             elsif ( $date->mon == 8 ) {
500             if ( $date->mday == 15 && $lectionary eq 'acna' ) {
501             return "St. Mary the Virgin";
502             }
503             if ( $date->mday == 24 && $lectionary eq 'acna' ) {
504             return "St. Bartholomew";
505             }
506             }
507              
508             #Moveable holidays in September
509             elsif ( $date->mon == 9 ) {
510             if ( $date->mday == 14 ) {
511             return "Holy Cross Day";
512             }
513             if ( $date->mday == 21 && $lectionary eq 'acna' ) {
514             return "St. Matthew";
515             }
516             if ( $date->mday == 29 && $lectionary eq 'acna' ) {
517             return "Holy Michael & All Angels";
518             }
519             }
520              
521             #Moveable holidays in October
522             elsif ( $date->mon == 10 ) {
523             if ( $date->mday == 18 && $lectionary eq 'acna' ) {
524             return "St. Luke";
525             }
526             if ( $date->mday == 28 && $lectionary eq 'acna' ) {
527             return "St. Simon & St. Jude";
528             }
529             }
530              
531             #Moveable holidays in November
532             elsif ( $date->mon == 11 ) {
533             if ( $date->mday == 30 && $lectionary eq 'acna' ) {
534             return "St. Andrew";
535             }
536             }
537              
538             #Moveable holidays in December
539             elsif ( $date->mon == 12 ) {
540             if ( $date->mday == 21 && $lectionary eq 'acna' ) {
541             return "St. Thomas";
542             }
543             if ( $date->mday == 26 && $lectionary eq 'acna' ) {
544             return "St. Stephen";
545             }
546             if ( $date->mday == 27 && $lectionary eq 'acna' ) {
547             return "St. John";
548             }
549             if ( $date->mday == 28 && $lectionary eq 'acna' ) {
550             return "Holy Innocents";
551             }
552             }
553             else {
554             confess "Date [" . $date->ymd . "] is not a known or valid date.";
555             }
556             }
557              
558             =head2 _buildFixedDays
559              
560             Private method that takes the Time::Piece date given at construction and determines if the date is one of many fixed (non-moveable) feasts in the liturgical calendar. Fixed feasts are taken from the Anglican Church in North America's revision of the revised common lectionary.
561              
562             =cut
563              
564             sub _buildFixedDays {
565             my $date = shift;
566             my $lectionary = shift;
567              
568             #Fixed holidays in January
569             if ( $date->mon == 1 ) {
570             if ( $date->mday == 1 ) {
571             return "Holy Name";
572             }
573             if ( $date->mday == 6 ) {
574             return "The Epiphany";
575             }
576             }
577              
578             #Fixed holidays in February
579             elsif ( $date->mon == 2 ) {
580             }
581              
582             #Fixed holidays in March
583             elsif ( $date->mon == 3 ) {
584             }
585              
586             #Fixed holidays in April
587             elsif ( $date->mon == 4 ) {
588             }
589              
590             #Fixed holidays in May
591             elsif ( $date->mon == 5 ) {
592             }
593              
594             #Fixed holidays in June
595             elsif ( $date->mon == 6 ) {
596             }
597              
598             #Fixed holidays in July
599             elsif ( $date->mon == 7 ) {
600             }
601              
602             #Fixed holidays in August
603             elsif ( $date->mon == 8 ) {
604             if ( $date->mday == 6 && $lectionary eq 'acna' ) {
605             return "The Transfiguration";
606             }
607             }
608              
609             #Fixed holidays in September
610             elsif ( $date->mon == 9 ) {
611             }
612              
613             #Fixed holidays in October
614             elsif ( $date->mon == 10 ) {
615             }
616              
617             #Fixed holidays in November
618             elsif ( $date->mon == 11 ) {
619             if ( $date->mday == 1 ) {
620             return "All Saints' Day";
621             }
622             }
623              
624             #Fixed holidays in December
625             elsif ( $date->mon == 12 ) {
626             if ( $date->mday == 25 ) {
627             return "Christmas Day";
628             }
629             }
630             else {
631             confess "Date [" . $date->ymd . "] is not a known or valid date.";
632             }
633             }
634              
635             =head2 _determineAshWednesday
636              
637             Private method that takes the Time::Piece date for Easter and determines the date for Ash Wednesday. Ash Wednesday is the start of Lent. It occurs 46 days before Easter for the given year.
638              
639             =cut
640              
641             sub _determineAshWednesday {
642             my $easter = shift;
643              
644             my $ashWednesday = undef;
645              
646             try {
647             my $secondsToSubtract = 46 * ONE_DAY;
648             $ashWednesday = $easter - $secondsToSubtract;
649             return $ashWednesday;
650             }
651             catch {
652             confess "Could not calculate Ash Wednesday for Easter ["
653             . $easter->ymd . "].";
654             };
655             }
656              
657             =head2 _determineAscension
658              
659             Private method that takes the Time::Piece date for Easter and determines the date for Ascension. Ascension is forty days (inclusive) after Easter.
660              
661             =cut
662              
663             sub _determineAscension {
664             my $easter = shift;
665              
666             my $ascension = undef;
667              
668             try {
669             my $secondsToAdd = 39 * ONE_DAY;
670             $ascension = $easter + $secondsToAdd;
671             return $ascension;
672             }
673             catch {
674             confess "Could not calculate Ascension for Easter ["
675             . $easter->ymd . "].";
676             };
677             }
678              
679             =head2 _determinePentecost
680              
681             Private method the takes the Time::Piece date for Easter and determines the date for Pentecost. Pentecost is fifty days (inclusive) after Easter.
682              
683             =cut
684              
685             sub _determinePentecost {
686             my $easter = shift;
687              
688             my $pentecost = undef;
689              
690             try {
691             my $secondsToAdd = 49 * ONE_DAY;
692             $pentecost = $easter + $secondsToAdd;
693             return $pentecost;
694             }
695             catch {
696             confess "Could not calculate Pentecost for Easter ["
697             . $easter->ymd . "].";
698             };
699             }
700              
701             =head2 _determineHolyWeek
702              
703             Private method used to return the names of various days within Holy Week. Takes the Time::Piece date given at construction and the Time::Piece date for Easter. Returns undef if the date given at construction is not found in Holy Week.
704              
705             =cut
706              
707             sub _determineHolyWeek {
708             my $date = shift;
709             my $easter = shift;
710              
711             my $dateMark = $easter - ONE_DAY;
712             if ( $date == $dateMark ) {
713             return "Holy Saturday";
714             }
715              
716             $dateMark = $dateMark - ONE_DAY;
717             if ( $date == $dateMark ) {
718             return "Good Friday";
719             }
720              
721             $dateMark = $dateMark - ONE_DAY;
722             if ( $date == $dateMark ) {
723             return "Maundy Thursday";
724             }
725              
726             $dateMark = $dateMark - ONE_DAY;
727             if ( $date == $dateMark ) {
728             return "Wednesday in Holy Week";
729             }
730              
731             $dateMark = $dateMark - ONE_DAY;
732             if ( $date == $dateMark ) {
733             return "Tuesday in Holy Week";
734             }
735              
736             $dateMark = $dateMark - ONE_DAY;
737             if ( $date == $dateMark ) {
738             return "Monday in Holy Week";
739             }
740              
741             $dateMark = $dateMark - ONE_DAY;
742             if ( $date == $dateMark ) {
743             return "Palm Sunday";
744             }
745              
746             return undef;
747             }
748              
749             =head2 _determineEasterWeek
750              
751             Private method used to return the names of various days within Easter Week. Takes the Time::Piece date given at construction and the Time::Piece date for Easter. Returns undef if the date given at construction is not found in Easter Week.
752              
753             =cut
754              
755             sub _determineEasterWeek {
756             my $date = shift;
757             my $easter = shift;
758              
759             my $dateMark = $easter + ONE_DAY;
760             if ( $date == $dateMark ) {
761             return "Monday of Easter Week";
762             }
763              
764             $dateMark = $dateMark + ONE_DAY;
765             if ( $date == $dateMark ) {
766             return "Tuesday of Easter Week";
767             }
768              
769             $dateMark = $dateMark + ONE_DAY;
770             if ( $date == $dateMark ) {
771             return "Wednesday of Easter Week";
772             }
773              
774             $dateMark = $dateMark + ONE_DAY;
775             if ( $date == $dateMark ) {
776             return "Thursday of Easter Week";
777             }
778              
779             $dateMark = $dateMark + ONE_DAY;
780             if ( $date == $dateMark ) {
781             return "Friday of Easter Week";
782             }
783              
784             $dateMark = $dateMark + ONE_DAY;
785             if ( $date == $dateMark ) {
786             return "Saturday of Easter Week";
787             }
788              
789             return undef;
790             }
791              
792             =head2 _determineChristmasEpiphany
793              
794             Private method that matches the date given at construction against the Sundays in Christmastide and Epiphany. Returns a string representation of the name of the Sunday in the lectionary.
795              
796             =cut
797              
798             sub _determineChristmasEpiphany {
799             my $date = shift;
800              
801             my $advent = shift;
802              
803             my $ashWednesday = shift;
804              
805             #Is the date in Christmastide?
806             my $dateMarker = nextSunday( $advent->fourthSunday );
807             if ( $date == $dateMarker ) {
808             return "The First Sunday of Christmas";
809             }
810              
811             $dateMarker = nextSunday($dateMarker);
812             if ( $date == $dateMarker ) {
813             return "The Second Sunday of Christmas";
814             }
815              
816             #Is the date in Epiphany?
817             $dateMarker = nextSunday($dateMarker);
818             if ( $date == $dateMarker ) {
819             return "The First Sunday of Epiphany";
820             }
821              
822             $dateMarker = nextSunday($dateMarker);
823             if ( $date == $dateMarker ) {
824             return "The Second Sunday of Epiphany";
825             }
826              
827             $dateMarker = nextSunday($dateMarker);
828             if ( $date == $dateMarker ) {
829             return "The Third Sunday of Epiphany";
830             }
831              
832             $dateMarker = nextSunday($dateMarker);
833             if ( $date == $dateMarker ) {
834             return "The Fourth Sunday of Epiphany";
835             }
836              
837             $dateMarker = nextSunday($dateMarker);
838             if ( $date == $dateMarker ) {
839             return "The Fifth Sunday of Epiphany";
840             }
841              
842             $dateMarker = nextSunday($dateMarker);
843             if ( $date == $dateMarker ) {
844             return "The Sixth Sunday of Epiphany";
845             }
846              
847             $dateMarker = nextSunday($dateMarker);
848             if ( $date == $dateMarker ) {
849             return "The Seventh Sunday of Epiphany";
850             }
851              
852             $dateMarker = nextSunday($dateMarker);
853             if ( $date == $dateMarker && $date != prevSunday($ashWednesday) ) {
854             return "The Second to Last Sunday after Epiphany";
855             }
856             elsif ( $date == $dateMarker && $date == prevSunday($ashWednesday) ) {
857             return "The Last Sunday after Epiphany";
858             }
859              
860             $dateMarker = nextSunday($dateMarker);
861             if ( $date == $dateMarker ) {
862             return "The Last Sunday after Epiphany";
863             }
864              
865             confess "There are no further Sundays of Christmastide or Epiphany.";
866             }
867              
868             =head2 _determineLent
869              
870             Private method that matches the date given at construction against the Sundays in Lent. Returns a string representation of the name of the Sunday in the lectionary.
871              
872             =cut
873              
874             sub _determineLent {
875             my $date = shift;
876             my $ashWednesday = shift;
877              
878             my $dateMarker = nextSunday($ashWednesday);
879             if ( $date == $dateMarker ) {
880             return "The First Sunday in Lent";
881             }
882              
883             $dateMarker = nextSunday($dateMarker);
884             if ( $date == $dateMarker ) {
885             return "The Second Sunday in Lent";
886             }
887              
888             $dateMarker = nextSunday($dateMarker);
889             if ( $date == $dateMarker ) {
890             return "The Third Sunday in Lent";
891             }
892              
893             $dateMarker = nextSunday($dateMarker);
894             if ( $date == $dateMarker ) {
895             return "The Fourth Sunday in Lent";
896             }
897              
898             $dateMarker = nextSunday($dateMarker);
899             if ( $date == $dateMarker ) {
900             return "The Fifth Sunday in Lent";
901             }
902              
903             confess "There are no further Sundays in Lent";
904             }
905              
906             =head2 _determineEasterSeason
907              
908             Private method that matches the date given at construction against the Sundays in the Easter season. Returns a string representation of the name of the Sunday in the lectionary.
909              
910             =cut
911              
912             sub _determineEasterSeason {
913             my $date = shift;
914             my $easter = shift;
915              
916             my $dateMarker = nextSunday($easter);
917             if ( $date == $dateMarker ) {
918             return "The Second Sunday of Easter";
919             }
920              
921             $dateMarker = nextSunday($dateMarker);
922             if ( $date == $dateMarker ) {
923             return "The Third Sunday of Easter";
924             }
925              
926             $dateMarker = nextSunday($dateMarker);
927             if ( $date == $dateMarker ) {
928             return "The Fourth Sunday of Easter";
929             }
930              
931             $dateMarker = nextSunday($dateMarker);
932             if ( $date == $dateMarker ) {
933             return "The Fifth Sunday of Easter";
934             }
935              
936             $dateMarker = nextSunday($dateMarker);
937             if ( $date == $dateMarker ) {
938             return "The Sixth Sunday of Easter";
939             }
940              
941             $dateMarker = nextSunday($dateMarker);
942             if ( $date == $dateMarker ) {
943             return "The Sunday after Ascension Day";
944             }
945              
946             confess "There are no further Sundays of Easter for [" . $date->ymd . "].";
947             }
948              
949             =head2 _determineOrdinary
950              
951             Private method that matches the date given at construction against the Sundays in Ordinary time, e.g. Trinity Sunday and following Sundays. Returns a string representation of the name of the Sunday in the lectionary.
952              
953             =cut
954              
955             sub _determineOrdinary {
956             my $date = shift;
957             my $pentecost = shift;
958              
959             my $trinitySunday = nextSunday($pentecost);
960             if ( $date == $trinitySunday ) {
961             return "Trinity Sunday";
962             }
963              
964             my $dateMarker = closestSunday(
965             Time::Piece->strptime( $pentecost->year . "-05-25", "%Y-%m-%d" ) );
966             if ( $date == $dateMarker ) {
967             return "Ordinary 8";
968             }
969              
970             $dateMarker = closestSunday(
971             Time::Piece->strptime( $pentecost->year . "-06-01", "%Y-%m-%d" ) );
972             if ( $date == $dateMarker ) {
973             return "Ordinary 9";
974             }
975              
976             $dateMarker = closestSunday(
977             Time::Piece->strptime( $pentecost->year . "-06-08", "%Y-%m-%d" ) );
978             if ( $date == $dateMarker ) {
979             return "Ordinary 10";
980             }
981              
982             $dateMarker = closestSunday(
983             Time::Piece->strptime( $pentecost->year . "-06-15", "%Y-%m-%d" ) );
984             if ( $date == $dateMarker ) {
985             return "Ordinary 11";
986             }
987              
988             $dateMarker = closestSunday(
989             Time::Piece->strptime( $pentecost->year . "-06-22", "%Y-%m-%d" ) );
990             if ( $date == $dateMarker ) {
991             return "Ordinary 12";
992             }
993              
994             $dateMarker = closestSunday(
995             Time::Piece->strptime( $pentecost->year . "-06-29", "%Y-%m-%d" ) );
996             if ( $date == $dateMarker ) {
997             return "Ordinary 13";
998             }
999              
1000             $dateMarker = closestSunday(
1001             Time::Piece->strptime( $pentecost->year . "-07-06", "%Y-%m-%d" ) );
1002             if ( $date == $dateMarker ) {
1003             return "Ordinary 14";
1004             }
1005              
1006             $dateMarker = closestSunday(
1007             Time::Piece->strptime( $pentecost->year . "-07-13", "%Y-%m-%d" ) );
1008             if ( $date == $dateMarker ) {
1009             return "Ordinary 15";
1010             }
1011              
1012             $dateMarker = closestSunday(
1013             Time::Piece->strptime( $pentecost->year . "-07-20", "%Y-%m-%d" ) );
1014             if ( $date == $dateMarker ) {
1015             return "Ordinary 16";
1016             }
1017              
1018             $dateMarker = closestSunday(
1019             Time::Piece->strptime( $pentecost->year . "-07-27", "%Y-%m-%d" ) );
1020             if ( $date == $dateMarker ) {
1021             return "Ordinary 17";
1022             }
1023              
1024             $dateMarker = closestSunday(
1025             Time::Piece->strptime( $pentecost->year . "-08-03", "%Y-%m-%d" ) );
1026             if ( $date == $dateMarker ) {
1027             return "Ordinary 18";
1028             }
1029              
1030             $dateMarker = closestSunday(
1031             Time::Piece->strptime( $pentecost->year . "-08-10", "%Y-%m-%d" ) );
1032             if ( $date == $dateMarker ) {
1033             return "Ordinary 19";
1034             }
1035              
1036             $dateMarker = closestSunday(
1037             Time::Piece->strptime( $pentecost->year . "-08-17", "%Y-%m-%d" ) );
1038             if ( $date == $dateMarker ) {
1039             return "Ordinary 20";
1040             }
1041              
1042             $dateMarker = closestSunday(
1043             Time::Piece->strptime( $pentecost->year . "-08-24", "%Y-%m-%d" ) );
1044             if ( $date == $dateMarker ) {
1045             return "Ordinary 21";
1046             }
1047              
1048             $dateMarker = closestSunday(
1049             Time::Piece->strptime( $pentecost->year . "-08-31", "%Y-%m-%d" ) );
1050             if ( $date == $dateMarker ) {
1051             return "Ordinary 22";
1052             }
1053              
1054             $dateMarker = closestSunday(
1055             Time::Piece->strptime( $pentecost->year . "-09-07", "%Y-%m-%d" ) );
1056             if ( $date == $dateMarker ) {
1057             return "Ordinary 23";
1058             }
1059              
1060             $dateMarker = closestSunday(
1061             Time::Piece->strptime( $pentecost->year . "-09-14", "%Y-%m-%d" ) );
1062             if ( $date == $dateMarker ) {
1063             return "Ordinary 24";
1064             }
1065              
1066             $dateMarker = closestSunday(
1067             Time::Piece->strptime( $pentecost->year . "-09-21", "%Y-%m-%d" ) );
1068             if ( $date == $dateMarker ) {
1069             return "Ordinary 25";
1070             }
1071              
1072             $dateMarker = closestSunday(
1073             Time::Piece->strptime( $pentecost->year . "-09-28", "%Y-%m-%d" ) );
1074             if ( $date == $dateMarker ) {
1075             return "Ordinary 26";
1076             }
1077              
1078             $dateMarker = closestSunday(
1079             Time::Piece->strptime( $pentecost->year . "-10-05", "%Y-%m-%d" ) );
1080             if ( $date == $dateMarker ) {
1081             return "Ordinary 27";
1082             }
1083              
1084             $dateMarker = closestSunday(
1085             Time::Piece->strptime( $pentecost->year . "-10-12", "%Y-%m-%d" ) );
1086             if ( $date == $dateMarker ) {
1087             return "Ordinary 28";
1088             }
1089              
1090             $dateMarker = closestSunday(
1091             Time::Piece->strptime( $pentecost->year . "-10-19", "%Y-%m-%d" ) );
1092             if ( $date == $dateMarker ) {
1093             return "Ordinary 29";
1094             }
1095              
1096             $dateMarker = closestSunday(
1097             Time::Piece->strptime( $pentecost->year . "-10-26", "%Y-%m-%d" ) );
1098             if ( $date == $dateMarker ) {
1099             return "Ordinary 30";
1100             }
1101              
1102             $dateMarker = closestSunday(
1103             Time::Piece->strptime( $pentecost->year . "-11-02", "%Y-%m-%d" ) );
1104             if ( $date == $dateMarker ) {
1105             return "Ordinary 31";
1106             }
1107              
1108             $dateMarker = closestSunday(
1109             Time::Piece->strptime( $pentecost->year . "-11-09", "%Y-%m-%d" ) );
1110             if ( $date == $dateMarker ) {
1111             return "Ordinary 32";
1112             }
1113              
1114             $dateMarker = closestSunday(
1115             Time::Piece->strptime( $pentecost->year . "-11-16", "%Y-%m-%d" ) );
1116             if ( $date == $dateMarker ) {
1117             return "Ordinary 33";
1118             }
1119              
1120             $dateMarker = closestSunday(
1121             Time::Piece->strptime( $pentecost->year . "-11-23", "%Y-%m-%d" ) );
1122             if ( $date == $dateMarker ) {
1123             return "Christ the King";
1124             }
1125              
1126             confess "There are no further Sundays of Ordinary Time.";
1127             }
1128              
1129             =head2 _determineDay
1130              
1131             Private method that takes the Time::Piece data given at construction and, using other private methods, determines the name of the Feast Day or Sunday in the lectionary. If the date given at construction is a fixed feast, that day will be returned. If the date given is a special feast -- e.g. Easter, Ash Wednesday, etc. -- or a Sunday the name of that day will be returned. If the date isn't a special feast or a Sunday the date represented as a string will be returned as the name with no associated readings.
1132              
1133             =cut
1134              
1135             sub _determineDay {
1136             my $date = shift;
1137             my $lectionary = shift;
1138             my $includeFeasts = shift;
1139              
1140             my $advent = shift;
1141             my $easter = shift;
1142              
1143             #Is the date in Advent?
1144             if ( $date == $advent->firstSunday ) {
1145             return ( commonName => "The First Sunday in Advent", type => 'Sunday' );
1146             }
1147             elsif ( $date == $advent->secondSunday ) {
1148             return (
1149             commonName => "The Second Sunday in Advent",
1150             type => 'Sunday'
1151             );
1152             }
1153             elsif ( $date == $advent->thirdSunday ) {
1154             return ( commonName => "The Third Sunday in Advent", type => 'Sunday' );
1155             }
1156             elsif ( $date == $advent->fourthSunday ) {
1157             return (
1158             commonName => "The Fourth Sunday in Advent",
1159             type => 'Sunday'
1160             );
1161             }
1162              
1163             #Is the date Easter Sunday?
1164             if ( $date == $easter ) {
1165             return ( commonName => "Easter Day", type => 'fixedFeast' );
1166             }
1167              
1168             #Determine when Ash Wednesday is
1169             my $ashWednesday = _determineAshWednesday($easter);
1170             if ( $date == $ashWednesday ) {
1171             return ( commonName => "Ash Wednesday", type => 'fixedFeast' );
1172             }
1173              
1174             #Holy Week
1175             my $holyWeekDay = _determineHolyWeek( $date, $easter );
1176             if ($holyWeekDay) {
1177             return ( commonName => $holyWeekDay, type => 'fixedFeast' );
1178             }
1179              
1180             #Easter Week
1181             my $easterWeekDay = _determineEasterWeek( $date, $easter );
1182             if ($easterWeekDay) {
1183             return ( commonName => $easterWeekDay, type => 'fixedFeast' );
1184             }
1185              
1186             #Ascension is 40 days after Easter
1187             my $ascension = _determineAscension($easter);
1188             if ( $date == $ascension ) {
1189             return ( commonName => "Ascension Day", type => 'fixedFeast' );
1190             }
1191              
1192             #Pentecost is 50 days after Easter
1193             my $pentecost = _determinePentecost($easter);
1194             if ( $date == $pentecost ) {
1195             return ( commonName => "Pentecost", type => 'fixedFeast' );
1196             }
1197              
1198             #Feast Day Celebrations
1199             if($includeFeasts eq 'yes'){
1200             my %feastDay = _determineFeasts( $date, $lectionary );
1201             if ( $feastDay{commonName} ) {
1202             return ( commonName => $feastDay{commonName}, type => $feastDay{type} );
1203             }
1204             }
1205              
1206             #If the date isn't a Sunday and we've determined it is not a fixed holiday
1207             #then there are no readings for that day.
1208             if ( $date->wday != 1 ) {
1209             return (
1210             commonName => $date->fullday . ', '
1211             . $date->fullmonth . ' '
1212             . $date->mday . ', '
1213             . $date->year,
1214             type => 'noLect'
1215             );
1216             }
1217              
1218             #Sundays of the Liturgical Year
1219             if ( $date < $ashWednesday ) {
1220             return (
1221             commonName =>
1222             _determineChristmasEpiphany( $date, $advent, $ashWednesday ),
1223             type => 'Sunday'
1224             );
1225             }
1226              
1227             if ( $date < $easter ) {
1228             return (
1229             commonName => _determineLent( $date, $ashWednesday ),
1230             type => 'Sunday'
1231             );
1232             }
1233              
1234             if ( $date > $easter && $date < $pentecost ) {
1235             return (
1236             commonName => _determineEasterSeason( $date, $easter ),
1237             type => 'Sunday'
1238             );
1239             }
1240              
1241             if ( $date > $pentecost ) {
1242             return (
1243             commonName => _determineOrdinary( $date, $pentecost ),
1244             type => 'Sunday'
1245             );
1246             }
1247             }
1248              
1249             =head1 AUTHOR
1250              
1251             Michael Wayne Arnold, C<< <marmanold at cpan.org> >>
1252              
1253             =head1 BUGS
1254              
1255             Please report any bugs or feature requests to C<bug-date-lectionary at rt.cpan.org>, or through
1256             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Date-Lectionary-Day>. I will be notified, and then you'll
1257             automatically be notified of progress on your bug as I make changes.
1258              
1259             =head1 SUPPORT
1260              
1261             You can find documentation for this module with the perldoc command.
1262              
1263             perldoc Date::Lectionary::Day
1264              
1265              
1266             You can also look for information at:
1267              
1268             =over 4
1269              
1270             =item * RT: CPAN's request tracker (report bugs here)
1271              
1272             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Date-Lectionary-Day>
1273              
1274             =item * AnnoCPAN: Annotated CPAN documentation
1275              
1276             L<http://annocpan.org/dist/Date-Lectionary-Day>
1277              
1278             =item * CPAN Ratings
1279              
1280             L<http://cpanratings.perl.org/d/Date-Lectionary-Day>
1281              
1282             =item * Search CPAN
1283              
1284             L<http://search.cpan.org/dist/Date-Lectionary-Day/>
1285              
1286             =back
1287              
1288             =head1 ACKNOWLEDGEMENTS
1289              
1290             Many thanks to my beautiful wife, Jennifer, and my amazing daughter, Rosemary. But, above all, SOLI DEO GLORIA!
1291              
1292             =head1 LICENSE AND COPYRIGHT
1293              
1294             Copyright 2017 Michael Wayne Arnold.
1295              
1296             This program is free software; you can redistribute it and/or modify it
1297             under the terms of either: the GNU General Public License as published
1298             by the Free Software Foundation; or the Artistic License.
1299              
1300             See L<http://dev.perl.org/licenses/> for more information.
1301              
1302              
1303             =cut
1304              
1305             __PACKAGE__->meta->make_immutable;
1306              
1307             1; # End of Date::Lectionary::Day