package Date::Holidays::US;
use strict;
use warnings;
use base qw{Exporter};
use POSIX; #strftime to calculate wday

our @EXPORT_OK = qw(is_holiday holidays is_us_holiday us_holidays);

our $VERSION = '0.01';

=head1 NAME

Date::Holidays::US - Date::Holidays Adapter for US Federal holidays

=head1 SYNOPSIS

  use Date::Holidays::US qw{is_holiday};
  my $holiday_name = is_holiday($year, $month, $day);

=head1 DESCRIPTION

Date::Holidays Adapter for US Federal holidays back to 1880 with updates from 2022.

=head1 METHODS

=head2 is_holiday

 my $holiday_name = Date::Holidays::US::is_holiday($year, $month, $day);
 print "Holiday: $holiday_name\n";

=cut

sub is_holiday {
  my $year  = shift;
  my $month = shift;
  my $day   = shift;
  my $wday  = POSIX::strftime(qq{%w}, 0, 0, 0, $day, $month-1, $year-1900); #12:00 am

  #5 U.S. Code § 6103 - Holidays
  #The history of federal holidays in the United States dates back to June 28, 1870
  if ($year > 1870 and $month == 1 and $day == 1) {
    return q{New Year's Day};                                          #January 1
  } elsif ($year > 1870 and $month == 1 and $day == 2 and $wday == 1) {
    return q{New Year's Day Observed};                                 #Monday afer January 1

  #observed for the first time on January 20, 1986. - Pub.L. 98–399, 98 Stat. 1475, enacted November 2, 1983
  } elsif ($year >= 1986 and $month == 1 and $day >= 15 and $day <= 21 and $wday == 1) {
    return 'Birthday of Martin Luther King, Jr.'                       #the third Monday in January

  #Inauguration Day - Really only DC and few
  } elsif ($year >= 1965 and $month == 1 and $day == 20 and $year % 4 == 1) {
    return'Inauguration Day'                       #January 20 of each fourth year after 1965
  } elsif ($year >= 1965 and $month == 1 and $day == 21 and $year % 4 == 1 and $wday == 1) {
    return'Inauguration Day Observed'              #When January 20 ... falls on Sunday, the next succeeding day ... observance

  # Washington's Birthday was celebrated on February 22 from 1879 until 1970.
  # in 1968 the Uniform Monday Holiday Act moved it to the third Monday in February
  # The Act was signed into law on June 1, 1968, and took effect on January 1, 1971.
  } elsif ($year >= 1879 and $year < 1971 and $month == 2 and $day == 22) {
    return q{Washington's Birthday};                                   #February 22 from 1879 until 1970
  } elsif ($year >= 1971 and $month == 2 and $day >= 15 and $day <= 21 and $wday == 1) {
    return q{Washington's Birthday};                                   #the third Monday in February

  # Memorial Day/Decoration Day
  } elsif ($year >= 1888 and $year < 1971 and $month == 5 and $day == 30) {
    return 'Decoration Day';                                          #May 30 #TODO: observed?
  } elsif ($year >= 1971 and $month == 5 and $day >= 25 and $day <= 31 and $wday == 1) {
    return 'Memorial Day';                                            #the last Monday in May

  #The day was first recognized as a federal holiday in June 2021, when President
  #Joe Biden signed the Juneteenth National Independence Day Act into law.
  } elsif ($year >= 2021 and $month == 6 and $day == 18 and $wday == 5) {
    return 'Juneteenth National Independence Day Observed';           #Friday before June 19
  } elsif ($year >= 2021 and $month == 6 and $day == 19) {
    return 'Juneteenth National Independence Day';                    #June 19
  } elsif ($year >= 2021 and $month == 6 and $day == 20 and $wday == 1) {
    return 'Juneteenth National Independence Day Observed';           #Monday afer June 19

  #Independence Day

  } elsif ($year >= 1870 and $month == 7 and $day == 3 and $wday == 5) {
    return 'Independence Day Observed';                               #Friday before July 4
  } elsif ($year >= 1870 and $month == 7 and $day == 4) {
    return 'Independence Day';                                        #July 4
  } elsif ($year >= 1870 and $month == 7 and $day == 5 and $wday == 1) {
    return 'Independence Day Observed';                               #Monday afer July 4

  ## Labor Day
  # By 1894, thirty U.S. states were already officially celebrating Labor Day. In that year,
  # Congress passed a bill recognizing the first Monday of September as Labor Day and making
  # it an official federal holiday. President Grover Cleveland signed the bill into law on
  # June 28.[15][4] The federal law, however, only made it a holiday for federal workers.

  } elsif ($year >= 1894 and $month == 9 and $day >= 1 and $day <= 7 and $wday == 1) {
    return 'Labor Day';                                               #the first Monday in September

  ##Columbus Day
  #Uniform Monday Holiday Act
  } elsif ($year >= 1971 and $month == 10 and $day >= 8 and $day <= 14 and $wday == 1) {
    return 'Columbus Day';                                            #the second Monday in October

  ##Veterans Day (>1954)/Armistice Day (<1954)
  #November 11 (1938 to 1970 and >1978)
  #fourth Monday in October (1971 to 1977)

  } elsif ($year >= 1938 and $year < 1954 and $month == 11 and $day == 10 and $wday == 5) {
    return 'Armistice Day Observed';                                   #Friday before November 11
  } elsif ($year >= 1938 and $year < 1954 and $month == 11 and $day == 11) {
    return 'Armistice Day';                                            #November 11
  } elsif ($year >= 1938 and $year < 1954 and $month == 11 and $day == 12 and $wday == 1) {
    return 'Armistice Day Observed';                                   #Monday afer November 11

  } elsif ($year >= 1954 and $year < 1971 and $month == 11 and $day == 10 and $wday == 5) {
    return 'Veterans Day Observed';                                   #Friday before November 11
  } elsif ($year >= 1954 and $year < 1971 and $month == 11 and $day == 11) {
    return 'Veterans Day';                                            #November 11
  } elsif ($year >= 1954 and $year < 1971 and $month == 11 and $day == 12 and $wday == 1) {
    return 'Veterans Day Observed';                                   #Monday afer November 11

  } elsif ($year >= 1971 and $year < 1978 and $month == 10 and $day >= 22 and $day <= 28 and $wday == 1) {
    return 'Veterans Day';                                            #fourth Monday in October

  } elsif ($year >= 1978 and $month == 11 and $day == 10 and $wday == 5) {
    return 'Veterans Day Observed';                                   #Friday before November 11
  } elsif ($year >= 1978 and $month == 11 and $day == 11) {
    return 'Veterans Day';                                            #November 11
  } elsif ($year >= 1978 and $month == 11 and $day == 12 and $wday == 1) {
    return 'Veterans Day Observed';                                   #Monday afer November 11

  ##Thanksgiving Day
  } elsif ($year >= 1870 and $month == 11 and $day >= 22 and $day <= 28 and $wday == 4) {
    return 'Thanksgiving Day';                                        #the fourth Thursday in November.

  ##Christmas Day
  } elsif ($year >= 1870 and $month == 12 and $day == 24 and $wday == 5) {
    return 'Christmas Day Observed';                                  #Friday before December 25
  } elsif ($year >= 1870 and $month == 12 and $day == 25) {
    return 'Christmas Day';                                           #December 25
  } elsif ($year >= 1870 and $month == 12 and $day == 26 and $wday == 1) {
    return 'Christmas Day Observed';                                  #Monday afer December 25

  } elsif ($year >= 1870 and $month == 12 and $day == 31 and $wday == 5) {
    return q{New Year's Day Observed};                                 #Friday before January 1

  } else {
    return undef;
  }
}

=head2 is_us_holiday

=cut

sub is_us_holiday {return is_holiday(@_)};

=head2 holidays

=cut

sub holidays {
  my $year     = shift;
  my %holidays = ();
  #All possible dates that could be a holiday
  my @dates    = qw{
                    0101 0102
                    0115 0116 0117 0118 0119 0120 0121
                    0222
                    0215 0216 0217 0218 0219 0220 0221
                    0525 0526 0527 0528 0529 0530 0531
                    0618 0619 0620
                    0703 0704 0705
                    0901 0902 0903 0904 0905 0906
                    1007 1008 1009 1010 1011 1012 1013
                    1110 1111 1112
                    1022 1023 1024 1025 1026 1027 1028
                    1122 1123 1124 1125 1126 1127 1128
                    1224 1225 1226
                    1231
                 };
  foreach my $date (@dates) {
    my ($month, $day) = $date =~ m/\A(..)(..)\Z/;
    my $name = is_holiday($year, $month, $day);
    $holidays{$date} = $name if $name;
  }
  return \%holidays;
}

=head2 us_holidays

=cut

sub us_holidays {return holidays(@_)};

=head1 SEE ALSO

L<Date::Holidays>, L<Date::Holidays::USFederal>

=head1 AUTHOR

Michael R. Davis, MRDVT

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2022 by Michael R. Davis

MIT License

=cut

1;
