1<?php 2/** 3ADOdb Date Library, part of the ADOdb abstraction library 4See Wiki: https://adodb.org/dokuwiki/doku.php?id=v5:datetime:datetime_index 5Download: https://github.com/ADOdb/ADOdb/blob/master/adodb-time.inc.php 6 7PHP native date functions use integer timestamps for computations. 8Because of this, dates are restricted to the years 1901-2038 on Unix 9and 1970-2038 on Windows due to integer overflow for dates beyond 10those years. This library overcomes these limitations by replacing the 11native function's signed integers (normally 32-bits) with PHP floating 12point numbers (normally 64-bits). 13 14Dates from 100 A.D. to 3000 A.D. and later 15have been tested. The minimum is 100 A.D. as <100 will invoke the 162 => 4 digit year conversion. The maximum is billions of years in the 17future, but this is a theoretical limit as the computation of that year 18would take too long with the current implementation of adodb_mktime(). 19 20This library replaces native functions as follows: 21 22<pre> 23 getdate() with adodb_getdate() 24 date() with adodb_date() 25 gmdate() with adodb_gmdate() 26 mktime() with adodb_mktime() 27 gmmktime() with adodb_gmmktime() 28 strftime() with adodb_strftime() 29 strftime() with adodb_gmstrftime() 30</pre> 31 32The parameters are identical, except that adodb_date() accepts a subset 33of date()'s field formats. Mktime() will convert from local time to GMT, 34and date() will convert from GMT to local time, but daylight savings is 35not handled currently. 36 37This library is independant of the rest of ADOdb, and can be used 38as standalone code. 39 40PERFORMANCE 41 42For high speed, this library uses the native date functions where 43possible, and only switches to PHP code when the dates fall outside 44the 32-bit signed integer range. 45 46GREGORIAN CORRECTION 47 48Pope Gregory shortened October of A.D. 1582 by ten days. Thursday, 49October 4, 1582 (Julian) was followed immediately by Friday, October 15, 501582 (Gregorian). 51 52Since 0.06, we handle this correctly, so: 53 54adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582) 55 == 24 * 3600 (1 day) 56 57============================================================================= 58 59COPYRIGHT 60 61(c) 2003-2014 John Lim and released under BSD-style license except for code by 62jackbbs, which includes adodb_mktime, adodb_get_gmt_diff, adodb_is_leap_year 63and originally found at http://www.php.net/manual/en/function.mktime.php 64 65============================================================================= 66 67BUG REPORTS 68 69These should be posted to the ADOdb forums at 70 71 http://phplens.com/lens/lensforum/topics.php?id=4 72 73============================================================================= 74 75FUNCTION DESCRIPTIONS 76 77** FUNCTION adodb_time() 78 79Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) as an unsigned integer. 80 81** FUNCTION adodb_getdate($date=false) 82 83Returns an array containing date information, as getdate(), but supports 84dates greater than 1901 to 2038. The local date/time format is derived from a 85heuristic the first time adodb_getdate is called. 86 87 88** FUNCTION adodb_date($fmt, $timestamp = false) 89 90Convert a timestamp to a formatted local date. If $timestamp is not defined, the 91current timestamp is used. Unlike the function date(), it supports dates 92outside the 1901 to 2038 range. 93 94The format fields that adodb_date supports: 95 96<pre> 97 a - "am" or "pm" 98 A - "AM" or "PM" 99 d - day of the month, 2 digits with leading zeros; i.e. "01" to "31" 100 D - day of the week, textual, 3 letters; e.g. "Fri" 101 F - month, textual, long; e.g. "January" 102 g - hour, 12-hour format without leading zeros; i.e. "1" to "12" 103 G - hour, 24-hour format without leading zeros; i.e. "0" to "23" 104 h - hour, 12-hour format; i.e. "01" to "12" 105 H - hour, 24-hour format; i.e. "00" to "23" 106 i - minutes; i.e. "00" to "59" 107 j - day of the month without leading zeros; i.e. "1" to "31" 108 l (lowercase 'L') - day of the week, textual, long; e.g. "Friday" 109 L - boolean for whether it is a leap year; i.e. "0" or "1" 110 m - month; i.e. "01" to "12" 111 M - month, textual, 3 letters; e.g. "Jan" 112 n - month without leading zeros; i.e. "1" to "12" 113 O - Difference to Greenwich time in hours; e.g. "+0200" 114 Q - Quarter, as in 1, 2, 3, 4 115 r - RFC 2822 formatted date; e.g. "Thu, 21 Dec 2000 16:01:07 +0200" 116 s - seconds; i.e. "00" to "59" 117 S - English ordinal suffix for the day of the month, 2 characters; 118 i.e. "st", "nd", "rd" or "th" 119 t - number of days in the given month; i.e. "28" to "31" 120 T - Timezone setting of this machine; e.g. "EST" or "MDT" 121 U - seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 122 w - day of the week, numeric, i.e. "0" (Sunday) to "6" (Saturday) 123 Y - year, 4 digits; e.g. "1999" 124 y - year, 2 digits; e.g. "99" 125 z - day of the year; i.e. "0" to "365" 126 Z - timezone offset in seconds (i.e. "-43200" to "43200"). 127 The offset for timezones west of UTC is always negative, 128 and for those east of UTC is always positive. 129</pre> 130 131Unsupported: 132<pre> 133 B - Swatch Internet time 134 I (capital i) - "1" if Daylight Savings Time, "0" otherwise. 135 W - ISO-8601 week number of year, weeks starting on Monday 136 137</pre> 138 139 140** FUNCTION adodb_date2($fmt, $isoDateString = false) 141Same as adodb_date, but 2nd parameter accepts iso date, eg. 142 143 adodb_date2('d-M-Y H:i','2003-12-25 13:01:34'); 144 145 146** FUNCTION adodb_gmdate($fmt, $timestamp = false) 147 148Convert a timestamp to a formatted GMT date. If $timestamp is not defined, the 149current timestamp is used. Unlike the function date(), it supports dates 150outside the 1901 to 2038 range. 151 152 153** FUNCTION adodb_mktime($hr, $min, $sec[, $month, $day, $year]) 154 155Converts a local date to a unix timestamp. Unlike the function mktime(), it supports 156dates outside the 1901 to 2038 range. All parameters are optional. 157 158 159** FUNCTION adodb_gmmktime($hr, $min, $sec [, $month, $day, $year]) 160 161Converts a gmt date to a unix timestamp. Unlike the function gmmktime(), it supports 162dates outside the 1901 to 2038 range. Differs from gmmktime() in that all parameters 163are currently compulsory. 164 165** FUNCTION adodb_gmstrftime($fmt, $timestamp = false) 166Convert a timestamp to a formatted GMT date. 167 168** FUNCTION adodb_strftime($fmt, $timestamp = false) 169 170Convert a timestamp to a formatted local date. Internally converts $fmt into 171adodb_date format, then echo result. 172 173For best results, you can define the local date format yourself. Define a global 174variable $ADODB_DATE_LOCALE which is an array, 1st element is date format using 175adodb_date syntax, and 2nd element is the time format, also in adodb_date syntax. 176 177 eg. $ADODB_DATE_LOCALE = array('d/m/Y','H:i:s'); 178 179 Supported format codes: 180 181<pre> 182 %a - abbreviated weekday name according to the current locale 183 %A - full weekday name according to the current locale 184 %b - abbreviated month name according to the current locale 185 %B - full month name according to the current locale 186 %c - preferred date and time representation for the current locale 187 %d - day of the month as a decimal number (range 01 to 31) 188 %D - same as %m/%d/%y 189 %e - day of the month as a decimal number, a single digit is preceded by a space (range ' 1' to '31') 190 %h - same as %b 191 %H - hour as a decimal number using a 24-hour clock (range 00 to 23) 192 %I - hour as a decimal number using a 12-hour clock (range 01 to 12) 193 %m - month as a decimal number (range 01 to 12) 194 %M - minute as a decimal number 195 %n - newline character 196 %p - either `am' or `pm' according to the given time value, or the corresponding strings for the current locale 197 %r - time in a.m. and p.m. notation 198 %R - time in 24 hour notation 199 %S - second as a decimal number 200 %t - tab character 201 %T - current time, equal to %H:%M:%S 202 %x - preferred date representation for the current locale without the time 203 %X - preferred time representation for the current locale without the date 204 %y - year as a decimal number without a century (range 00 to 99) 205 %Y - year as a decimal number including the century 206 %Z - time zone or name or abbreviation 207 %% - a literal `%' character 208</pre> 209 210 Unsupported codes: 211<pre> 212 %C - century number (the year divided by 100 and truncated to an integer, range 00 to 99) 213 %g - like %G, but without the century. 214 %G - The 4-digit year corresponding to the ISO week number (see %V). 215 This has the same format and value as %Y, except that if the ISO week number belongs 216 to the previous or next year, that year is used instead. 217 %j - day of the year as a decimal number (range 001 to 366) 218 %u - weekday as a decimal number [1,7], with 1 representing Monday 219 %U - week number of the current year as a decimal number, starting 220 with the first Sunday as the first day of the first week 221 %V - The ISO 8601:1988 week number of the current year as a decimal number, 222 range 01 to 53, where week 1 is the first week that has at least 4 days in the 223 current year, and with Monday as the first day of the week. (Use %G or %g for 224 the year component that corresponds to the week number for the specified timestamp.) 225 %w - day of the week as a decimal, Sunday being 0 226 %W - week number of the current year as a decimal number, starting with the 227 first Monday as the first day of the first week 228</pre> 229 230============================================================================= 231 232NOTES 233 234Useful url for generating test timestamps: 235 http://www.4webhelp.net/us/timestamp.php 236 237Possible future optimizations include 238 239a. Using an algorithm similar to Plauger's in "The Standard C Library" 240(page 428, xttotm.c _Ttotm() function). Plauger's algorithm will not 241work outside 32-bit signed range, so i decided not to implement it. 242 243b. Implement daylight savings, which looks awfully complicated, see 244 http://webexhibits.org/daylightsaving/ 245 246 247CHANGELOG 248- 16 Jan 2011 0.36 249Added adodb_time() which returns current time. If > 2038, will return as float 250 251- 7 Feb 2011 0.35 252Changed adodb_date to be symmetric with adodb_mktime. See $jan1_71. fix for bc. 253 254- 13 July 2010 0.34 255Changed adodb_get_gm_diff to use DateTimeZone(). 256 257- 11 Feb 2008 0.33 258* Bug in 0.32 fix for hour handling. Fixed. 259 260- 1 Feb 2008 0.32 261* Now adodb_mktime(0,0,0,12+$m,20,2040) works properly. 262 263- 10 Jan 2008 0.31 264* Now adodb_mktime(0,0,0,24,1,2037) works correctly. 265 266- 15 July 2007 0.30 267Added PHP 5.2.0 compatability fixes. 268 * gmtime behaviour for 1970 has changed. We use the actual date if it is between 1970 to 2038 to get the 269 * timezone, otherwise we use the current year as the baseline to retrieve the timezone. 270 * Also the timezone's in php 5.2.* support historical data better, eg. if timezone today was +8, but 271 in 1970 it was +7:30, then php 5.2 return +7:30, while this library will use +8. 272 * 273 274- 19 March 2006 0.24 275Changed strftime() locale detection, because some locales prepend the day of week to the date when %c is used. 276 277- 10 Feb 2006 0.23 278PHP5 compat: when we detect PHP5, the RFC2822 format for gmt 0000hrs is changed from -0000 to +0000. 279 In PHP4, we will still use -0000 for 100% compat with PHP4. 280 281- 08 Sept 2005 0.22 282In adodb_date2(), $is_gmt not supported properly. Fixed. 283 284- 18 July 2005 0.21 285In PHP 4.3.11, the 'r' format has changed. Leading 0 in day is added. Changed for compat. 286Added support for negative months in adodb_mktime(). 287 288- 24 Feb 2005 0.20 289Added limited strftime/gmstrftime support. x10 improvement in performance of adodb_date(). 290 291- 21 Dec 2004 0.17 292In adodb_getdate(), the timestamp was accidentally converted to gmt when $is_gmt is false. 293Also adodb_mktime(0,0,0) did not work properly. Both fixed thx Mauro. 294 295- 17 Nov 2004 0.16 296Removed intval typecast in adodb_mktime() for secs, allowing: 297 adodb_mktime(0,0,0 + 2236672153,1,1,1934); 298Suggested by Ryan. 299 300- 18 July 2004 0.15 301All params in adodb_mktime were formerly compulsory. Now only the hour, min, secs is compulsory. 302This brings it more in line with mktime (still not identical). 303 304- 23 June 2004 0.14 305 306Allow you to define your own daylights savings function, adodb_daylight_sv. 307If the function is defined (somewhere in an include), then you can correct for daylights savings. 308 309In this example, we apply daylights savings in June or July, adding one hour. This is extremely 310unrealistic as it does not take into account time-zone, geographic location, current year. 311 312function adodb_daylight_sv(&$arr, $is_gmt) 313{ 314 if ($is_gmt) return; 315 $m = $arr['mon']; 316 if ($m == 6 || $m == 7) $arr['hours'] += 1; 317} 318 319This is only called by adodb_date() and not by adodb_mktime(). 320 321The format of $arr is 322Array ( 323 [seconds] => 0 324 [minutes] => 0 325 [hours] => 0 326 [mday] => 1 # day of month, eg 1st day of the month 327 [mon] => 2 # month (eg. Feb) 328 [year] => 2102 329 [yday] => 31 # days in current year 330 [leap] => # true if leap year 331 [ndays] => 28 # no of days in current month 332 ) 333 334 335- 28 Apr 2004 0.13 336Fixed adodb_date to properly support $is_gmt. Thx to Dimitar Angelov. 337 338- 20 Mar 2004 0.12 339Fixed month calculation error in adodb_date. 2102-June-01 appeared as 2102-May-32. 340 341- 26 Oct 2003 0.11 342Because of daylight savings problems (some systems apply daylight savings to 343January!!!), changed adodb_get_gmt_diff() to ignore daylight savings. 344 345- 9 Aug 2003 0.10 346Fixed bug with dates after 2038. 347See http://phplens.com/lens/lensforum/msgs.php?id=6980 348 349- 1 July 2003 0.09 350Added support for Q (Quarter). 351Added adodb_date2(), which accepts ISO date in 2nd param 352 353- 3 March 2003 0.08 354Added support for 'S' adodb_date() format char. Added constant ADODB_ALLOW_NEGATIVE_TS 355if you want PHP to handle negative timestamps between 1901 to 1969. 356 357- 27 Feb 2003 0.07 358All negative numbers handled by adodb now because of RH 7.3+ problems. 359See http://bugs.php.net/bug.php?id=20048&edit=2 360 361- 4 Feb 2003 0.06 362Fixed a typo, 1852 changed to 1582! This means that pre-1852 dates 363are now correctly handled. 364 365- 29 Jan 2003 0.05 366 367Leap year checking differs under Julian calendar (pre 1582). Also 368leap year code optimized by checking for most common case first. 369 370We also handle month overflow correctly in mktime (eg month set to 13). 371 372Day overflow for less than one month's days is supported. 373 374- 28 Jan 2003 0.04 375 376Gregorian correction handled. In PHP5, we might throw an error if 377mktime uses invalid dates around 5-14 Oct 1582. Released with ADOdb 3.10. 378Added limbo 5-14 Oct 1582 check, when we set to 15 Oct 1582. 379 380- 27 Jan 2003 0.03 381 382Fixed some more month problems due to gmt issues. Added constant ADODB_DATE_VERSION. 383Fixed calculation of days since start of year for <1970. 384 385- 27 Jan 2003 0.02 386 387Changed _adodb_getdate() to inline leap year checking for better performance. 388Fixed problem with time-zones west of GMT +0000. 389 390- 24 Jan 2003 0.01 391 392First implementation. 393*/ 394 395 396/* Initialization */ 397 398/* 399 Version Number 400*/ 401define('ADODB_DATE_VERSION',0.35); 402 403$ADODB_DATETIME_CLASS = (PHP_VERSION >= 5.2); 404 405/* 406 This code was originally for windows. But apparently this problem happens 407 also with Linux, RH 7.3 and later! 408 409 glibc-2.2.5-34 and greater has been changed to return -1 for dates < 410 1970. This used to work. The problem exists with RedHat 7.3 and 8.0 411 echo (mktime(0, 0, 0, 1, 1, 1960)); // prints -1 412 413 References: 414 http://bugs.php.net/bug.php?id=20048&edit=2 415 http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html 416*/ 417 418if (!defined('ADODB_ALLOW_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1); 419 420function adodb_date_test_date($y1,$m,$d=13) 421{ 422 $h = round(rand()% 24); 423 $t = adodb_mktime($h,0,0,$m,$d,$y1); 424 $rez = adodb_date('Y-n-j H:i:s',$t); 425 if ($h == 0) $h = '00'; 426 else if ($h < 10) $h = '0'.$h; 427 if ("$y1-$m-$d $h:00:00" != $rez) { 428 print "<b>$y1 error, expected=$y1-$m-$d $h:00:00, adodb=$rez</b><br>"; 429 return false; 430 } 431 return true; 432} 433 434function adodb_date_test_strftime($fmt) 435{ 436 $s1 = strftime($fmt); 437 $s2 = adodb_strftime($fmt); 438 439 if ($s1 == $s2) return true; 440 441 echo "error for $fmt, strftime=$s1, adodb=$s2<br>"; 442 return false; 443} 444 445/** 446 Test Suite 447*/ 448function adodb_date_test() 449{ 450 451 for ($m=-24; $m<=24; $m++) 452 echo "$m :",adodb_date('d-m-Y',adodb_mktime(0,0,0,1+$m,20,2040)),"<br>"; 453 454 error_reporting(E_ALL); 455 print "<h4>Testing adodb_date and adodb_mktime. version=".ADODB_DATE_VERSION.' PHP='.PHP_VERSION."</h4>"; 456 @set_time_limit(0); 457 $fail = false; 458 459 // This flag disables calling of PHP native functions, so we can properly test the code 460 if (!defined('ADODB_TEST_DATES')) define('ADODB_TEST_DATES',1); 461 462 $t = time(); 463 464 465 $fmt = 'Y-m-d H:i:s'; 466 echo '<pre>'; 467 echo 'adodb: ',adodb_date($fmt,$t),'<br>'; 468 echo 'php : ',date($fmt,$t),'<br>'; 469 echo '</pre>'; 470 471 adodb_date_test_strftime('%Y %m %x %X'); 472 adodb_date_test_strftime("%A %d %B %Y"); 473 adodb_date_test_strftime("%H %M S"); 474 475 $t = adodb_mktime(0,0,0); 476 if (!(adodb_date('Y-m-d') == date('Y-m-d'))) print 'Error in '.adodb_mktime(0,0,0).'<br>'; 477 478 $t = adodb_mktime(0,0,0,6,1,2102); 479 if (!(adodb_date('Y-m-d',$t) == '2102-06-01')) print 'Error in '.adodb_date('Y-m-d',$t).'<br>'; 480 481 $t = adodb_mktime(0,0,0,2,1,2102); 482 if (!(adodb_date('Y-m-d',$t) == '2102-02-01')) print 'Error in '.adodb_date('Y-m-d',$t).'<br>'; 483 484 485 print "<p>Testing gregorian <=> julian conversion<p>"; 486 $t = adodb_mktime(0,0,0,10,11,1492); 487 //http://www.holidayorigins.com/html/columbus_day.html - Friday check 488 if (!(adodb_date('D Y-m-d',$t) == 'Fri 1492-10-11')) print 'Error in Columbus landing<br>'; 489 490 $t = adodb_mktime(0,0,0,2,29,1500); 491 if (!(adodb_date('Y-m-d',$t) == '1500-02-29')) print 'Error in julian leap years<br>'; 492 493 $t = adodb_mktime(0,0,0,2,29,1700); 494 if (!(adodb_date('Y-m-d',$t) == '1700-03-01')) print 'Error in gregorian leap years<br>'; 495 496 print adodb_mktime(0,0,0,10,4,1582).' '; 497 print adodb_mktime(0,0,0,10,15,1582); 498 $diff = (adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582)); 499 if ($diff != 3600*24) print " <b>Error in gregorian correction = ".($diff/3600/24)." days </b><br>"; 500 501 print " 15 Oct 1582, Fri=".(adodb_dow(1582,10,15) == 5 ? 'Fri' : '<b>Error</b>')."<br>"; 502 print " 4 Oct 1582, Thu=".(adodb_dow(1582,10,4) == 4 ? 'Thu' : '<b>Error</b>')."<br>"; 503 504 print "<p>Testing overflow<p>"; 505 506 $t = adodb_mktime(0,0,0,3,33,1965); 507 if (!(adodb_date('Y-m-d',$t) == '1965-04-02')) print 'Error in day overflow 1 <br>'; 508 $t = adodb_mktime(0,0,0,4,33,1971); 509 if (!(adodb_date('Y-m-d',$t) == '1971-05-03')) print 'Error in day overflow 2 <br>'; 510 $t = adodb_mktime(0,0,0,1,60,1965); 511 if (!(adodb_date('Y-m-d',$t) == '1965-03-01')) print 'Error in day overflow 3 '.adodb_date('Y-m-d',$t).' <br>'; 512 $t = adodb_mktime(0,0,0,12,32,1965); 513 if (!(adodb_date('Y-m-d',$t) == '1966-01-01')) print 'Error in day overflow 4 '.adodb_date('Y-m-d',$t).' <br>'; 514 $t = adodb_mktime(0,0,0,12,63,1965); 515 if (!(adodb_date('Y-m-d',$t) == '1966-02-01')) print 'Error in day overflow 5 '.adodb_date('Y-m-d',$t).' <br>'; 516 $t = adodb_mktime(0,0,0,13,3,1965); 517 if (!(adodb_date('Y-m-d',$t) == '1966-01-03')) print 'Error in mth overflow 1 <br>'; 518 519 print "Testing 2-digit => 4-digit year conversion<p>"; 520 if (adodb_year_digit_check(00) != 2000) print "Err 2-digit 2000<br>"; 521 if (adodb_year_digit_check(10) != 2010) print "Err 2-digit 2010<br>"; 522 if (adodb_year_digit_check(20) != 2020) print "Err 2-digit 2020<br>"; 523 if (adodb_year_digit_check(30) != 2030) print "Err 2-digit 2030<br>"; 524 if (adodb_year_digit_check(40) != 1940) print "Err 2-digit 1940<br>"; 525 if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950<br>"; 526 if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990<br>"; 527 528 // Test string formating 529 print "<p>Testing date formating</p>"; 530 531 $fmt = '\d\a\t\e T Y-m-d H:i:s a A d D F g G h H i j l L m M n O \R\F\C2822 r s t U w y Y z Z 2003'; 532 $s1 = date($fmt,0); 533 $s2 = adodb_date($fmt,0); 534 if ($s1 != $s2) { 535 print " date() 0 failed<br>$s1<br>$s2<br>"; 536 } 537 flush(); 538 for ($i=100; --$i > 0; ) { 539 540 $ts = 3600.0*((rand()%60000)+(rand()%60000))+(rand()%60000); 541 $s1 = date($fmt,$ts); 542 $s2 = adodb_date($fmt,$ts); 543 //print "$s1 <br>$s2 <p>"; 544 $pos = strcmp($s1,$s2); 545 546 if (($s1) != ($s2)) { 547 for ($j=0,$k=strlen($s1); $j < $k; $j++) { 548 if ($s1[$j] != $s2[$j]) { 549 print substr($s1,$j).' '; 550 break; 551 } 552 } 553 print "<b>Error date(): $ts<br><pre> 554 \"$s1\" (date len=".strlen($s1).") 555 \"$s2\" (adodb_date len=".strlen($s2).")</b></pre><br>"; 556 $fail = true; 557 } 558 559 $a1 = getdate($ts); 560 $a2 = adodb_getdate($ts); 561 $rez = array_diff($a1,$a2); 562 if (sizeof($rez)>0) { 563 print "<b>Error getdate() $ts</b><br>"; 564 print_r($a1); 565 print "<br>"; 566 print_r($a2); 567 print "<p>"; 568 $fail = true; 569 } 570 } 571 572 // Test generation of dates outside 1901-2038 573 print "<p>Testing random dates between 100 and 4000</p>"; 574 adodb_date_test_date(100,1); 575 for ($i=100; --$i >= 0;) { 576 $y1 = 100+rand(0,1970-100); 577 $m = rand(1,12); 578 adodb_date_test_date($y1,$m); 579 580 $y1 = 3000-rand(0,3000-1970); 581 adodb_date_test_date($y1,$m); 582 } 583 print '<p>'; 584 $start = 1960+rand(0,10); 585 $yrs = 12; 586 $i = 365.25*86400*($start-1970); 587 $offset = 36000+rand(10000,60000); 588 $max = 365*$yrs*86400; 589 $lastyear = 0; 590 591 // we generate a timestamp, convert it to a date, and convert it back to a timestamp 592 // and check if the roundtrip broke the original timestamp value. 593 print "Testing $start to ".($start+$yrs).", or $max seconds, offset=$offset: "; 594 $cnt = 0; 595 for ($max += $i; $i < $max; $i += $offset) { 596 $ret = adodb_date('m,d,Y,H,i,s',$i); 597 $arr = explode(',',$ret); 598 if ($lastyear != $arr[2]) { 599 $lastyear = $arr[2]; 600 print " $lastyear "; 601 flush(); 602 } 603 $newi = adodb_mktime($arr[3],$arr[4],$arr[5],$arr[0],$arr[1],$arr[2]); 604 if ($i != $newi) { 605 print "Error at $i, adodb_mktime returned $newi ($ret)"; 606 $fail = true; 607 break; 608 } 609 $cnt += 1; 610 } 611 echo "Tested $cnt dates<br>"; 612 if (!$fail) print "<p>Passed !</p>"; 613 else print "<p><b>Failed</b> :-(</p>"; 614} 615 616function adodb_time() 617{ 618 $d = new DateTime(); 619 return $d->format('U'); 620} 621 622/** 623 Returns day of week, 0 = Sunday,... 6=Saturday. 624 Algorithm from PEAR::Date_Calc 625*/ 626function adodb_dow($year, $month, $day) 627{ 628/* 629Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and 630proclaimed that from that time onwards 3 days would be dropped from the calendar 631every 400 years. 632 633Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian). 634*/ 635 if ($year <= 1582) { 636 if ($year < 1582 || 637 ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) $greg_correction = 3; 638 else 639 $greg_correction = 0; 640 } else 641 $greg_correction = 0; 642 643 if($month > 2) 644 $month -= 2; 645 else { 646 $month += 10; 647 $year--; 648 } 649 650 $day = floor((13 * $month - 1) / 5) + 651 $day + ($year % 100) + 652 floor(($year % 100) / 4) + 653 floor(($year / 100) / 4) - 2 * 654 floor($year / 100) + 77 + $greg_correction; 655 656 return $day - 7 * floor($day / 7); 657} 658 659 660/** 661 Checks for leap year, returns true if it is. No 2-digit year check. Also 662 handles julian calendar correctly. 663*/ 664function _adodb_is_leap_year($year) 665{ 666 if ($year % 4 != 0) return false; 667 668 if ($year % 400 == 0) { 669 return true; 670 // if gregorian calendar (>1582), century not-divisible by 400 is not leap 671 } else if ($year > 1582 && $year % 100 == 0 ) { 672 return false; 673 } 674 675 return true; 676} 677 678 679/** 680 checks for leap year, returns true if it is. Has 2-digit year check 681*/ 682function adodb_is_leap_year($year) 683{ 684 return _adodb_is_leap_year(adodb_year_digit_check($year)); 685} 686 687/** 688 Fix 2-digit years. Works for any century. 689 Assumes that if 2-digit is more than 30 years in future, then previous century. 690*/ 691function adodb_year_digit_check($y) 692{ 693 if ($y < 100) { 694 695 $yr = (integer) date("Y"); 696 $century = (integer) ($yr /100); 697 698 if ($yr%100 > 50) { 699 $c1 = $century + 1; 700 $c0 = $century; 701 } else { 702 $c1 = $century; 703 $c0 = $century - 1; 704 } 705 $c1 *= 100; 706 // if 2-digit year is less than 30 years in future, set it to this century 707 // otherwise if more than 30 years in future, then we set 2-digit year to the prev century. 708 if (($y + $c1) < $yr+30) $y = $y + $c1; 709 else $y = $y + $c0*100; 710 } 711 return $y; 712} 713 714function adodb_get_gmt_diff_ts($ts) 715{ 716 if (0 <= $ts && $ts <= 0x7FFFFFFF) { // check if number in 32-bit signed range) { 717 $arr = getdate($ts); 718 $y = $arr['year']; 719 $m = $arr['mon']; 720 $d = $arr['mday']; 721 return adodb_get_gmt_diff($y,$m,$d); 722 } else { 723 return adodb_get_gmt_diff(false,false,false); 724 } 725 726} 727 728/** 729 get local time zone offset from GMT. Does not handle historical timezones before 1970. 730*/ 731function adodb_get_gmt_diff($y,$m,$d) 732{ 733static $TZ,$tzo; 734global $ADODB_DATETIME_CLASS; 735 736 if (!defined('ADODB_TEST_DATES')) $y = false; 737 else if ($y < 1970 || $y >= 2038) $y = false; 738 739 if ($ADODB_DATETIME_CLASS && $y !== false) { 740 $dt = new DateTime(); 741 $dt->setISODate($y,$m,$d); 742 if (empty($tzo)) { 743 $tzo = new DateTimeZone(date_default_timezone_get()); 744 # $tzt = timezone_transitions_get( $tzo ); 745 } 746 return -$tzo->getOffset($dt); 747 } else { 748 if (isset($TZ)) return $TZ; 749 $y = date('Y'); 750 /* 751 if (function_exists('date_default_timezone_get') && function_exists('timezone_offset_get')) { 752 $tzonename = date_default_timezone_get(); 753 if ($tzonename) { 754 $tobj = new DateTimeZone($tzonename); 755 $TZ = -timezone_offset_get($tobj,new DateTime("now",$tzo)); 756 } 757 } 758 */ 759 if (empty($TZ)) $TZ = mktime(0,0,0,12,2,$y) - gmmktime(0,0,0,12,2,$y); 760 } 761 return $TZ; 762} 763 764/** 765 Returns an array with date info. 766*/ 767function adodb_getdate($d=false,$fast=false) 768{ 769 if ($d === false) return getdate(); 770 if (!defined('ADODB_TEST_DATES')) { 771 if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range 772 if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer 773 return @getdate($d); 774 } 775 } 776 return _adodb_getdate($d); 777} 778 779/* 780// generate $YRS table for _adodb_getdate() 781function adodb_date_gentable($out=true) 782{ 783 784 for ($i=1970; $i >= 1600; $i-=10) { 785 $s = adodb_gmmktime(0,0,0,1,1,$i); 786 echo "$i => $s,<br>"; 787 } 788} 789adodb_date_gentable(); 790 791for ($i=1970; $i > 1500; $i--) { 792 793echo "<hr />$i "; 794 adodb_date_test_date($i,1,1); 795} 796 797*/ 798 799 800$_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); 801$_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); 802 803function adodb_validdate($y,$m,$d) 804{ 805global $_month_table_normal,$_month_table_leaf; 806 807 if (_adodb_is_leap_year($y)) $marr = $_month_table_leaf; 808 else $marr = $_month_table_normal; 809 810 if ($m > 12 || $m < 1) return false; 811 812 if ($d > 31 || $d < 1) return false; 813 814 if ($marr[$m] < $d) return false; 815 816 if ($y < 1000 && $y > 3000) return false; 817 818 return true; 819} 820 821/** 822 Low-level function that returns the getdate() array. We have a special 823 $fast flag, which if set to true, will return fewer array values, 824 and is much faster as it does not calculate dow, etc. 825*/ 826function _adodb_getdate($origd=false,$fast=false,$is_gmt=false) 827{ 828static $YRS; 829global $_month_table_normal,$_month_table_leaf; 830 831 $d = $origd - ($is_gmt ? 0 : adodb_get_gmt_diff_ts($origd)); 832 $_day_power = 86400; 833 $_hour_power = 3600; 834 $_min_power = 60; 835 836 if ($d < -12219321600) $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction 837 838 $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); 839 $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); 840 841 $d366 = $_day_power * 366; 842 $d365 = $_day_power * 365; 843 844 if ($d < 0) { 845 846 if (empty($YRS)) $YRS = array( 847 1970 => 0, 848 1960 => -315619200, 849 1950 => -631152000, 850 1940 => -946771200, 851 1930 => -1262304000, 852 1920 => -1577923200, 853 1910 => -1893456000, 854 1900 => -2208988800, 855 1890 => -2524521600, 856 1880 => -2840140800, 857 1870 => -3155673600, 858 1860 => -3471292800, 859 1850 => -3786825600, 860 1840 => -4102444800, 861 1830 => -4417977600, 862 1820 => -4733596800, 863 1810 => -5049129600, 864 1800 => -5364662400, 865 1790 => -5680195200, 866 1780 => -5995814400, 867 1770 => -6311347200, 868 1760 => -6626966400, 869 1750 => -6942499200, 870 1740 => -7258118400, 871 1730 => -7573651200, 872 1720 => -7889270400, 873 1710 => -8204803200, 874 1700 => -8520336000, 875 1690 => -8835868800, 876 1680 => -9151488000, 877 1670 => -9467020800, 878 1660 => -9782640000, 879 1650 => -10098172800, 880 1640 => -10413792000, 881 1630 => -10729324800, 882 1620 => -11044944000, 883 1610 => -11360476800, 884 1600 => -11676096000); 885 886 if ($is_gmt) $origd = $d; 887 // The valid range of a 32bit signed timestamp is typically from 888 // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT 889 // 890 891 # old algorithm iterates through all years. new algorithm does it in 892 # 10 year blocks 893 894 /* 895 # old algo 896 for ($a = 1970 ; --$a >= 0;) { 897 $lastd = $d; 898 899 if ($leaf = _adodb_is_leap_year($a)) $d += $d366; 900 else $d += $d365; 901 902 if ($d >= 0) { 903 $year = $a; 904 break; 905 } 906 } 907 */ 908 909 $lastsecs = 0; 910 $lastyear = 1970; 911 foreach($YRS as $year => $secs) { 912 if ($d >= $secs) { 913 $a = $lastyear; 914 break; 915 } 916 $lastsecs = $secs; 917 $lastyear = $year; 918 } 919 920 $d -= $lastsecs; 921 if (!isset($a)) $a = $lastyear; 922 923 //echo ' yr=',$a,' ', $d,'.'; 924 925 for (; --$a >= 0;) { 926 $lastd = $d; 927 928 if ($leaf = _adodb_is_leap_year($a)) $d += $d366; 929 else $d += $d365; 930 931 if ($d >= 0) { 932 $year = $a; 933 break; 934 } 935 } 936 /**/ 937 938 $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd; 939 940 $d = $lastd; 941 $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; 942 for ($a = 13 ; --$a > 0;) { 943 $lastd = $d; 944 $d += $mtab[$a] * $_day_power; 945 if ($d >= 0) { 946 $month = $a; 947 $ndays = $mtab[$a]; 948 break; 949 } 950 } 951 952 $d = $lastd; 953 $day = $ndays + ceil(($d+1) / ($_day_power)); 954 955 $d += ($ndays - $day+1)* $_day_power; 956 $hour = floor($d/$_hour_power); 957 958 } else { 959 for ($a = 1970 ;; $a++) { 960 $lastd = $d; 961 962 if ($leaf = _adodb_is_leap_year($a)) $d -= $d366; 963 else $d -= $d365; 964 if ($d < 0) { 965 $year = $a; 966 break; 967 } 968 } 969 $secsInYear = $lastd; 970 $d = $lastd; 971 $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; 972 for ($a = 1 ; $a <= 12; $a++) { 973 $lastd = $d; 974 $d -= $mtab[$a] * $_day_power; 975 if ($d < 0) { 976 $month = $a; 977 $ndays = $mtab[$a]; 978 break; 979 } 980 } 981 $d = $lastd; 982 $day = ceil(($d+1) / $_day_power); 983 $d = $d - ($day-1) * $_day_power; 984 $hour = floor($d /$_hour_power); 985 } 986 987 $d -= $hour * $_hour_power; 988 $min = floor($d/$_min_power); 989 $secs = $d - $min * $_min_power; 990 if ($fast) { 991 return array( 992 'seconds' => $secs, 993 'minutes' => $min, 994 'hours' => $hour, 995 'mday' => $day, 996 'mon' => $month, 997 'year' => $year, 998 'yday' => floor($secsInYear/$_day_power), 999 'leap' => $leaf, 1000 'ndays' => $ndays 1001 ); 1002 } 1003 1004 1005 $dow = adodb_dow($year,$month,$day); 1006 1007 return array( 1008 'seconds' => $secs, 1009 'minutes' => $min, 1010 'hours' => $hour, 1011 'mday' => $day, 1012 'wday' => $dow, 1013 'mon' => $month, 1014 'year' => $year, 1015 'yday' => floor($secsInYear/$_day_power), 1016 'weekday' => gmdate('l',$_day_power*(3+$dow)), 1017 'month' => gmdate('F',mktime(0,0,0,$month,2,1971)), 1018 0 => $origd 1019 ); 1020} 1021/* 1022 if ($isphp5) 1023 $dates .= sprintf('%s%04d',($gmt<=0)?'+':'-',abs($gmt)/36); 1024 else 1025 $dates .= sprintf('%s%04d',($gmt<0)?'+':'-',abs($gmt)/36); 1026 break;*/ 1027function adodb_tz_offset($gmt,$isphp5) 1028{ 1029 $zhrs = abs($gmt)/3600; 1030 $hrs = floor($zhrs); 1031 if ($isphp5) 1032 return sprintf('%s%02d%02d',($gmt<=0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60); 1033 else 1034 return sprintf('%s%02d%02d',($gmt<0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60); 1035} 1036 1037 1038function adodb_gmdate($fmt,$d=false) 1039{ 1040 return adodb_date($fmt,$d,true); 1041} 1042 1043// accepts unix timestamp and iso date format in $d 1044function adodb_date2($fmt, $d=false, $is_gmt=false) 1045{ 1046 if ($d !== false) { 1047 if (!preg_match( 1048 "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", 1049 ($d), $rr)) return adodb_date($fmt,false,$is_gmt); 1050 1051 if ($rr[1] <= 100 && $rr[2]<= 1) return adodb_date($fmt,false,$is_gmt); 1052 1053 // h-m-s-MM-DD-YY 1054 if (!isset($rr[5])) $d = adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1],false,$is_gmt); 1055 else $d = @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1],false,$is_gmt); 1056 } 1057 1058 return adodb_date($fmt,$d,$is_gmt); 1059} 1060 1061 1062/** 1063 Return formatted date based on timestamp $d 1064*/ 1065function adodb_date($fmt,$d=false,$is_gmt=false) 1066{ 1067static $daylight; 1068global $ADODB_DATETIME_CLASS; 1069static $jan1_1971; 1070 1071 1072 if (!isset($daylight)) { 1073 $daylight = function_exists('adodb_daylight_sv'); 1074 if (empty($jan1_1971)) $jan1_1971 = mktime(0,0,0,1,1,1971); // we only use date() when > 1970 as adodb_mktime() only uses mktime() when > 1970 1075 } 1076 1077 if ($d === false) return ($is_gmt)? @gmdate($fmt): @date($fmt); 1078 if (!defined('ADODB_TEST_DATES')) { 1079 if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range 1080 1081 if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= $jan1_1971) // if windows, must be +ve integer 1082 return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d); 1083 1084 } 1085 } 1086 $_day_power = 86400; 1087 1088 $arr = _adodb_getdate($d,true,$is_gmt); 1089 1090 if ($daylight) adodb_daylight_sv($arr, $is_gmt); 1091 1092 $year = $arr['year']; 1093 $month = $arr['mon']; 1094 $day = $arr['mday']; 1095 $hour = $arr['hours']; 1096 $min = $arr['minutes']; 1097 $secs = $arr['seconds']; 1098 1099 $max = strlen($fmt); 1100 $dates = ''; 1101 1102 $isphp5 = PHP_VERSION >= 5; 1103 1104 /* 1105 at this point, we have the following integer vars to manipulate: 1106 $year, $month, $day, $hour, $min, $secs 1107 */ 1108 for ($i=0; $i < $max; $i++) { 1109 switch($fmt[$i]) { 1110 case 'e': 1111 $dates .= date('e'); 1112 break; 1113 case 'T': 1114 if ($ADODB_DATETIME_CLASS) { 1115 $dt = new DateTime(); 1116 $dt->SetDate($year,$month,$day); 1117 $dates .= $dt->Format('T'); 1118 } else 1119 $dates .= date('T'); 1120 break; 1121 // YEAR 1122 case 'L': $dates .= $arr['leap'] ? '1' : '0'; break; 1123 case 'r': // Thu, 21 Dec 2000 16:01:07 +0200 1124 1125 // 4.3.11 uses '04 Jun 2004' 1126 // 4.3.8 uses ' 4 Jun 2004' 1127 $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))).', ' 1128 . ($day<10?'0'.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' '; 1129 1130 if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour; 1131 1132 if ($min < 10) $dates .= ':0'.$min; else $dates .= ':'.$min; 1133 1134 if ($secs < 10) $dates .= ':0'.$secs; else $dates .= ':'.$secs; 1135 1136 $gmt = adodb_get_gmt_diff($year,$month,$day); 1137 1138 $dates .= ' '.adodb_tz_offset($gmt,$isphp5); 1139 break; 1140 1141 case 'Y': $dates .= $year; break; 1142 case 'y': $dates .= substr($year,strlen($year)-2,2); break; 1143 // MONTH 1144 case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break; 1145 case 'Q': $dates .= ($month+3)>>2; break; 1146 case 'n': $dates .= $month; break; 1147 case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break; 1148 case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break; 1149 // DAY 1150 case 't': $dates .= $arr['ndays']; break; 1151 case 'z': $dates .= $arr['yday']; break; 1152 case 'w': $dates .= adodb_dow($year,$month,$day); break; 1153 case 'l': $dates .= gmdate('l',$_day_power*(3+adodb_dow($year,$month,$day))); break; 1154 case 'D': $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))); break; 1155 case 'j': $dates .= $day; break; 1156 case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break; 1157 case 'S': 1158 $d10 = $day % 10; 1159 if ($d10 == 1) $dates .= 'st'; 1160 else if ($d10 == 2 && $day != 12) $dates .= 'nd'; 1161 else if ($d10 == 3) $dates .= 'rd'; 1162 else $dates .= 'th'; 1163 break; 1164 1165 // HOUR 1166 case 'Z': 1167 $dates .= ($is_gmt) ? 0 : -adodb_get_gmt_diff($year,$month,$day); break; 1168 case 'O': 1169 $gmt = ($is_gmt) ? 0 : adodb_get_gmt_diff($year,$month,$day); 1170 1171 $dates .= adodb_tz_offset($gmt,$isphp5); 1172 break; 1173 1174 case 'H': 1175 if ($hour < 10) $dates .= '0'.$hour; 1176 else $dates .= $hour; 1177 break; 1178 case 'h': 1179 if ($hour > 12) $hh = $hour - 12; 1180 else { 1181 if ($hour == 0) $hh = '12'; 1182 else $hh = $hour; 1183 } 1184 1185 if ($hh < 10) $dates .= '0'.$hh; 1186 else $dates .= $hh; 1187 break; 1188 1189 case 'G': 1190 $dates .= $hour; 1191 break; 1192 1193 case 'g': 1194 if ($hour > 12) $hh = $hour - 12; 1195 else { 1196 if ($hour == 0) $hh = '12'; 1197 else $hh = $hour; 1198 } 1199 $dates .= $hh; 1200 break; 1201 // MINUTES 1202 case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break; 1203 // SECONDS 1204 case 'U': $dates .= $d; break; 1205 case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break; 1206 // AM/PM 1207 // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM 1208 case 'a': 1209 if ($hour>=12) $dates .= 'pm'; 1210 else $dates .= 'am'; 1211 break; 1212 case 'A': 1213 if ($hour>=12) $dates .= 'PM'; 1214 else $dates .= 'AM'; 1215 break; 1216 default: 1217 $dates .= $fmt[$i]; break; 1218 // ESCAPE 1219 case "\\": 1220 $i++; 1221 if ($i < $max) $dates .= $fmt[$i]; 1222 break; 1223 } 1224 } 1225 return $dates; 1226} 1227 1228/** 1229 Returns a timestamp given a GMT/UTC time. 1230 Note that $is_dst is not implemented and is ignored. 1231*/ 1232function adodb_gmmktime($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_dst=false) 1233{ 1234 return adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst,true); 1235} 1236 1237/** 1238 Return a timestamp given a local time. Originally by jackbbs. 1239 Note that $is_dst is not implemented and is ignored. 1240 1241 Not a very fast algorithm - O(n) operation. Could be optimized to O(1). 1242*/ 1243function adodb_mktime($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_dst=false,$is_gmt=false) 1244{ 1245 if (!defined('ADODB_TEST_DATES')) { 1246 1247 if ($mon === false) { 1248 return $is_gmt? @gmmktime($hr,$min,$sec): @mktime($hr,$min,$sec); 1249 } 1250 1251 // for windows, we don't check 1970 because with timezone differences, 1252 // 1 Jan 1970 could generate negative timestamp, which is illegal 1253 $usephpfns = (1970 < $year && $year < 2038 1254 || !defined('ADODB_NO_NEGATIVE_TS') && (1901 < $year && $year < 2038) 1255 ); 1256 1257 1258 if ($usephpfns && ($year + $mon/12+$day/365.25+$hr/(24*365.25) >= 2038)) $usephpfns = false; 1259 1260 if ($usephpfns) { 1261 return $is_gmt ? 1262 @gmmktime($hr,$min,$sec,$mon,$day,$year): 1263 @mktime($hr,$min,$sec,$mon,$day,$year); 1264 } 1265 } 1266 1267 $gmt_different = ($is_gmt) ? 0 : adodb_get_gmt_diff($year,$mon,$day); 1268 1269 /* 1270 # disabled because some people place large values in $sec. 1271 # however we need it for $mon because we use an array... 1272 $hr = intval($hr); 1273 $min = intval($min); 1274 $sec = intval($sec); 1275 */ 1276 $mon = intval($mon); 1277 $day = intval($day); 1278 $year = intval($year); 1279 1280 1281 $year = adodb_year_digit_check($year); 1282 1283 if ($mon > 12) { 1284 $y = floor(($mon-1)/ 12); 1285 $year += $y; 1286 $mon -= $y*12; 1287 } else if ($mon < 1) { 1288 $y = ceil((1-$mon) / 12); 1289 $year -= $y; 1290 $mon += $y*12; 1291 } 1292 1293 $_day_power = 86400; 1294 $_hour_power = 3600; 1295 $_min_power = 60; 1296 1297 $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); 1298 $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); 1299 1300 $_total_date = 0; 1301 if ($year >= 1970) { 1302 for ($a = 1970 ; $a <= $year; $a++) { 1303 $leaf = _adodb_is_leap_year($a); 1304 if ($leaf == true) { 1305 $loop_table = $_month_table_leaf; 1306 $_add_date = 366; 1307 } else { 1308 $loop_table = $_month_table_normal; 1309 $_add_date = 365; 1310 } 1311 if ($a < $year) { 1312 $_total_date += $_add_date; 1313 } else { 1314 for($b=1;$b<$mon;$b++) { 1315 $_total_date += $loop_table[$b]; 1316 } 1317 } 1318 } 1319 $_total_date +=$day-1; 1320 $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different; 1321 1322 } else { 1323 for ($a = 1969 ; $a >= $year; $a--) { 1324 $leaf = _adodb_is_leap_year($a); 1325 if ($leaf == true) { 1326 $loop_table = $_month_table_leaf; 1327 $_add_date = 366; 1328 } else { 1329 $loop_table = $_month_table_normal; 1330 $_add_date = 365; 1331 } 1332 if ($a > $year) { $_total_date += $_add_date; 1333 } else { 1334 for($b=12;$b>$mon;$b--) { 1335 $_total_date += $loop_table[$b]; 1336 } 1337 } 1338 } 1339 $_total_date += $loop_table[$mon] - $day; 1340 1341 $_day_time = $hr * $_hour_power + $min * $_min_power + $sec; 1342 $_day_time = $_day_power - $_day_time; 1343 $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different); 1344 if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction 1345 else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582. 1346 } 1347 //print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret; 1348 return $ret; 1349} 1350 1351function adodb_gmstrftime($fmt, $ts=false) 1352{ 1353 return adodb_strftime($fmt,$ts,true); 1354} 1355 1356// hack - convert to adodb_date 1357function adodb_strftime($fmt, $ts=false,$is_gmt=false) 1358{ 1359global $ADODB_DATE_LOCALE; 1360 1361 if (!defined('ADODB_TEST_DATES')) { 1362 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range 1363 if (!defined('ADODB_NO_NEGATIVE_TS') || $ts >= 0) // if windows, must be +ve integer 1364 return ($is_gmt)? @gmstrftime($fmt,$ts): @strftime($fmt,$ts); 1365 1366 } 1367 } 1368 1369 if (empty($ADODB_DATE_LOCALE)) { 1370 /* 1371 $tstr = strtoupper(gmstrftime('%c',31366800)); // 30 Dec 1970, 1 am 1372 $sep = substr($tstr,2,1); 1373 $hasAM = strrpos($tstr,'M') !== false; 1374 */ 1375 # see http://phplens.com/lens/lensforum/msgs.php?id=14865 for reasoning, and changelog for version 0.24 1376 $dstr = gmstrftime('%x',31366800); // 30 Dec 1970, 1 am 1377 $sep = substr($dstr,2,1); 1378 $tstr = strtoupper(gmstrftime('%X',31366800)); // 30 Dec 1970, 1 am 1379 $hasAM = strrpos($tstr,'M') !== false; 1380 1381 $ADODB_DATE_LOCALE = array(); 1382 $ADODB_DATE_LOCALE[] = strncmp($tstr,'30',2) == 0 ? 'd'.$sep.'m'.$sep.'y' : 'm'.$sep.'d'.$sep.'y'; 1383 $ADODB_DATE_LOCALE[] = ($hasAM) ? 'h:i:s a' : 'H:i:s'; 1384 1385 } 1386 $inpct = false; 1387 $fmtdate = ''; 1388 for ($i=0,$max = strlen($fmt); $i < $max; $i++) { 1389 $ch = $fmt[$i]; 1390 if ($ch == '%') { 1391 if ($inpct) { 1392 $fmtdate .= '%'; 1393 $inpct = false; 1394 } else 1395 $inpct = true; 1396 } else if ($inpct) { 1397 1398 $inpct = false; 1399 switch($ch) { 1400 case '0': 1401 case '1': 1402 case '2': 1403 case '3': 1404 case '4': 1405 case '5': 1406 case '6': 1407 case '7': 1408 case '8': 1409 case '9': 1410 case 'E': 1411 case 'O': 1412 /* ignore format modifiers */ 1413 $inpct = true; 1414 break; 1415 1416 case 'a': $fmtdate .= 'D'; break; 1417 case 'A': $fmtdate .= 'l'; break; 1418 case 'h': 1419 case 'b': $fmtdate .= 'M'; break; 1420 case 'B': $fmtdate .= 'F'; break; 1421 case 'c': $fmtdate .= $ADODB_DATE_LOCALE[0].$ADODB_DATE_LOCALE[1]; break; 1422 case 'C': $fmtdate .= '\C?'; break; // century 1423 case 'd': $fmtdate .= 'd'; break; 1424 case 'D': $fmtdate .= 'm/d/y'; break; 1425 case 'e': $fmtdate .= 'j'; break; 1426 case 'g': $fmtdate .= '\g?'; break; //? 1427 case 'G': $fmtdate .= '\G?'; break; //? 1428 case 'H': $fmtdate .= 'H'; break; 1429 case 'I': $fmtdate .= 'h'; break; 1430 case 'j': $fmtdate .= '?z'; $parsej = true; break; // wrong as j=1-based, z=0-basd 1431 case 'm': $fmtdate .= 'm'; break; 1432 case 'M': $fmtdate .= 'i'; break; 1433 case 'n': $fmtdate .= "\n"; break; 1434 case 'p': $fmtdate .= 'a'; break; 1435 case 'r': $fmtdate .= 'h:i:s a'; break; 1436 case 'R': $fmtdate .= 'H:i:s'; break; 1437 case 'S': $fmtdate .= 's'; break; 1438 case 't': $fmtdate .= "\t"; break; 1439 case 'T': $fmtdate .= 'H:i:s'; break; 1440 case 'u': $fmtdate .= '?u'; $parseu = true; break; // wrong strftime=1-based, date=0-based 1441 case 'U': $fmtdate .= '?U'; $parseU = true; break;// wrong strftime=1-based, date=0-based 1442 case 'x': $fmtdate .= $ADODB_DATE_LOCALE[0]; break; 1443 case 'X': $fmtdate .= $ADODB_DATE_LOCALE[1]; break; 1444 case 'w': $fmtdate .= '?w'; $parseu = true; break; // wrong strftime=1-based, date=0-based 1445 case 'W': $fmtdate .= '?W'; $parseU = true; break;// wrong strftime=1-based, date=0-based 1446 case 'y': $fmtdate .= 'y'; break; 1447 case 'Y': $fmtdate .= 'Y'; break; 1448 case 'Z': $fmtdate .= 'T'; break; 1449 } 1450 } else if (('A' <= ($ch) && ($ch) <= 'Z' ) || ('a' <= ($ch) && ($ch) <= 'z' )) 1451 $fmtdate .= "\\".$ch; 1452 else 1453 $fmtdate .= $ch; 1454 } 1455 //echo "fmt=",$fmtdate,"<br>"; 1456 if ($ts === false) $ts = time(); 1457 $ret = adodb_date($fmtdate, $ts, $is_gmt); 1458 return $ret; 1459} 1460