AbleDesign - Programs
Programs > Holiday Calculations
<?php
/* US Holiday Calculations in PHP
* Version 1.02
* by Dan Kaplan <design@abledesign.com>
* Last Modified: April 15, 2001
* ------------------------------------------------------------------------
* The holiday calculations on this page were assembled for
* use in MyCalendar: http://abledesign.com/programs/MyCalendar/
*
* USE THIS LIBRARY AT YOUR OWN RISK; no warranties are expressed or
* implied. You may modify the file however you see fit, so long as
* you retain this header information and any credits to other sources
* throughout the file. If you make any modifications or improvements,
* please send them via email to Dan Kaplan <design@abledesign.com>.
* ------------------------------------------------------------------------
*/
// Gregorian Calendar = 1583 or later
if (!$_GET["y"] || ($_GET["y"] < 1583) || ($_GET["y"] > 4099)) {
$_GET["y"] = date("Y",time()); // use the current year if nothing is specified
}
function format_date($year, $month, $day) {
// pad single digit months/days with a leading zero for consistency (aesthetics)
// and format the date as desired: YYYY-MM-DD by default
if (strlen($month) == 1) {
$month = "0". $month;
}
if (strlen($day) == 1) {
$day = "0". $day;
}
$date = $year ."-". $month ."-". $day;
return $date;
}
// the following function get_holiday() is based on the work done by
// Marcos J. Montes: http://www.smart.net/~mmontes/ushols.html
//
// if $week is not passed in, then we are checking for the last week of the month
function get_holiday($year, $month, $day_of_week, $week="") {
if ( (($week != "") && (($week > 5) || ($week < 1))) || ($day_of_week > 6) || ($day_of_week < 0) ) {
// $day_of_week must be between 0 and 6 (Sun=0, ... Sat=6); $week must be between 1 and 5
return FALSE;
} else {
if (!$week || ($week == "")) {
$lastday = date("t", mktime(0,0,0,$month,1,$year));
$temp = (date("w",mktime(0,0,0,$month,$lastday,$year)) - $day_of_week) % 7;
} else {
$temp = ($day_of_week - date("w",mktime(0,0,0,$month,1,$year))) % 7;
}
if ($temp < 0) {
$temp += 7;
}
if (!$week || ($week == "")) {
$day = $lastday - $temp;
} else {
$day = (7 * $week) - 6 + $temp;
}
return format_date($year, $month, $day);
}
}
function observed_day($year, $month, $day) {
// sat -> fri & sun -> mon, any exceptions?
//
// should check $lastday for bumping forward and $firstday for bumping back,
// although New Year's & Easter look to be the only holidays that potentially
// move to a different month, and both are accounted for.
$dow = date("w", mktime(0, 0, 0, $month, $day, $year));
if ($dow == 0) {
$dow = $day + 1;
} elseif ($dow == 6) {
if (($month == 1) && ($day == 1)) { // New Year's on a Saturday
$year--;
$month = 12;
$dow = 31;
} else {
$dow = $day - 1;
}
} else {
$dow = $day;
}
return format_date($year, $month, $dow);
}
function calculate_easter($y) {
// In the text below, 'intval($var1/$var2)' represents an integer division neglecting
// the remainder, while % is division keeping only the remainder. So 30/7=4, and 30%7=2
//
// This algorithm is from Practical Astronomy With Your Calculator, 2nd Edition by Peter
// Duffett-Smith. It was originally from Butcher's Ecclesiastical Calendar, published in
// 1876. This algorithm has also been published in the 1922 book General Astronomy by
// Spencer Jones; in The Journal of the British Astronomical Association (Vol.88, page
// 91, December 1977); and in Astronomical Algorithms (1991) by Jean Meeus.
$a = $y%19;
$b = intval($y/100);
$c = $y%100;
$d = intval($b/4);
$e = $b%4;
$f = intval(($b+8)/25);
$g = intval(($b-$f+1)/3);
$h = (19*$a+$b-$d-$g+15)%30;
$i = intval($c/4);
$k = $c%4;
$l = (32+2*$e+2*$i-$h-$k)%7;
$m = intval(($a+11*$h+22*$l)/451);
$p = ($h+$l-7*$m+114)%31;
$EasterMonth = intval(($h+$l-7*$m+114)/31); // [3 = March, 4 = April]
$EasterDay = $p+1; // (day in Easter Month)
return format_date($y, $EasterMonth, $EasterDay);
}
/////////////////////////////////////////////////////////////////////////////
// end of calculation functions; place the dates you wish to calculate below
/////////////////////////////////////////////////////////////////////////////
?>
<form action="<?php echo($PHP_SELF); ?>" method="get">
<b>Enter a Year:</b> <input type="text" name="y" value="<?php echo ($_GET["y"]); ?>" size="4" maxlength="4"> <input type="submit" value="Go">
</form>
<?php
// format to use:
//
// get_holiday("year", "month", "day_of_week", "week_of_month");
// get_holiday("year", "month", "day_of_week); // no 4th field indicates last week of month check
// format_date("year", "month", "day");
echo "<p><b>". $_GET["y"] ." Holidays</b></p>";
echo "<ul>";
echo "<li>New Year's Day = ". format_date($_GET["y"], 1, 1);
echo "<br>New Year's Day Observed = ". observed_day($_GET["y"], 1, 1);
echo "<li>Martin Luther King Day Observed (Third Monday in January) = ". get_holiday($_GET["y"], 1, 1, 3);
echo "<li>Valentine's Day = ". format_date($_GET["y"], 2, 14);
echo "<li>President's Day Observed (Third Monday in February) = ". get_holiday($_GET["y"], 2, 1, 3);
echo "<li>St. Patrick's Day = ". format_date($_GET["y"], 3, 17);
echo "<li>Easter = ". calculate_easter($_GET["y"]);
echo "<li>Cinco De Mayo = ". format_date($_GET["y"], 5, 5);
echo "<li>Memorial Day Observed (Last Monday in May) = ". get_holiday($_GET["y"], 5, 1);
echo "<li>Independence Day = ". format_date($_GET["y"], 7, 4);
echo "<br>Independence Day Observed = ". observed_day($_GET["y"], 7, 4);
echo "<li>Labor Day Observed (First Monday in September) = ". get_holiday($_GET["y"], 9, 1, 1);
echo "<li>Columbus Day Observed (Second Monday in October) = ". get_holiday($_GET["y"], 10, 1, 2);
echo "<li>Halloween = ". format_date($_GET["y"], 10, 31);
// Veteran's Day Observed - November 11th ?
echo "<li>Thanksgiving (Fourth Thursday in November) = ". get_holiday($_GET["y"], 11, 4, 4);
echo "<li>Christmas Day = ". format_date($_GET["y"], 12, 25);
echo "</ul>";
?>
Alternative Calculations:
function get_holiday($year, $month, $day_of_week, $week_of_month) {
// i.e. find the 3rd Monday in January, 2000
if (($week_of_month > 5) || ($week_of_month < 1) || ($day_of_week > 6) || ($day_of_week < 0)) {
// should check any month/week combo first against $lastday...
return FALSE;
} else {
$firstday = date("w", mktime(0, 0, 0, $month, 1, $year));
$lastday = date("t", mktime(0, 0, 0, $month, 1, $year));
// start by finding the first occurence in the month of that $day_of_week #
// then add appropriate number of weeks
if ($firstday > $day_of_week) {
// means we need to jump to the second week to find the first $day_of_week
$diff = ($firstday - $day_of_week);
$first_dow = (7 - $diff) + 1;
$day = $first_dow + (($week_of_month * 7) - 7);
} elseif ($firstday < $day_of_week) {
// correct week, now move forward to specified day
$day = ($day_of_week - $firstday + 1) + (($week_of_month * 7) - 7);
} else {
// correct day in first week ($firstday = $day_of_week)
$day = 1 + (($week_of_month * 7) - 7);
}
$date = format_date($year, $month, $day);
return $date;
}
}
function count_dows_in_month($year, $month, $day_of_week) {
// count how many weeks in the month have a specified day, such as Monday.
// we know there will be 4 or 5, so no need to check for $weeks<4 or $weeks>5
$firstday = date("w", mktime(0, 0, 0, $month, 1, $year));
$lastday = date("t", mktime(0, 0, 0, $month, 1, $year));
if ($firstday > $day_of_week) {
// means we need to jump to the second week to find the first $day_of_week
$d = (7 - ($firstday - $day_of_week)) + 1;
} elseif ($firstday < $day_of_week) {
// correct week, now move forward to specified day
$d = ($day_of_week - $firstday + 1);
} else { // $firstday = $day_of_week
// correct day in first week
$d = ($firstday - 1);
}
$d += 28; // jump to the 5th week and see if the day exists
if ($d > $lastday) {
$weeks = 4;
} else {
$weeks = 5;
}
return $weeks;
}
To Use Alternative Calculations: count_dows_in_month() will tell you the number of weeks in a given month, which can then be fed into the alternative get_holiday() to determine something like the last Monday in May, i.e. Memorial Day.