#!/usr/bin/perl use warnings; use strict; use v5.10; use DateTime; my $today; my $requested_date = shift; my ( $req_year, $req_month, $req_day ) = split( '-', $requested_date ) if $requested_date; main() unless caller; sub main { $today = DateTime->today; if ( $req_year && $req_month && $req_day ) { $today = eval { return DateTime->new( year => $req_year, month => $req_month, day => $req_day ); }; if ($@) { say "$requested_date isn't a real date, exiting.."; exit; } } my $message = is_today_payday($today); if ($message) { say $message; exit; } #print "Checking the payday for date " . $today->ymd() . "\n"; my $dt = find_nominal_payday($today); #print "Nominal payday is " . $dt->ymd . "\n"; $dt = adjust_for_dayoff($dt); #print "Actual pay date is " . $dt->ymd . "\n"; my $duration = $dt->subtract_datetime($today); my $days_until = $duration->in_units('days'); if ( $days_until < 0 ) { #say "Got negative duration, adding a week and trying again.."; #print "Checking the payday for date " . $today->ymd() . "\n"; $dt = find_nominal_payday( $today->clone()->add( weeks => 1 ) ); #print "Nominal payday is " . $dt->ymd . "\n"; $dt = adjust_for_dayoff($dt); #print "Actual pay date is " . $dt->ymd . "\n"; $duration = $dt->subtract_datetime($today); $days_until = $duration->in_units('days'); } my $date_paid = $dt->mdy(); my $weekday = $dt->day_name; say "Payday is $days_until day(s) away on $weekday, $date_paid"; return; } sub is_today_payday { my $today = shift; my $dt = find_nominal_payday($today); $dt = adjust_for_dayoff($dt); if ( $today == $dt ) { if ( caller() ) { return "Today is payday! " . $today->ymd(); } else { return "\x0313,14Today is payday!\x03"; } } return 0; } sub adjust_for_dayoff { my $dt = shift; # Adjust for Fri. if ( $dt->day_of_week == 5 ) { while ( check_for_holiday($dt) ) { $dt->subtract( days => 1 ); } } # Adjust for Sat. if ( $dt->day_of_week == 6 ) { $dt->subtract( days => 1 ); while ( check_for_holiday($dt) ) { $dt->subtract( days => 1 ); } } # Adjust for Sun. Paid on Thursday if ( $dt->day_of_week == 7 ) { $dt->subtract( days => 2 ); while ( check_for_holiday($dt) ) { $dt->subtract( days => 1 ); } } # Adjust for Mon. Paid on Sat. if ( $dt->day_of_week == 1 ) { $dt->subtract( days => 2 ); while ( check_for_holiday($dt) ) { $dt->subtract( days => 1 ); } } return $dt; } sub find_nominal_payday { my ($dt) = @_; if ( $dt->day < 10 ) { return DateTime->new( year => $dt->year, month => $dt->month, day => 10, ); } elsif ( $dt->day < 25 ) { return DateTime->new( year => $dt->year, month => $dt->month, day => 25, ); } else { return DateTime->new( year => $dt->year, month => $dt->month, day => 10, )->add( months => 1 ); } } sub check_for_holiday { my $today = shift; # There are 7 cP holidays my @holidays; my $year = $today->year(); # 1 - New years push( @holidays, dt_for_holiday( $year, 1, 1 ) ); # 2 - Memorial Day (last Monday of May) push( @holidays, dt_for_memorial_day($year) ); # 3 - Independence Day push( @holidays, dt_for_holiday( $year, 7, 4 ) ); # 4 - Labor Day push( @holidays, dt_for_labor_day($year) ); # 5 - Thanksgiving Day push( @holidays, dt_for_thanksgiving_day($year) ); # 6 - Day after Thanksgiving push( @holidays, dt_for_thanksgiving_day($year)->add( days => 1 ) ); # 7 - Christmas Day push( @holidays, dt_for_holiday( $year, 12, 25 ) ); foreach my $holiday (@holidays) { if ( $today->ymd() eq $holiday->ymd() ) { return 1; } } return 0; } sub dt_for_holiday { # Observe on Friday if it's on Saturday, Monday if it's on Sunday my ( $year, $month, $day ) = @_; my $dt = DateTime->new( year => $year, month => $month, day => $day ); my $dow = $dt->day_of_week(); if ( $dow == 6 ) { return $dt->subtract( days => 1 ); } elsif ( $dow == 7 ) { return $dt->add( days => 1 ); } else { return $dt; } } sub dt_for_memorial_day { # Last Monday of May my $year = shift; my $dt = DateTime->last_day_of_month( year => $year, month => 5 ); my $dow = $dt->day_of_week(); if ( $dow == 1 ) { # Right on the money return $dt; } else { return $dt->subtract( days => ( $dow - 1 ) ); } } sub dt_for_labor_day { # First Monday of September my $year = shift; my $dt = DateTime->new( year => $year, month => 9, day => 1 ); my $dow = $dt->day_of_week(); if ( $dow == 1 ) { # Right on the money return $dt; } else { return $dt->add( days => ( 8 - $dow ) ); } } sub dt_for_thanksgiving_day { # 4th Thursday in November my $year = shift; my $dt = DateTime->new( year => $year, month => 11, day => 1 ); my $dow = $dt->day_of_week(); if ( $dow == 4 ) { #Right on the money return $dt->add( weeks => 3 ); } elsif ( $dow < 4 ) { return $dt->subtract( days => ( $dow + 3 ) )->add( weeks => 3 ); } elsif ( $dow > 4 ) { return $dt->subtract( days => ( $dow - 4 ) )->add( weeks => 3 ); } }