Date-Lectionary

 view release on metacpan or  search on metacpan

lib/Date/Lectionary/Day.pm  view on Meta::CPAN

package Date::Lectionary::Day;

use v5.22;
use strict;
use warnings;

use Moose;
use MooseX::StrictConstructor;
use MooseX::Aliases;
use Carp;
use Try::Catch;
use Time::Piece;
use Time::Seconds;
use Date::Advent;
use Date::Easter;
use Date::Lectionary::Time qw(nextSunday prevSunday closestSunday);
use namespace::autoclean;
use Moose::Util::TypeConstraints;
use File::Share ':all';
use XML::LibXML;

=head1 NAME

Date::Lectionary::Day - Determines the Day in the Christian Liturgical Year

=head1 VERSION

Version 1.20200203

=cut

use version; our $VERSION = version->declare("v1.20200203");

=head1 SYNOPSIS

A helper object for Date::Lectionary to determine the liturgical name(s) and type for the given day according to a given lectionary.

=cut

enum 'DayType',        [qw(fixedFeast moveableFeast Sunday noLect)];
enum 'LectionaryType', [qw(acna rcl)];
enum 'MultiLect',      [qw(yes no)];
enum 'IncludeFeasts',  [qw(yes no)];
enum 'XianSeason',     [qw(Advent Christmas Epiphany Ordinary Lent Easter Pentecost NaN)];
no Moose::Util::TypeConstraints;

=head1 SUBROUTINES/METHODS/ATTRIBUTES

=head2 ATTRIBUTES

=head3 date

The Time::Piece object date given at object construction.

=head3 lectionary

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.

=head3 type

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 f...

=head3 name

The name of the day in the lectionary.  For noLect days a String representation of the day is returned as the name.

=head3 alt

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.

=head3 multiLect

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.

=head3 subLects

An ArrayRef of the names of the multiple services that occur on a multiLect day.

=head3 includeFeasts

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.

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...

=head3 season

The liturgical season the day falls within.

Valid values are 'Advent', 'Christmas', 'Epiphany', 'Ordinary', 'Lent', 'Easter', or 'Pentecost'.

If a date is given that is not a Sunday nor a pricipal holy day 'NaN' will be given for the season.

=cut

has 'date' => (
    is       => 'ro',
    isa      => 'Time::Piece',
    required => 1,
);

has 'type' => (
    is       => 'ro',
    isa      => 'DayType',
    writer   => '_setType',
    init_arg => undef,
);

has 'lectionary' => (
    is      => 'ro',
    isa     => 'LectionaryType',
    default => 'acna',
);

has 'displayName' => (
    is       => 'ro',
    isa      => 'Str',
    writer   => '_setDisplayName',
    init_arg => undef,
    alias    => 'name',
);

has 'altName' => (
    is       => 'ro',
    isa      => 'Str',
    writer   => '_setAltName',
    init_arg => undef,
    alias    => 'alt',
);

has 'commonName' => (
    is       => 'ro',
    isa      => 'Str',
    writer   => '_setCommonName',
    init_arg => undef,
);

has 'multiLect' => (
    is       => 'ro',
    isa      => 'MultiLect',
    writer   => '_setMultiLect',
    init_arg => undef,
);

has 'subLects' => (
    is       => 'ro',
    isa      => 'ArrayRef',
    writer   => '_setSubLects',
    init_arg => undef,
);

