From 1f6d9caf8397416747aa2105b2aac352cdf92fe3 Mon Sep 17 00:00:00 2001 From: ctrlaltca <> Date: Sun, 6 Jan 2013 17:22:11 +0000 Subject: ported TDateTimeStamp to php's native DateTime class --- framework/Util/TDateTimeStamp.php | 631 ++++---------------------------------- 1 file changed, 54 insertions(+), 577 deletions(-) (limited to 'framework') diff --git a/framework/Util/TDateTimeStamp.php b/framework/Util/TDateTimeStamp.php index 5619f6cd..a7a43b3c 100644 --- a/framework/Util/TDateTimeStamp.php +++ b/framework/Util/TDateTimeStamp.php @@ -1,13 +1,8 @@ + + * @author Fabio Bas ctrlaltca[AT]gmail[DOT]com * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft * @license http://www.pradosoft.com/license/ @@ -15,50 +10,16 @@ * @package System.Util */ -/* - This code was originally for windows. But apparently this problem happens - also with Linux, RH 7.3 and later! - - glibc-2.2.5-34 and greater has been changed to return -1 for dates < - 1970. This used to work. The problem exists with RedHat 7.3 and 8.0 - echo (mktime(0, 0, 0, 1, 1, 1960)); // prints -1 - - References: - http://bugs.php.net/bug.php?id=20048&edit=2 - http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html -*/ - -if (!defined('ADODB_ALLOW_NEGATIVE_TS') - && !defined('ADODB_NO_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1); - /** * TDateTimeStamp Class * - * TDateTimeStamp Class is adpated from the ADOdb Date Library, - * part of the ADOdb abstraction library - * Download: http://phplens.com/phpeverywhere/ + * TDateTimeStamp Class is a wrapper to DateTime: http://php.net/manual/book.datetime.php + * Prado implemented this class before php started shipping the DateTime extension. + * This class is deprecated and left here only for compatibility for legacy code. + * Please note that this class doesn't support automatic conversion from gregorian to + * julian dates anymore. * - * http://phplens.com/phpeverywhere/adodb_date_library - * - * PHP native date functions use integer timestamps for computations. - * Because of this, dates are restricted to the years 1901-2038 on Unix - * and 1970-2038 on Windows due to integer overflow for dates beyond - * those years. This library overcomes these limitations by replacing the - * native function's signed integers (normally 32-bits) with PHP floating - * point numbers (normally 64-bits). - * - * Dates from 100 A.D. to 3000 A.D. and later have been tested. The minimum - * is 100 A.D. as <100 will invoke the 2 => 4 digit year conversion. - * The maximum is billions of years in the future, but this is a theoretical - * limit as the computation of that year would take too long with the - * current implementation of {@link getTimeStamp}. - * - * PERFORMANCE - * For high speed, this library uses the native date functions where - * possible, and only switches to PHP code when the dates fall outside - * the 32-bit signed integer range. - * - * @author Wei Zhuo + * @author Fabio Bas ctrlaltca[AT]gmail[DOT]com * @version $Id$ * @package System.Util * @since 3.0.4 @@ -69,50 +30,16 @@ class TDateTimeStamp protected static $_month_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); /** - * Gets day of week, 0 = Sunday,... 6=Saturday. - * Algorithm from PEAR::Date_Calc + * Returns the day of the week (0=Sunday, 1=Monday, .. 6=Saturday) + * @param int year + * @param int month + * @param int day */ public function getDayofWeek($year, $month, $day) { - /* - 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 - every 400 years. - - Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian). - */ - if ($year <= 1582) - { - if ($year < 1582 || - ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) - { - $greg_correction = 3; - } - else - { - $greg_correction = 0; - } - } - else - { - $greg_correction = 0; - } - - if($month > 2) - $month -= 2; - else - { - $month += 10; - $year--; - } - - $day = floor((13 * $month - 1) / 5) + - $day + ($year % 100) + - floor(($year % 100) / 4) + - floor(($year / 100) / 4) - 2 * - floor($year / 100) + 77 + $greg_correction; - - return $day - 7 * floor($day / 7); + $dt = new DateTime(); + $dt->setDate($year, $month, $day); + return (int) $dt->format('w'); } /** @@ -124,15 +51,9 @@ class TDateTimeStamp public function isLeapYear($year) { $year = $this->digitCheck($year); - if ($year % 4 != 0) - return false; - - if ($year % 400 == 0) - return true; - // if gregorian calendar (>1582), century not-divisible by 400 is not leap - else if ($year > 1582 && $year % 100 == 0 ) - return false; - return true; + $dt = new DateTime(); + $dt->setDate($year, 1, 1); + return (bool) $dt->format('L'); } /** @@ -170,13 +91,15 @@ class TDateTimeStamp /** * @return integer get local time zone offset from GMT */ - public function getGMTDiff() + public function getGMTDiff($ts=false) { - static $TZ; - if (isset($TZ)) return $TZ; + $dt = new DateTime(); + if($ts) + $dt->setTimeStamp($ts); + else + $dt->setDate(1970, 1, 2); - $TZ = mktime(0,0,0,1,2,1970) - gmmktime(0,0,0,1,2,1970); - return $TZ; + return (int) $dt->format('Z'); } /** @@ -185,236 +108,23 @@ class TDateTimeStamp function getDate($d=false,$fast=false) { if ($d === false) return getdate(); - // check if number in 32-bit signed range - if ((abs($d) <= 0x7FFFFFFF)) - { - if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer - return @getdate($d); - } - return $this->_getDateInternal($d); - } - - - /** - * 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 float original date - * @param boolean false to compute the day of the week, default is true - * @param boolean true to calculate the GMT dates - * @return array an array with date info. - */ - protected function _getDateInternal($origd=false,$fast=true,$is_gmt=false) - { - static $YRS; - - $d = $origd - ($is_gmt ? 0 : $this->getGMTDiff()); - - $_day_power = 86400; - $_hour_power = 3600; - $_min_power = 60; - - if ($d < -12219321600) - $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction - - $_month_table_normal =& self::$_month_normal; - $_month_table_leaf = & self::$_month_leaf; - - $d366 = $_day_power * 366; - $d365 = $_day_power * 365; - - if ($d < 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); - - 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 - // - - # old algorithm iterates through all years. new algorithm does it in - # 10 year blocks - - /* - # old algo - for ($a = 1970 ; --$a >= 0;) { - $lastd = $d; - - if ($leaf = _adodb_is_leap_year($a)) $d += $d366; - else $d += $d365; - - if ($d >= 0) { - $year = $a; - break; - } - } - */ - - $lastsecs = 0; - $lastyear = 1970; - foreach($YRS as $year => $secs) - { - if ($d >= $secs) - { - $a = $lastyear; - break; - } - $lastsecs = $secs; - $lastyear = $year; - } - - $d -= $lastsecs; - if (!isset($a)) $a = $lastyear; - - //echo ' yr=',$a,' ', $d,'.'; - - for (; --$a >= 0;) - { - $lastd = $d; - - if ($leaf = $this->isLeapYear($a)) - $d += $d366; - else - $d += $d365; - - if ($d >= 0) - { - $year = $a; - break; - } - } - /**/ - - $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd; - - $d = $lastd; - $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; - for ($a = 13 ; --$a > 0;) - { - $lastd = $d; - $d += $mtab[$a] * $_day_power; - if ($d >= 0) - { - $month = $a; - $ndays = $mtab[$a]; - break; - } - } - - $d = $lastd; - $day = $ndays + ceil(($d+1) / ($_day_power)); - - $d += ($ndays - $day+1)* $_day_power; - $hour = floor($d/$_hour_power); - - } else { - for ($a = 1970 ;; $a++) - { - $lastd = $d; - - if ($leaf = $this->isLeapYear($a)) $d -= $d366; - else $d -= $d365; - if ($d < 0) - { - $year = $a; - break; - } - } - $secsInYear = $lastd; - $d = $lastd; - $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; - for ($a = 1 ; $a <= 12; $a++) - { - $lastd = $d; - $d -= $mtab[$a] * $_day_power; - if ($d < 0) - { - $month = $a; - $ndays = $mtab[$a]; - break; - } - } - $d = $lastd; - $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; - if ($fast) - { - return array( - 'seconds' => $secs, - 'minutes' => $min, - 'hours' => $hour, - 'mday' => $day, - 'mon' => $month, - 'year' => $year, - 'yday' => floor($secsInYear/$_day_power), - 'leap' => $leaf, - 'ndays' => $ndays - ); - } - - - $dow = $this->getDayofWeek($year,$month,$day); + $dt = new DateTime(); + $dt->setTimestamp($d); return array( - 'seconds' => $secs, - 'minutes' => $min, - 'hours' => $hour, - 'mday' => $day, - 'wday' => $dow, - 'mon' => $month, - 'year' => $year, - 'yday' => floor($secsInYear/$_day_power), - 'weekday' => gmdate('l',$_day_power*(3+$dow)), - 'month' => gmdate('F',mktime(0,0,0,$month,2,1971)), - 0 => $origd - ); + 'seconds' => (int) $dt->format('s'), + 'minutes' => (int) $dt->format('i'), + 'hours' => (int) $dt->format('G'), + 'mday' => (int) $dt->format('j'), + 'wday' => (int) $dt->format('w'), + 'mon' => (int) $dt->format('n'), + 'year' => (int) $dt->format('Y'), + 'yday' => (int) $dt->format('z'), + 'weekday' => $dt->format('l'), + 'month' => $dt->format('F'), + 0 => (int) $d + ); } /** @@ -441,262 +151,29 @@ class TDateTimeStamp /** * @return string formatted date based on timestamp $d */ - function formatDate($fmt,$d=false,$is_gmt=false) + function formatDate($fmt,$ts=false,$is_gmt=false) { - if ($d === false) - return ($is_gmt)? @gmdate($fmt): @date($fmt); - - // check if number in 32-bit signed range - if ((abs($d) <= 0x7FFFFFFF)) - { - // if windows, must be +ve integer - if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) - return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d); - } - - $_day_power = 86400; - - $arr = $this->getDate($d,true,$is_gmt); - - $year = $arr['year']; - $month = $arr['mon']; - $day = $arr['mday']; - $hour = $arr['hours']; - $min = $arr['minutes']; - $secs = $arr['seconds']; - - $max = strlen($fmt); - $dates = ''; - - $isphp5 = PHP_VERSION >= 5; - - /* - at this point, we have the following integer vars to manipulate: - $year, $month, $day, $hour, $min, $secs - */ - for ($i=0; $i < $max; $i++) - { - switch($fmt[$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+$this->getDayOfWeek($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 = $this->getGMTDiff(); - if ($isphp5) - $dates .= sprintf(' %s%04d',($gmt<=0)?'+':'-',abs($gmt)/36); - else - $dates .= sprintf(' %s%04d',($gmt<0)?'+':'-',abs($gmt)/36); - break; + $dt = new DateTime(); + if($is_gmt) + $dt->setTimeZone(new DateTimeZone('UTC')); + $dt->setTimestamp($ts); - case 'Y': $dates .= $year; break; - case 'y': $dates .= 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 .= $this->getDayOfWeek($year,$month,$day); break; - case 'l': $dates .= gmdate('l',$_day_power*(3+$this->getDayOfWeek($year,$month,$day))); break; - case 'D': $dates .= gmdate('D',$_day_power*(3+$this->getDayOfWeek($year,$month,$day))); 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'; - else if ($d10 == 2 && $day != 12) $dates .= 'nd'; - else if ($d10 == 3) $dates .= 'rd'; - else $dates .= 'th'; - break; - - // HOUR - case 'Z': - $dates .= ($is_gmt) ? 0 : -$this->getGMTDiff(); break; - case 'O': - $gmt = ($is_gmt) ? 0 : $this->getGMTDiff(); - - if ($isphp5) - $dates .= sprintf('%s%04d',($gmt<=0)?'+':'-',abs($gmt)/36); - else - $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 .= $d; 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 .= $fmt[$i]; break; - // ESCAPE - case "\\": - $i++; - if ($i < $max) $dates .= $fmt[$i]; - break; - } - } - return $dates; + return $dt->format($fmt); } /** - * Not a very fast algorithm - O(n) operation. Could be optimized to O(1). - * @return integer|float a timestamp given a local time. Originally by jackbbs. + * @return integer|float a timestamp given a local time */ function getTimeStamp($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_gmt=false) { - - if ($mon === false) - return $is_gmt? @gmmktime($hr,$min,$sec): @mktime($hr,$min,$sec); - - // 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)) - { - return $is_gmt ? @gmmktime($hr,$min,$sec,$mon,$day,$year) - : @mktime($hr,$min,$sec,$mon,$day,$year); - } - - $gmt_different = ($is_gmt) ? 0 : $this->getGMTDiff(); - - /* - # 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 = (int)$mon; - $day = (int)$day; - $year = (int)$year; - - - $year = $this->digitCheck($year); - - if ($mon > 12) - { - $y = floor($mon / 12); - $year += $y; - $mon -= $y*12; - } - else if ($mon < 1) - { - $y = ceil((1-$mon) / 12); - $year -= $y; - $mon += $y*12; - } - - $_day_power = 86400; - $_hour_power = 3600; - $_min_power = 60; - - $_month_table_normal = & self::$_month_normal; - $_month_table_leaf = & self::$_month_leaf; - - $_total_date = 0; - if ($year >= 1970) - { - for ($a = 1970 ; $a <= $year; $a++) - { - $leaf = $this->isLeapYear($a); - if ($leaf == true) { - $loop_table = $_month_table_leaf; - $_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 + $gmt_different; - - } else { - for ($a = 1969 ; $a >= $year; $a--) { - $leaf = $this->isLeapYear($a); - if ($leaf == true) { - $loop_table = $_month_table_leaf; - $_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 - $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; - return $ret; + $dt = new DateTime(); + if($is_gmt) + $dt->setTimeZone(new DateTimeZone('UTC')); + $dt->setDate($year!==false ? $year : date('Y'), + $mon!==false ? $mon : date('m'), + $day!==false ? $day : date('d')); + $dt->setTime($hr, $min, $sec); + return $dt->format('U'); } } -- cgit v1.2.3