Source for file BitDate.php
Documentation is available at BitDate.php
* Created by: Jeremy Jongsma (jjongsma@tickchat.com)
* Created on: Sat Jul 26 11:51:31 CDT 2003
* This class takes care of all time/date conversions for
* storing dates in the DB and displaying dates to the user.
* - Dates will always stored in UTC in the database
* - Display dates will be computed based on the preferred
* display offset specified in the constructor
* @todo As of 1.7, dates are still stored in server local time.
* This should be changed for 1.7.1 (requires many module changes).
* UTC offset to use for display
* Current UTC offset of server
* @param int desired offset for date display, in minutes
function BitDate($_display_offset = 0) {
$this->server_offset = mktime(0,0,0,1,2,1970) - gmmktime(0,0,0,1,2,1970);
* Retrieves the user's preferred offset for displaying dates.
* @param int the logged-in user.
* @return int the preferred offset to UTC or 0 for straight UTC display
// Cache preference from DB
// Default to UTC get_display_offset
// Load pref from DB if cache is empty
$display_tz = $gBitUser->getPreference('site_display_utc', "Local");
// Recompute offset each request in case DST kicked in
if ( $display_tz == "Local" && isset ($_COOKIE["tz_offset"]))
else if ( $display_tz == "Fixed" )
$this->display_offset = $gBitUser->getPreference( 'site_display_timezone', 0 );
$dtNow = new DateTime( "now" );
* Convert a UTC timestamp to the preferred display offset.
* @param timestamp ISO format date
* yYYY-mM-dD hH:mM:sS.s ( Lower case letters optional, but should be 0 )
* @return int Seconds count based on 1st Jan 1970<br>
if ( $gBitUser->getPreference('site_display_utc', "Local") == "Fixed" && class_exists( 'DateTime' ) ) {
$dateTimeUser = new DateTime( '@'. $_timestamp );
$dateTimeUser = new DateTime( $_timestamp );
$dateTimeUserZone = new DateTimeZone( $gBitUser->getPreference( 'site_display_timezone', 'UTC' ) );
* Convert a display-offset timestamp to UTC.
* @param timestamp ISO format date
* yYYY-mM-dD hH:mM:sS.s ( Lower case letters optional, but should be 0 )
* @return int Seconds count based on 1st Jan 1970<br>
if ( $gBitUser->getPreference('site_display_utc', "Local") == "Fixed" ) {
$dateTimeUser = new DateTime( '@'. $_timestamp );
$dateTimeUser = new DateTime( $_timestamp );
$dateTimeUserZone = new DateTimeZone( $gBitUser->getPreference( 'site_display_timezone', 'UTC' ) );
* Convert a UTC timestamp to the local server time.
* @param timestamp UTC timestamp to convert.
* @return timestamp Server timestamp.
* Convert a local server timestamp to UTC.
* @param timestamp Server timestamp to convert.
* @return timestamp UTC timestamp.
* Retrieve a current UTC timestamp as Unix epoch.
* Retrieve a current UTC Timestamp as an ISO formated date/time.
* @return string Current ISO formated date/time
return $this->date("Y-m-d H:i:s",time(),true);
* Retrieve a current UTC Date as an ISO formated date
* @return string Current ISO formated date
return $this->date("Y-m-d",time(),true);
* Get the name of the current timezone.
* Currently, only "UTC" or an empty string (Local).
* @return string Current timezone
* Convert ISO date to numberic timestamp.
* @param string ISO format date
* yYYY-mM-dD hH:mM:sS.s ( Lower case letters optional, but should be 0 )
* @return int Seconds count based on 1st Jan 1970<br>
* returns $iso_date if it is a number, or 0 if format invalid
"|^([0-9]{1,2})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{3,4}), ?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,8}))?|",
if (!isset ($rr[5])) $ret = $this->gmmktime(0,0,0,$rr[2],$rr[1],$rr[3]);
else $ret = @$this->gmmktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[1],$rr[3]);
"|^([0-9]{3,4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
if (!isset ($rr[5])) $ret = $this->gmmktime(0,0,0,$rr[2],$rr[3],$rr[1]);
else $ret = @$this->gmmktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
* Returns day of week, 0 = Sunday,... 6=Saturday.
* Algorithm from PEAR::Date_Calc
Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and
proclaimed that from that time onwards 3 days would be dropped from the calendar
Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian).
($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) $greg_correction = 3;
$day = floor((13 * $month - 1) / 5) +
floor(($year % 100) / 4) +
floor(($year / 100) / 4) - 2 *
floor($year / 100) + 77 + $greg_correction;
return $day - 7 * floor($day / 7);
* Returns week of year, 1 = first week of year.
* Algorithm from PEAR::Date_Calc
* This needs to be checked out for both start day and early date rules
$week_number = intval($parts[1]);
if ( $week_number == 0 ) $week_number = 53;
* Checks for leap year, returns true if it is. No 2-digit year check. Also
* handles julian calendar correctly.
if ($year % 4 != 0) return false;
// if gregorian calendar (>1582), century not-divisible by 400 is not leap
} else if ($year > 1582 && $year % 100 == 0 ) {
* checks for leap year, returns true if it is. Has 2-digit year check
* Fix 2-digit years. Works for any century.
* Assumes that if 2-digit is more than 30 years in future, then previous century.
* @todo This needs to be disabled when dates prior to 100AD are required in ISO format
$yr = (integer) date("Y");
$century = (integer) ($yr / 100);
// if 2-digit year is less than 30 years in future, set it to this century
// otherwise if more than 30 years in future, then we set 2-digit year to the prev century.
if (($y + $c1) < $yr+ 30) $y = $y + $c1;
*Returns an array with date info.
function getDate($d= false,$fast= false)
if ($d === false) return $this->getdate();
if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer
* generate $YRS table for _adodb_getdate()
function _date_gentable($out=true)
for ($i=1970; $i >= 1600; $i-=10) {
$s = adodb_gmmktime(0,0,0,1,1,$i);
for ($i=1970; $i > 1500; $i--) {
adodb_date_test_date($i,1,1);
* Low-level function that returns the getdate() array. We have a special
* $fast flag, which if set to true, will return fewer array values,
* and is much faster as it does not calculate dow, etc.
* @param int Date to be converted in Unix epochs
* @param boolean Return short format array ( less weekday and month )
* @param boolean Ignore timezone
function _getDate($origd= false,$fast= false,$is_gmt= false)
$d = $origd - ($is_gmt ? 0 : adodb_get_gmt_diff(false,false,false));
if ($d < - 12219321600) $d -= 86400* 10; // if 15 Oct 1582 or earlier, gregorian correction
$_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31);
$_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31);
$d366 = $_day_power * 366;
$d365 = $_day_power * 365;
if (empty($YRS)) $YRS = array(
if ($is_gmt) $origd = $d;
// The valid range of a 32bit signed timestamp is typically from
// Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT
foreach($YRS as $year => $secs) {
if (!isset ($a)) $a = $lastyear;
if ($leaf = _adodb_is_leap_year($a)) $d += $d366;
$secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd;
$mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;
for ($a = 13 ; -- $a > 0;) {
$d += $mtab[$a] * $_day_power;
$day = $ndays + ceil(($d+ 1) / ($_day_power));
$d += ($ndays - $day+ 1)* $_day_power;
$hour = floor($d/ $_hour_power);
for ($a = 1970 ;; $a++ ) {
if ($leaf = _adodb_is_leap_year($a)) $d -= $d366;
$mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;
for ($a = 1 ; $a <= 12; $a++ ) {
$d -= $mtab[$a] * $_day_power;
$day = ceil(($d+ 1) / $_day_power);
$d = $d - ($day- 1) * $_day_power;
$hour = floor($d / $_hour_power);
$d -= $hour * $_hour_power;
$min = floor($d/ $_min_power);
$secs = $d - $min * $_min_power;
'yday' => floor($secsInYear/ $_day_power),
$dow = adodb_dow($year,$month,$day);
'yday' => floor($secsInYear/ $_day_power),
'weekday' => gmdate('l',$_day_power* (3+ $dow)),
* Accepts unix timestamp and iso date format
* ISO format date is converted into unix timestamp before calling date()
* @param string Format of date output
* @param int/string Date to be converted
* @param boolean Ignore timezone
* @return string In the format specified by $fmt
function date2($fmt, $d= false, $is_gmt= false)
"|^([0-9]{3,4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
($d), $rr)) return $this->date($fmt,false,$is_gmt);
if ($rr[1] <= 100 && $rr[2]<= 1) return adodb_date($fmt,false,$is_gmt);
if (!isset ($rr[5])) $d = adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
else $d = @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
return $this->date($fmt,$d,$is_gmt);
* Return formatted date based on timestamp $d
* @param string Format of date output
* @param int Date to be converted
* @param boolean Ignore timezone
* @return string In the format specified by $fmt
function date($fmt,$d= false,$is_gmt= false)
if ($d === false) return ($is_gmt)? @gmdate($fmt): @date($fmt);
if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer
return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d);
$arr = $this->_getdate($d,true,$is_gmt);
// if (!isset($daylight)) $daylight = function_exists('adodb_daylight_sv');
// if ($daylight) adodb_daylight_sv($arr, $is_gmt);
at this point, we have the following integer vars to manipulate:
$year, $month, $day, $hour, $min, $secs
for ($i= 0; $i < $max; $i++ ) {
case 'T': $dates .= date('T');break;
case 'L': $dates .= $arr['leap'] ? '1' : '0'; break;
case 'r': // Thu, 21 Dec 2000 16:01:07 +0200
// 4.3.11 uses '04 Jun 2004'
// 4.3.8 uses ' 4 Jun 2004'
$dates .= gmdate('D',$_day_power* (3+ $this->dow($year,$month,$day))). ', '
. ($day< 10? '0'. $day: $day) . ' '. date('M',mktime(0,0,0,$month,2,1971)). ' '. $year. ' ';
if ($hour < 10) $dates .= '0'. $hour; else $dates .= $hour;
if ($min < 10) $dates .= ':0'. $min; else $dates .= ':'. $min;
if ($secs < 10) $dates .= ':0'. $secs; else $dates .= ':'. $secs;
$gmt = adodb_get_gmt_diff();
$dates .= sprintf(' %s%04d',($gmt< 0)? '+': '-',abs($gmt)/ 36); break;
case 'Y': $dates .= $year; break;
case 'm': if ($month< 10) $dates .= '0'. $month; else $dates .= $month; break;
case 'Q': $dates .= ($month+ 3)>> 2; break;
case 'n': $dates .= $month; break;
case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break;
case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break;
case 't': $dates .= $arr['ndays']; break;
case 'z': $dates .= $arr['yday']; break;
case 'w': $dates .= adodb_dow($year,$month,$day); break;
case 'l': $dates .= gmdate('l',$_day_power* (3+ adodb_dow($year,$month,$day))); break;
case 'D': $dates .= gmdate('D',$_day_power* (3+ adodb_dow($year,$month,$day))); break;
case 'j': $dates .= $day; break;
case 'd': if ($day< 10) $dates .= '0'. $day; else $dates .= $day; break;
if ($d10 == 1) $dates .= 'st';
else if ($d10 == 2 && $day != 12) $dates .= 'nd';
else if ($d10 == 3) $dates .= 'rd';
$dates .= ($is_gmt) ? 0 : - adodb_get_gmt_diff(); break;
$gmt = ($is_gmt) ? 0 : adodb_get_gmt_diff();
$dates .= sprintf('%s%04d',($gmt< 0)? '+': '-',abs($gmt)/ 36); break;
if ($hour < 10) $dates .= '0'. $hour;
if ($hour > 12) $hh = $hour - 12;
if ($hour == 0) $hh = '12';
if ($hh < 10) $dates .= '0'. $hh;
if ($hour > 12) $hh = $hour - 12;
if ($hour == 0) $hh = '12';
case 'i': if ($min < 10) $dates .= '0'. $min; else $dates .= $min; break;
case 'U': $dates .= $d; break;
case 's': if ($secs < 10) $dates .= '0'. $secs; else $dates .= $secs; break;
// Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM
if ($hour>= 12) $dates .= 'pm';
if ($hour>= 12) $dates .= 'PM';
$dates .= $fmt[$i]; break;
if ($i < $max) $dates .= $fmt[$i];
* Returns a timestamp given a GMT/UTC time.
* @param int $is_dst is not implemented and is ignored
function gmmktime($hr,$min,$sec,$mon= false,$day= false,$year= false,$is_dst= false)
return $this->mktime($hr,$min,$sec,$mon,$day,$year,$is_dst,true);
* Return a timestamp given a local time. Originally by jackbbs.
* Not a very fast algorithm - O(n) operation. Could be optimized to O(1).
* @param int $is_dst is not implemented and is ignored
function mktime($hr,$min,$sec,$mon= false,$day= false,$year= false,$is_dst= false,$is_gmt= false)
/* Need to check if this can be deleted
// for windows, we don't check 1970 because with timezone differences,
// 1 Jan 1970 could generate negative timestamp, which is illegal
if (1971 < $year && $year < 2038
|| !defined('ADODB_NO_NEGATIVE_TS') && (1901 < $year && $year < 2038)
@gmmktime($hr,$min,$sec,$mon,$day,$year):
@mktime($hr,$min,$sec,$mon,$day,$year);
# disabled because some people place large values in $sec.
# however we need it for $mon because we use an array...
$_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31);
$_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31);
for ($a = 1970 ; $a <= $year; $a++ ) {
$leaf = _adodb_is_leap_year($a);
$loop_table = $_month_table_leaf;
$loop_table = $_month_table_normal;
$_total_date += $_add_date;
$_total_date += $loop_table[$b];
$ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different;
for ($a = 1969 ; $a >= $year; $a-- ) {
$leaf = _adodb_is_leap_year($a);
$loop_table = $_month_table_leaf;
$loop_table = $_month_table_normal;
if ($a > $year) { $_total_date += $_add_date;
for($b= 12;$b> $mon;$b-- ) {
$_total_date += $loop_table[$b];
$_total_date += $loop_table[$mon] - $day;
$_day_time = $hr * $_hour_power + $min * $_min_power + $sec;
$_day_time = $_day_power - $_day_time;
$ret = - ( $_total_date * $_day_power + $_day_time - $gmt_different);
if ($ret < - 12220185600) $ret += 10* 86400; // if earlier than 5 Oct 1582 - gregorian correction
else if ($ret < - 12219321600) $ret = - 12219321600; // if in limbo, reset to 15 Oct 1582.
//print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret;
// hack - convert to adodb_date
function strftime($fmt, $ls= false,$is_gmt= false)
global $ADODB_DATE_LOCALE;
if ((abs($ls) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
if (!defined('ADODB_NO_NEGATIVE_TS') || $ls >= 0) // if windows, must be +ve integer
if (empty($ADODB_DATE_LOCALE)) {
$hasAM = strrpos($tstr,'M') !== false;
$ADODB_DATE_LOCALE = array();
$ADODB_DATE_LOCALE[] = strncmp($tstr,'30',2) == 0 ? 'd'. $sep. 'm'. $sep. 'y' : 'm'. $sep. 'd'. $sep. 'y';
$ADODB_DATE_LOCALE[] = ($hasAM) ? 'h:i:s a' : 'H:i:s';
for ($i= 0,$max = strlen($fmt); $i < $max; $i++ ) {
/* ignore format modifiers */
case 'a': $fmtdate .= 'D'; break;
case 'A': $fmtdate .= 'l'; break;
case 'b': $fmtdate .= 'M'; break;
case 'B': $fmtdate .= 'F'; break;
case 'c': $fmtdate .= $ADODB_DATE_LOCALE[0]. $ADODB_DATE_LOCALE[1]; break;
case 'C': $fmtdate .= '\C?'; break; // century
case 'd': $fmtdate .= 'd'; break;
case 'D': $fmtdate .= 'm/d/y'; break;
case 'e': $fmtdate .= 'j'; break;
case 'g': $fmtdate .= '\g?'; break; //?
case 'G': $fmtdate .= '\G?'; break; //?
case 'H': $fmtdate .= 'H'; break;
case 'I': $fmtdate .= 'h'; break;
case 'j': $fmtdate .= '?z'; $parsej = true; break; // wrong as j=1-based, z=0-basd
case 'm': $fmtdate .= 'm'; break;
case 'M': $fmtdate .= 'i'; break;
case 'n': $fmtdate .= "\n"; break;
case 'p': $fmtdate .= 'a'; break;
case 'r': $fmtdate .= 'h:i:s a'; break;
case 'R': $fmtdate .= 'H:i:s'; break;
case 'S': $fmtdate .= 's'; break;
case 't': $fmtdate .= "\t"; break;
case 'T': $fmtdate .= 'H:i:s'; break;
case 'u': $fmtdate .= '?u'; $parseu = true; break; // wrong strftime=1-based, date=0-basde
case 'U': $fmtdate .= '?U'; $parseU = true; break;// wrong strftime=1-based, date=0-based
case 'x': $fmtdate .= $ADODB_DATE_LOCALE[0]; break;
case 'X': $fmtdate .= $ADODB_DATE_LOCALE[1]; break;
case 'w': $fmtdate .= '?w'; $parseu = true; break; // wrong strftime=1-based, date=0-basde
case 'W': $fmtdate .= '?W'; $parseU = true; break;// wrong strftime=1-based, date=0-based
case 'y': $fmtdate .= 'y'; break;
case 'Y': $fmtdate .= 'Y'; break;
case 'Z': $fmtdate .= 'T'; break;
} else if (('A' <= ($ch) && ($ch) <= 'Z' ) || ('a' <= ($ch) && ($ch) <= 'z' ))
//echo "fmt=",$fmtdate,"<br>";
if ($ls === false) $ls = time();
$ret = $this->date($fmtdate, $ls, $is_gmt);
* Converts from Gregorian Year-Month-Day to ISO YearNumber-WeekNumber-WeekDay
* Uses ISO 8601 definitions.
* Algorithm from Rick McCarty, 1999 at http://personal.ecu.edu/mccartyr/ISOwdALG.txt
// Transcribed to PHP by Jesus M. Castagnetto (blame him if it is fubared ;-)
$mnth = array (0,31,59,90,120,151,181,212,243,273,304,334);
$day_of_year_number = $day + $mnth[$month - 1];
if ($y_isleap && $month > 2) {
// find Jan 1 weekday (monday = 1, sunday = 7)
$jan1_weekday = 1 + intval((((($c / 100) % 4) * 5) + $g) % 7);
// weekday for year-month-day
$h = $day_of_year_number + ($jan1_weekday - 1) - 1;
$weekday = 1 + intval(($h - 1) % 7);
// find if Y M D falls in YearNumber Y-1, WeekNumber 52 or
if ($day_of_year_number <= (8 - $jan1_weekday) && $jan1_weekday > 4){
if ($jan1_weekday == 5 || ($jan1_weekday == 6 && $y_1_isleap)) {
// find if Y M D falls in YearNumber Y+1, WeekNumber 1
if ($yearnumber == $year) {
if (($i - $day_of_year_number) < (4 - $weekday)) {
// find if Y M D falls in YearNumber Y, WeekNumber 1 through 53
if ($yearnumber == $year) {
$j = $day_of_year_number + (7 - $weekday) + ($jan1_weekday - 1);
//$weeknumber = intval($j / 7) + 1; // kludge!!! - JMC
$weeknumber = intval($j / 7); // kludge!!! - JMC
$weeknumber = '0'. $weeknumber;
return "{ $yearnumber}-{$weeknumber}-{$weekday}";
* Get a list of timezones to be worked with
static $timezone_options;
if (!$timezone_options) {
$timezone_options = array();
$timezone_options['default'] = '-- Use Default Time Zone --';
foreach ($GLOBALS['_DATE_TIMEZONE_DATA'] as $tz_key => $tz) {
$absoffset = abs($offset /= 60000);
$plusminus = $offset < 0 ? '-' : '+';
$gmtoff = sprintf("GMT%1s%02d:%02d", $plusminus, $absoffset / 60, $absoffset - (intval($absoffset / 60) * 60));
$tzlongshort = $tz['longname'] . ' (' . $tz['shortname'] . ')';
$timezone_options[$tz_key] = sprintf('%-28.28s: %-36.36s %s', $tz_key, $tzlongshort, $gmtoff);
return $timezone_options;
* Per http://www.w3.org/TR/NOTE-datetime
return $this->strftime('%Y-%m-%dT%H:%M:%S%O', $timestamp, $user);
# rfc2822 requires dates to be en formatted
#was return date('D, j M Y H:i:s ', $time) . $this->timezone_offset($time, 'no colon');
# switch back to the 'saved' locale
$secs = $this->strftime('%Z', $time, $user);
$colon = $no_colon ? '' : ':';
$mins = intval(($secs + 30) / 60);
return sprintf("%s%02d%s%02d", $sign, $mins / 60, $colon, $mins % 60);
# breaks the RFC 2822 code
$locale = @setlocale(LC_TIME, $this->get_locale($user));
#print "<pre>set_locale(): locale=$locale\n</pre>";
* Get a hash of holidays for a given year
* @param $pYear the year in question
* @param $pCountryCode -- the country in question - only US is supported currently
* @return an associative array containing the holidays occuring in the given year they key is a date stamp of the form Y-m-d, the value is the name of the corresponding holiday
static function getHolidays( $pYear= NULL, $pCountryCode= 'US' ) {
switch( $pCountryCode ) {
$return[date( 'Y-m-d', strtotime("-1 week monday", strtotime("1 september $pYear")) + 43200)] = 'Bank Holiday';
// First off, the simple ones
$return[$pYear . '-01-01'] = 'New Year`s Day';
//$return[$pYear . '-02-14'] = 'Valentine`s Day';
$return[$pYear . '-06-14'] = 'Flag Day';
$return[$pYear . '-07-04'] = 'Independence Day';
$return[$pYear . '-11-11'] = 'Veteran`s Day';
$return[$pYear . '-12-25'] = 'Christmas';
// Martin Luther King, Jr. Day - third Monday in January
$return[date( 'Y-m-d', strtotime( '2 weeks monday', strtotime( "January 1, $pYear" ) ) + 43200 )] = 'Martin Luther King, Jr. Day';
// Presidents` Day - third Monday in February
$return[date( 'Y-m-d', strtotime( '2 weeks monday', strtotime( "February 1, $pYear" ) ) + 43200 )] = 'Presidents` Day';
// Mardi Gras - Tuesday ~47 days before Easter
//$return[date( 'Y-m-d', strtotime( 'last tuesday 46 days ago', easter_date( $pYear ) ) + 43200 )] = 'Mardi Gras';
// Memorial Day - last Monday in May
$return[date( 'Y-m-d', strtotime( '-1 week monday', strtotime( "June 1, $pYear" ) ) + 43200 )] = 'Memorial Day';
// Labor Day - first Monday in September
// Columbus Day - second Monday in October
$return[date( 'Y-m-d', strtotime( '1 week monday', strtotime( "October 1, $pYear" ) ) + 43200 )] = 'Columbus Day';
// Thanksgiving - fourth Thursday in November
$return[date( 'Y-m-d', strtotime( '3 weeks thursday', strtotime( "November 1, $pYear" ) ) + 43200 )] = 'Thanksgiving';
// Easter - the Sunday after the first full moon which falls on or after the Spring Equinox
// thank god PHP has a function for that...
$return[date( 'Y-m-d', easter_date( $pYear ) + 43200 )] = 'Easter';
function calculateTimeZoneDate( $dateTime, $fromTZ, $toTZ, $fromLocation = 'North America', $toLocation = 'GMT' ) {
$timeZonesArray = array( 'GMT' => array( 'GMT' => + 0 // GMT
'North America' => array( 'NST' => - 3.5, // Newfoundland Standard Time
'NDT' => - 2.5, // Newfoundland Daylight Time
'AST' => - 4, // Atlantic Standard Time
'ADT' => - 3, // Atlantic Daylight Time
'EST' => - 5, // Eastern Standard Time
'EDT' => - 4, // Eastern Daylight Time
'CST' => - 6, // Central Standard Time
'CDT' => - 5, // Central Daylight Time
'MST' => - 7, // Central Daylight Time
'MDT' => - 6, // Mountain Daylight Time
'PST' => - 8, // Pacific Standard Time
'PDT' => - 7, // Pacific Daylight Time
'AKST' => - 9, // Alaska Standard Time
'AKDT' => - 8, // Alaska Daylight Time
'HAST' => - 10, // Hawaii-Aleutian Standard Time
'HADT' => - 9 // Hawaii-Aleutian Daylight Time
'Australia' => array( 'NFT' => + 11.5, // Norfolk (Island) Time
'EST' => + 10, // Eastern Standard Time
'EDT' => + 11, // Eastern Daylight Time
'CST' => + 9.5, // Central Standard Time
'CDT' => + 10.5, // Central Daylight Time
'WST' => + 8, // Western Standard Time
'CXT' => + 7, // Christmas Island Time
'Europe' => array( 'GMT' => + 0, // Greenwich Mean Time
'BST' => + 1, // British Summer Time
'IST' => + 1, // Irish Summer Time
'WET' => + 0, // Western European Time
'WEST' => + 1, // Western European Summer Time
'CET' => + 1, // Central European Time
'CEST' => + 2, // Central European Summer Time
'EET' => + 2, // Eastern European Time
'EEST' => + 3 // Eastern European Summer Time
'Military' => array( 'Z' => + 0, // Zulu Time Zone
'Y' => - 12, // Yankee Time Zone
'X' => - 11, // X-ray Time Zone
'W' => - 10, // Whiskey Time Zone
'V' => - 9, // Victor Time Zone
'U' => - 8, // Uniform Time Zone
'T' => - 7, // Tango Time Zone
'S' => - 6, // Sierra Time Zone
'R' => - 5, // Romeo Time Zone
'Q' => - 4, // Quebec Time Zone
'P' => - 3, // Papa Time Zone
'O' => - 2, // Oscar Time Zone
'N' => - 1, // November Time Zone
'A' => + 1, // Alpha Time Zone
'B' => + 2, // Bravo Time Zone
'C' => + 3, // Charlie Time Zone
'D' => + 4, // Delta Time Zone
'E' => + 5, // Echo Time Zone
'F' => + 6, // Foxtrot Time Zone
'G' => + 7, // Golf Time Zone
'H' => + 8, // Hotel Time Zone
'I' => + 9, // India Time Zone
'K' => + 10, // Kilo Time Zone
'L' => + 11, // Lima Time Zone
'M' => + 12 // Mike Time Zone
$fromGMTDiff = $timeZonesArray[$fromLocation][$fromTZ];
$toGMTDiff = $timeZonesArray[$toLocation][$toTZ];
if(( '' != trim( $fromGMTDiff )) && ( '' != trim( $toGMTDiff ))) {
if( $fromGMTDiff > $toGMTDiff ) {
$netDiff = $fromGMTDiff - $toGMTDiff;
$netDiff = $toGMTDiff - $fromGMTDiff;
$retval = date( 'Y-m-d h:i:sa', ( $timeStamp + ( 3600 * $netDiff )));
} // end function calculateTimeZoneDate()
|