has 'includeFeasts' => (

lib/Date/Lectionary/Day.pm  view on Meta::CPAN

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.

=cut

sub _determineAdvent {
    my $date = shift;

    my $advent = undef;

    try {
        $advent = Date::Advent->new( date => $date );
        return $advent;
    }
    catch {
        confess "Could not calculate Advent for the given date [" . $date->ymd . "].";
    };
}

=head2 _determineEaster

Private method that takes a four-digit representation of a Common Era year and calculates the date for Easter as a Time::Piece object.

=cut

sub _determineEaster {
    my $easterYear = shift;

    my $easter = undef;

    try {
        my ( $easterMonth, $easterDay ) = easter($easterYear);
        $easter = Time::Piece->strptime( $easterYear . "-" . $easterMonth . "-" . $easterDay, "%Y-%m-%d" );
        return $easter;
    }
    catch {
        confess "Could not calculate Easter for the year [" . $easterYear . "]";
    };
}

=head2 _determineFeasts

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.

=cut

sub _determineFeasts {
    my $date       = shift;
    my $lectionary = shift;

    my $yesterday = $date - ONE_DAY;

    my $yesterdayName;
    if ( $yesterday->wday == 1 ) {
        $yesterdayName = _buildMoveableDays( $yesterday, $lectionary );
    }

    if ($yesterdayName) {
        return (
            commonName => $yesterdayName,
            type       => 'moveableFeast',
            season     => 'NaN'
        );
    }

    my $fixedDayName = _buildFixedDays( $date, $lectionary );
    if ($fixedDayName) {
        return (
            commonName => $fixedDayName->{commonName},
            type       => 'fixedFeast',
            season     => $fixedDayName->{season}
        );
    }

    my $moveableDayName = _buildMoveableDays( $date, $lectionary );
    if ( $moveableDayName && $date->wday != 1 ) {
        return (
            commonName => $moveableDayName,
            type       => 'moveableFeast',
            season     => 'NaN'
        );
    }

    return ( commonName => undef, type => undef, season => 'NaN' );
}

=head2 _buildMoveableDays

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 le...

=cut

sub _buildMoveableDays {
    my $date       = shift;
    my $lectionary = shift;

    #Moveable holidays in January
    if ( $date->mon == 1 ) {
        if ( $date->mday == 18 && $lectionary eq 'acna' ) {
            return "Confession of St. Peter";
        }
        if ( $date->mday == 25 && $lectionary eq 'acna' ) {
            return "Conversion of St. Paul";
        }
    }

    #Moveable holidays in February
    elsif ( $date->mon == 2 ) {
        if ( $date->mday == 2 ) {
            return "The Presentation of Christ in the Temple";
        }
        if ( $date->mday == 24 && $lectionary eq 'acna' ) {
            return "St. Matthias";
        }
    }

    #Moveable holidays in March
    elsif ( $date->mon == 3 ) {
        if ( $date->mday == 19 && $lectionary eq 'acna' ) {
            return "St. Joseph";
        }
        if ( $date->mday == 25 ) {
            return "The Annunciation";
        }
    }

    #Moveable holidays in April
    elsif ( $date->mon == 4 ) {
        if ( $date->mday == 25 && $lectionary eq 'acna' ) {
            return "St. Mark";
        }
    }

    #Moveable holidays in May
    elsif ( $date->mon == 5 ) {
        if ( $date->mday == 1 && $lectionary eq 'acna' ) {
            return "St. Philip & St. James";
        }
        if ( $date->mday == 31 ) {
            return "The Visitation";
        }
    }

    #Moveable holidays in June

lib/Date/Lectionary/Day.pm  view on Meta::CPAN

    }

    #Holy Week
    my $holyWeekDay = _determineHolyWeek( $date, $easter );
    if ($holyWeekDay) {
        return (
            commonName => $holyWeekDay,
            type       => 'fixedFeast',
            season     => 'Lent'
        );
    }

    #Easter Week
    my $easterWeekDay = _determineEasterWeek( $date, $easter );
    if ($easterWeekDay) {
        return (
            commonName => $easterWeekDay,
            type       => 'fixedFeast',
            season     => 'Easter'
        );
    }

    #Ascension is 40 days after Easter
    my $ascension = _determineAscension($easter);
    if ( $date == $ascension ) {
        return (
            commonName => "Ascension Day",
            type       => 'fixedFeast',
            season     => 'Easter'
        );
    }

    #Pentecost is 50 days after Easter
    my $pentecost = _determinePentecost($easter);
    if ( $date == $pentecost ) {
        return (
            commonName => "Pentecost",
            type       => 'fixedFeast',
            season     => 'Pentecost'
        );
    }

    #Feast Day Celebrations
    if ( $includeFeasts eq 'yes' ) {
        my %feastDay = _determineFeasts( $date, $lectionary );
        if ( $feastDay{commonName} ) {
            return (
                commonName => $feastDay{commonName},
                type       => $feastDay{type},
                season     => $feastDay{season}
            );
        }
    }

    #If the date isn't a Sunday and we've determined it is not a fixed holiday
    #then there are no readings for that day.
    if ( $date->wday != 1 ) {
        return (
            commonName => $date->fullday . ', ' . $date->fullmonth . ' ' . $date->mday . ', ' . $date->year,
            type       => 'noLect',
            season     => 'NaN'
        );
    }

    #Sundays of the Liturgical Year
    if ( $date < $ashWednesday ) {
        my %xmasEpiphany = (
            commonName => _determineChristmasEpiphany( $date, $advent, $ashWednesday, $lectionary ),
            type       => 'Sunday'
        );

        if ( $xmasEpiphany{commonName} =~ m/Christmas/ig ) {
            $xmasEpiphany{season} = 'Christmas';
            return %xmasEpiphany;
        }
        else {
            $xmasEpiphany{season} = 'Epiphany';
            return %xmasEpiphany;
        }
    }

    if ( $date < $easter ) {
        return (
            commonName => _determineLent( $date, $ashWednesday ),
            type       => 'Sunday',
            season     => 'Lent'
        );
    }

    if ( $date > $easter && $date < $pentecost ) {
        return (
            commonName => _determineEasterSeason( $date, $easter ),
            type       => 'Sunday',
            season     => 'Easter'
        );
    }

    if ( $date > $pentecost ) {
        return (
            commonName => _determineOrdinary( $date, $pentecost ),
            type       => 'Sunday',
            season     => 'Ordinary'
        );
    }
}

=head1 AUTHOR

Michael Wayne Arnold, C<< <michael at rnold.info> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-date-lectionary at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Date-Lectionary-Day>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Date::Lectionary::Day



( run in 0.541 second using v1.01-cache-2.11-cpan-437f7b0c052 )