442 lines
No EOL
13 KiB
PHP
442 lines
No EOL
13 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Date Library, extended date functions.
|
|
* File is included only when needed.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup adodb
|
|
* @{
|
|
*/
|
|
/**
|
|
* The following functions are low level functions that implements pre 1970
|
|
* to post 2038 versions of native php date functions. Will handle dates from
|
|
* the year 100 to the year 3000. Uses native php date functions when possible,
|
|
* alterate methods when native functions won't work.
|
|
*
|
|
* Altered the original ADODB code to split it between the high level
|
|
* functions which are used when pre-1970 and post-2038 dates are not needed
|
|
* and this large file which is only parsed for dates that are out of range
|
|
* for native php date handling.
|
|
*
|
|
* Replace native php functions:
|
|
* getdate() with date_getdate()
|
|
* date() with date_date()
|
|
* gmdate() with date_gmdate()
|
|
* mktime() with date_mktime()
|
|
* gmmktime() with gmdate_mktime()
|
|
*
|
|
* The following functions were derived from code obtained from
|
|
* http://phplens.com/phpeverywhere/adodb_date_library, licensed as follows:
|
|
*
|
|
* COPYRIGHT(c) 2003-2005 John Lim
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted under the terms of the BSD License.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* Low-level function that returns the getdate() array for pre-1970
|
|
* and post-2038 dates.
|
|
*
|
|
* 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 $timestamp a unix timestamp
|
|
* @param $timezone name
|
|
* Use 'UTC' to avoid timezone conversion.
|
|
*/
|
|
function _date_getdate($timestamp = FALSE, $timezone = FALSE) {
|
|
static $YRS;
|
|
if ($timezone === FALSE) {
|
|
$timezone = date_default_timezone_name();
|
|
}
|
|
|
|
$timestamp_in = $timestamp;
|
|
|
|
$_day_power = 86400;
|
|
$_hour_power = 3600;
|
|
$_min_power = 60;
|
|
if ($timestamp < -12219321600) $timestamp -= 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_leap = array("", 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
|
|
$d366 = $_day_power * 366;
|
|
$d365 = $_day_power * 365;
|
|
if ($timestamp < 0) {
|
|
if (empty($YRS)) $YRS = array(
|
|
1970 => 0,
|
|
1960 => -315619200,
|
|
1950 => -631152000,
|
|
1940 => -946771200,
|
|
1930 => -1262304000,
|
|
1920 => -1577923200,
|
|
1910 => -1893456000,
|
|
1900 => -2208988800,
|
|
1890 => -2524521600,
|
|
1880 => -2840140800,
|
|
1870 => -3155673600,
|
|
1860 => -3471292800,
|
|
1850 => -3786825600,
|
|
1840 => -4102444800,
|
|
1830 => -4417977600,
|
|
1820 => -4733596800,
|
|
1810 => -5049129600,
|
|
1800 => -5364662400,
|
|
1790 => -5680195200,
|
|
1780 => -5995814400,
|
|
1770 => -6311347200,
|
|
1760 => -6626966400,
|
|
1750 => -6942499200,
|
|
1740 => -7258118400,
|
|
1730 => -7573651200,
|
|
1720 => -7889270400,
|
|
1710 => -8204803200,
|
|
1700 => -8520336000,
|
|
1690 => -8835868800,
|
|
1680 => -9151488000,
|
|
1670 => -9467020800,
|
|
1660 => -9782640000,
|
|
1650 => -10098172800,
|
|
1640 => -10413792000,
|
|
1630 => -10729324800,
|
|
1620 => -11044944000,
|
|
1610 => -11360476800,
|
|
1600 => -11676096000);
|
|
|
|
// 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
|
|
//
|
|
$lastsecs = 0;
|
|
$lastyear = 1970;
|
|
foreach ($YRS as $year => $secs) {
|
|
if ($timestamp >= $secs) {
|
|
$a = $lastyear;
|
|
break;
|
|
}
|
|
$lastsecs = $secs;
|
|
$lastyear = $year;
|
|
}
|
|
$timestamp -= $lastsecs;
|
|
if (!isset($a)) $a = $lastyear;
|
|
for (; --$a >= 0;) {
|
|
$lastd = $timestamp;
|
|
if ($leap = date_is_leap_year($a)) $timestamp += $d366;
|
|
else $timestamp += $d365;
|
|
|
|
if ($timestamp >= 0) {
|
|
$year = $a;
|
|
break;
|
|
}
|
|
}
|
|
$secs_in_year = 86400 * ($leap ? 366 : 365) + $lastd;
|
|
$timestamp = $lastd;
|
|
$mtab = ($leap) ? $_month_table_leap : $_month_table_normal;
|
|
for ($a = 13 ; --$a > 0;) {
|
|
$lastd = $timestamp;
|
|
$timestamp += $mtab[$a] * $_day_power;
|
|
if ($timestamp >= 0) {
|
|
$month = $a;
|
|
$ndays = $mtab[$a];
|
|
break;
|
|
}
|
|
}
|
|
$timestamp = $lastd;
|
|
$day = $ndays + ceil(($timestamp+1) / ($_day_power));
|
|
$timestamp += ($ndays - $day+1)* $_day_power;
|
|
$hour = floor($timestamp/$_hour_power);
|
|
}
|
|
else {
|
|
|
|
if ($timezone != 'UTC') {
|
|
$timestamp += date_get_gmt_diff_ts($timestamp, $timezone);
|
|
}
|
|
|
|
for ($a = 1970 ;; $a++) {
|
|
$lastd = $timestamp;
|
|
if ($leap = date_is_leap_year($a)) $timestamp -= $d366;
|
|
else $timestamp -= $d365;
|
|
if ($timestamp < 0) {
|
|
$year = $a;
|
|
break;
|
|
}
|
|
}
|
|
$secs_in_year = $lastd;
|
|
$timestamp = $lastd;
|
|
$mtab = ($leap) ? $_month_table_leap : $_month_table_normal;
|
|
for ($a = 1 ; $a <= 12; $a++) {
|
|
$lastd = $timestamp;
|
|
$timestamp -= $mtab[$a] * $_day_power;
|
|
if ($timestamp < 0) {
|
|
$month = $a;
|
|
$ndays = $mtab[$a];
|
|
break;
|
|
}
|
|
}
|
|
$timestamp = $lastd;
|
|
$day = ceil(($timestamp + 1) / $_day_power);
|
|
$timestamp = $timestamp - ($day - 1) * $_day_power;
|
|
$hour = floor($timestamp / $_hour_power);
|
|
}
|
|
$timestamp -= $hour * $_hour_power;
|
|
$min = floor($timestamp / $_min_power);
|
|
$secs = $timestamp - $min * $_min_power;
|
|
$dow = date_dow($day, $month, $year);
|
|
return array(
|
|
'second' => $secs,
|
|
'minute' => $min,
|
|
'hour' => $hour,
|
|
'day' => $day,
|
|
'wday' => $dow,
|
|
'month' => $month,
|
|
'year' => $year,
|
|
'yday' => floor($secs_in_year / $_day_power),
|
|
'weekday' => gmdate('l', ($_day_power * (3 + $dow))),
|
|
'leap' => $leap,
|
|
'ndays' => $ndays,
|
|
'month_name' => gmdate('F', mktime(0, 0, 0, $month, 2, 1971)),
|
|
0 => $timestamp_in
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Low level function to create date() for pre-1970 and post-2038 dates.
|
|
*
|
|
* @param $format a format string for the result
|
|
* @param $timestamp a unix timestamp
|
|
* @param $timezone name
|
|
* Use 'UTC' to avoid timezone conversion.
|
|
*/
|
|
function _date_date($format, $timestamp = FALSE, $timezone = FALSE) {
|
|
if ($timezone === FALSE) {
|
|
$timezone = date_default_timezone_name();
|
|
}
|
|
|
|
$_day_power = 86400;
|
|
$arr = _date_getdate($timestamp, $timezone);
|
|
$year = $arr['year'];
|
|
$month = $arr['month'];
|
|
$day = $arr['day'];
|
|
$hour = $arr['hour'];
|
|
$min = $arr['minute'];
|
|
$secs = $arr['second'];
|
|
$max = strlen($format);
|
|
$dates = '';
|
|
|
|
/*
|
|
at this point, we have the following integer vars to manipulate:
|
|
$year, $month, $day, $hour, $min, $secs
|
|
*/
|
|
for ($i = 0; $i < $max; $i++) {
|
|
switch ($format[$i]) {
|
|
case 'T': $dates .= date('T');break;
|
|
// YEAR
|
|
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 + date_dow($day, $month, $year))) .', '.
|
|
($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 = date_get_gmt_diff_ts($timestamp, $timezone);
|
|
$dates .= sprintf(' %s%04d', ($gmt >= 0) ? '+' : '-', abs($gmt) / 36); break;
|
|
case 'Y': $dates .= date_pad($year, 4); break;
|
|
case 'y': $dates .= drupal_substr($year, strlen($year)-2, 2); break;
|
|
// MONTH
|
|
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;
|
|
// DAY
|
|
case 't': $dates .= $arr['ndays']; break;
|
|
case 'z': $dates .= $arr['yday']; break;
|
|
case 'w': $dates .= date_dow($day, $month, $year); break;
|
|
case 'l': $dates .= gmdate('l', $_day_power*(3 + date_dow($day, $month, $year))); break;
|
|
case 'D': $dates .= gmdate('D', $_day_power*(3 + date_dow($day, $month, $year))); break;
|
|
case 'j': $dates .= $day; break;
|
|
case 'd': if ($day<10) $dates .= '0'. $day; else $dates .= $day; break;
|
|
case 'S':
|
|
$d10 = $day % 10;
|
|
if ($d10 == 1) $dates .= 'st';
|
|
elseif ($d10 == 2 && $day != 12) $dates .= 'nd';
|
|
elseif ($d10 == 3) $dates .= 'rd';
|
|
else $dates .= 'th';
|
|
break;
|
|
// HOUR
|
|
case 'Z':
|
|
$dates .= -date_get_gmt_diff_ts($timestamp, $timezone);
|
|
break;
|
|
case 'O':
|
|
$gmt = date_get_gmt_diff_ts($timestamp, $timezone);
|
|
$dates .= sprintf('%s%04d', ($gmt<0)?'+':'-', abs($gmt)/36);
|
|
break;
|
|
case 'H':
|
|
if ($hour < 10) $dates .= '0'. $hour;
|
|
else $dates .= $hour;
|
|
break;
|
|
case 'h':
|
|
if ($hour > 12) $hh = $hour - 12;
|
|
else {
|
|
if ($hour == 0) $hh = '12';
|
|
else $hh = $hour;
|
|
}
|
|
if ($hh < 10) $dates .= '0'. $hh;
|
|
else $dates .= $hh;
|
|
break;
|
|
case 'G':
|
|
$dates .= $hour;
|
|
break;
|
|
case 'g':
|
|
if ($hour > 12) $hh = $hour - 12;
|
|
else {
|
|
if ($hour == 0) $hh = '12';
|
|
else $hh = $hour;
|
|
}
|
|
$dates .= $hh;
|
|
break;
|
|
// MINUTES
|
|
case 'i': if ($min < 10) $dates .= '0'. $min; else $dates .= $min; break;
|
|
// SECONDS
|
|
case 'U': $dates .= $timestamp; break;
|
|
case 's': if ($secs < 10) $dates .= '0'. $secs; else $dates .= $secs; break;
|
|
// AM/PM
|
|
// Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM
|
|
case 'a':
|
|
if ($hour>=12) $dates .= 'pm';
|
|
else $dates .= 'am';
|
|
break;
|
|
case 'A':
|
|
if ($hour>=12) $dates .= 'PM';
|
|
else $dates .= 'AM';
|
|
break;
|
|
default:
|
|
$dates .= $format[$i]; break;
|
|
// ESCAPE
|
|
case "\\":
|
|
$i++;
|
|
if ($i < $max) $dates .= $format[$i];
|
|
break;
|
|
}
|
|
}
|
|
return $dates;
|
|
}
|
|
|
|
/**
|
|
* Low level function to create mktime() for pre-1970 and post 2038 dates.
|
|
*
|
|
* @param $hr the hour
|
|
* @param $min the minute
|
|
* @param $sec the second
|
|
* @param $mon the month
|
|
* @param $day the day
|
|
* @param $year the year
|
|
* @param $timezone name
|
|
* Use 'UTC' to avoid timezone conversion.
|
|
*/
|
|
function _date_mktime($hr, $min, $sec, $mon = FALSE, $day = FALSE, $year = FALSE, $timezone = FALSE) {
|
|
if ($timezone === FALSE) {
|
|
$timezone = date_default_timezone_name();
|
|
}
|
|
|
|
/*
|
|
# disabled because some people place large values in $sec.
|
|
# however we need it for $mon because we use an array...
|
|
$hr = intval($hr);
|
|
$min = intval($min);
|
|
$sec = intval($sec);
|
|
*/
|
|
$mon = intval($mon);
|
|
$day = intval($day);
|
|
$year = intval($year);
|
|
$year = date_year_digit_check($year);
|
|
if ($mon > 12) {
|
|
$y = floor(($mon-1) / 12);
|
|
$year += $y;
|
|
$mon -= $y * 12;
|
|
}
|
|
elseif ($mon < 1) {
|
|
$y = ceil((1-$mon) / 12);
|
|
$year -= $y;
|
|
$mon += $y * 12;
|
|
}
|
|
$_day_power = 86400;
|
|
$_hour_power = 3600;
|
|
$_min_power = 60;
|
|
$_month_table_normal = array("", 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
|
|
$_month_table_leap = array("", 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
|
|
$_total_date = 0;
|
|
if ($year >= 1970) {
|
|
$year_in = $year;
|
|
for ($a = 1970 ; $a <= $year; $a++) {
|
|
$leap = date_is_leap_year($a);
|
|
if ($leap == true) {
|
|
$loop_table = $_month_table_leap;
|
|
$_add_date = 366;
|
|
}
|
|
else {
|
|
$loop_table = $_month_table_normal;
|
|
$_add_date = 365;
|
|
}
|
|
if ($a < $year) {
|
|
$_total_date += $_add_date;
|
|
}
|
|
else {
|
|
for ($b=1; $b<$mon; $b++) {
|
|
$_total_date += $loop_table[$b];
|
|
}
|
|
}
|
|
}
|
|
$_total_date +=$day-1;
|
|
$ret = ($_total_date * $_day_power) + ($hr * $_hour_power) + ($min * $_min_power) + $sec;
|
|
$ret -= date_get_gmt_diff_ts($ret, $timezone);
|
|
|
|
}
|
|
else {
|
|
for ($a = 1969 ; $a >= $year; $a--) {
|
|
$leap = date_is_leap_year($a);
|
|
if ($leap == true) {
|
|
$loop_table = $_month_table_leap;
|
|
$_add_date = 366;
|
|
}
|
|
else {
|
|
$loop_table = $_month_table_normal;
|
|
$_add_date = 365;
|
|
}
|
|
if ($a > $year) { $_total_date += $_add_date;
|
|
}
|
|
else {
|
|
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);
|
|
if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction
|
|
elseif ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582.
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* @} End of ingroup "adodb".
|
|
*/ |