#!/usr/bin/perl -w use strict; use warnings; use Time::Local; use Test::More "no_plan"; test(); sub test { is ( identify_quarter(1), 1); is ( identify_quarter(2), 1); is ( identify_quarter(3), 1); is ( identify_quarter(4), 2); is ( identify_quarter(5), 2); is ( identify_quarter(6), 2); is ( identify_quarter(7), 3); is ( identify_quarter(8), 3); is ( identify_quarter(9), 3); is ( identify_quarter(10), 4); is ( identify_quarter(11), 4); is ( identify_quarter(12), 4); is_deeply ( [ get_previous_quarter(2011,4)],[(2011,3)]); is_deeply ( [ get_previous_quarter(2011,4)],[(2011,3)]); is_deeply ( [ get_previous_quarter(2011,3)],[(2011,2)]); is_deeply ( [ get_previous_quarter(2011,1)],[(2010,4)]); is ( last_month_of_quarter(1), 3); is ( last_month_of_quarter(2), 6); is ( last_month_of_quarter(3), 9); is ( last_month_of_quarter(4), 12); is_deeply ( [next_month(2011,1)] , [ (2011,2) ] ); is_deeply ( [next_month(2011,12)] , [ (2012,1) ] ); is ( last_day_of_month(2011,01), 31); is ( last_day_of_month(2011,02), 28); is ( last_day_of_month(2012,02), 29); is_deeply ( [ get_last_day_of_prev_quarter(2011,1,1) ],[ (2010,12,31) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,2,1) ],[ (2010,12,31) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,3,1) ],[ (2010,12,31) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,3,1) ],[ (2010,12,31) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,4,1) ],[ (2011,3,31) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,5,1) ],[ (2011,3,31) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,6,1) ],[ (2011,3,31) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,7,1) ],[ (2011,6,30) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,8,1) ],[ (2011,6,30) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,9,1) ],[ (2011,6,30) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,10,1) ],[ (2011,9,30) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,11,1) ],[ (2011,9,30) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,12,1) ],[ (2011,9,30) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2011,9,1) ],[ (2011,6,30) ] ); is_deeply ( [ get_last_day_of_prev_quarter(2012,1,1) ],[ (2011,12,31) ] ); is_deeply ( [ get_last_bus_day_of_prev_quarter(2011,9,1) ],[ (2011,6,30) ]); is_deeply ( [ get_last_bus_day_of_prev_quarter(2012,1,1) ],[ (2011,12,30) ]); } sub get_last_day_of_prev_quarter { my ($yyyy,$mm,$dd) = @_; my ($year, $quarter) = get_previous_quarter($yyyy,identify_quarter($mm)); my $month = $quarter * 3; my $day = last_day_of_month($year,$month); return ($year,$month,$day); } sub get_last_bus_day_of_prev_quarter { my ($yyyy,$mm,$dd) = @_; my ($year,$month,$day) = get_last_day_of_prev_quarter($yyyy,$mm,$dd); my $epoch = timelocal(0,0,12, $day, $month-1, $year-1900); my @timestuff = localtime($epoch); # # If the day is a Sunday (0) or a Saturday (6) # # Subtract 1 or 2 days worth of seconds, then convert # back. my $date_diff = 0; if ($timestuff[6] == 0) { $date_diff = 2; } if ($timestuff[6] == 6) { $date_diff = 1; } if ($date_diff > 0) { $epoch = $epoch - (60*60*24) * $date_diff; } @timestuff = localtime($epoch); return ($timestuff[5] + 1900, $timestuff[4] + 1,$timestuff[3]); } sub last_day_of_month { # This logic is moronic, but it works. # To get the last day of a given month, first take the first day # of the first month, convert it to epoch seconds, subtract a day # (86400 seconds), then re-convert back and pull the "day of month" # element out of the list. my ($yyyy,$mm) = @_; my ($year,$month) = next_month($yyyy,$mm); my $epoch = timelocal(0,0,12,1, $month-1, $year-1900); $epoch = $epoch - ( 60*60*24 ); my @timestuff = localtime($epoch); return $timestuff[3]; } sub next_month { my ($yyyy,$mm) = @_; if ($mm == 12) { $mm = 1; $yyyy++; } else { $mm++; } return ($yyyy,$mm); } sub get_previous_quarter { my ($yyyy,$quarter) = @_; if ($quarter == 1) { $quarter = 4; $yyyy--; } else { $quarter--; } return ($yyyy,$quarter); } sub identify_quarter { my ($mm) = @_; return int(($mm-1) / 3) +1; }