#!/usr/bin/perl -w use strict; use warnings; use Time::Local; use Test::More "no_plan"; test(); sub test { ok ( identify_quarter("201101") eq "2011Q1"); ok ( identify_quarter("201102") eq "2011Q1"); ok ( identify_quarter("201103") eq "2011Q1"); ok ( identify_quarter("201104") eq "2011Q2"); ok ( identify_quarter("201105") eq "2011Q2"); ok ( identify_quarter("201106") eq "2011Q2"); ok ( identify_quarter("201107") eq "2011Q3"); ok ( identify_quarter("201108") eq "2011Q3"); ok ( identify_quarter("201109") eq "2011Q3"); ok ( identify_quarter("201110") eq "2011Q4"); ok ( identify_quarter("201111") eq "2011Q4"); ok ( identify_quarter("201112") eq "2011Q4"); ok ( identify_quarter("201201") eq "2012Q1"); ok ( get_previous_quarter("2011Q4") eq "2011Q3"); ok ( get_previous_quarter("2011Q3") eq "2011Q2"); ok ( get_previous_quarter("2012Q1") eq "2011Q4"); ok ( last_month_of_quarter(1) == 3); ok ( last_month_of_quarter(2) == 6); ok ( last_month_of_quarter(3) == 9); ok ( last_month_of_quarter(4) == 12); ok ( get_next_month("201101") eq "201102"); ok ( get_next_month("201112") eq "201201"); ok ( last_day_of_month("201101") == 31); ok ( last_day_of_month("201202") == 29); ok ( get_last_month_of_quarter("2011Q1") eq "201103"); ok ( get_last_month_of_quarter("2011Q2") eq "201106"); ok ( get_last_month_of_quarter("2011Q3") eq "201109"); ok ( get_last_month_of_quarter("2011Q4") eq "201112"); ok ( get_last_month_of_quarter("2012Q1") eq "201203"); ok ( get_last_day_of_prev_quarter("20110101") eq "20101231"); ok ( get_last_day_of_prev_quarter("20110203") eq "20101231"); ok ( get_last_day_of_prev_quarter("20110310") eq "20101231"); ok ( get_last_day_of_prev_quarter("20110331") eq "20101231"); ok ( get_last_day_of_prev_quarter("20110415") eq "20110331"); ok ( get_last_day_of_prev_quarter("20110516") eq "20110331"); ok ( get_last_day_of_prev_quarter("20110629") eq "20110331"); ok ( get_last_day_of_prev_quarter("20110731") eq "20110630"); ok ( get_last_day_of_prev_quarter("20110805") eq "20110630"); ok ( get_last_day_of_prev_quarter("20110930") eq "20110630"); ok ( get_last_day_of_prev_quarter("20111030") eq "20110930"); ok ( get_last_day_of_prev_quarter("20111130") eq "20110930"); ok ( get_last_day_of_prev_quarter("20111231") eq "20110930"); ok ( get_last_bus_day_of_prev_quarter("20110901") eq "20110630"); ok ( get_last_bus_day_of_prev_quarter("20120101") eq "20111230"); } sub get_last_day_of_prev_quarter { my ($date) = @_; my $this_quarter = identify_quarter($date); my $quarter = get_previous_quarter($this_quarter); my $last_month_of_quarter = get_last_month_of_quarter($quarter); my $ldom = last_day_of_month($last_month_of_quarter); return $last_month_of_quarter . $ldom; } sub get_last_bus_day_of_prev_quarter { my ($date) = @_; my $new_date = get_last_day_of_prev_quarter($date); my $year = substr($new_date,0,4); my $month = substr($new_date,4,2); my $day = substr($new_date,6,2); 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 from the day. 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); # # Rebuild the YYYYMMDD string from the localtime bits. # return $timestuff[5] + 1900 . sprintf( "%02d",$timestuff[4] + 1) . "$timestuff[3]"; } sub get_last_month_of_quarter { my ($yyyymm) = @_; return substr($yyyymm,0,4) . sprintf("%02d",last_month_of_quarter(substr($yyyymm,5,1))); } sub last_month_of_quarter { my ($quarter) = @_; return $quarter * 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 ($yyyymm) = @_; my $next_day = get_next_month($yyyymm) . "01"; my $epoch = timelocal(0,0,12,1, substr($next_day,4,2)-1, substr($next_day,0,4)-1900); $epoch = $epoch - ( 60*60*24 ); my @timestuff = localtime($epoch); return $timestuff[3]; } sub get_next_month { my ($yyyymm) = @_; my $year = substr($yyyymm,0,4); my $month = substr($yyyymm,4,2); if ($month == 12) { $month = 1; $year++; } else { $month++; } return $year . sprintf "%02d",$month; } sub get_previous_quarter { my ($quarter) = @_; my $year = substr($quarter,0,4); my $q = substr($quarter,5,1); if ($q == 1) { $q = 4; $year--; } else { $q--; } return "$year" . "Q$q"; } sub identify_quarter { my ($date) = @_; my $year = substr ($date,0,4); my $month = substr ($date,4,2); my $quarter = int(($month-1) / 3) +1; return "$year" . "Q$quarter"; }