Modified documentation to recommend no longer using the mode, prepend, or append...
[openemr.git] / library / adodb / adodb-time.inc.php
blobb3e8062b62b066fa6ba0b188a9c80ee309e86796
1 <?php
2 /**
3 ADOdb Date Library, part of the ADOdb abstraction library
4 Download: http://php.weblogs.com/adodb_date_time_library
6 PHP native date functions use integer timestamps for computations.
7 Because of this, dates are restricted to the years 1901-2038 on Unix
8 and 1970-2038 on Windows due to integer overflow for dates beyond
9 those years. This library overcomes these limitations by replacing the
10 native function's signed integers (normally 32-bits) with PHP floating
11 point numbers (normally 64-bits).
13 Dates from 100 A.D. to 3000 A.D. and later
14 have been tested. The minimum is 100 A.D. as <100 will invoke the
15 2 => 4 digit year conversion. The maximum is billions of years in the
16 future, but this is a theoretical limit as the computation of that year
17 would take too long with the current implementation of adodb_mktime().
19 This library replaces native functions as follows:
21 <pre>
22 getdate() with adodb_getdate()
23 date() with adodb_date()
24 gmdate() with adodb_gmdate()
25 mktime() with adodb_mktime()
26 gmmktime() with adodb_gmmktime()
27 </pre>
29 The parameters are identical, except that adodb_date() accepts a subset
30 of date()'s field formats. Mktime() will convert from local time to GMT,
31 and date() will convert from GMT to local time, but daylight savings is
32 not handled currently.
34 This library is independant of the rest of ADOdb, and can be used
35 as standalone code.
37 PERFORMANCE
39 For high speed, this library uses the native date functions where
40 possible, and only switches to PHP code when the dates fall outside
41 the 32-bit signed integer range.
43 GREGORIAN CORRECTION
45 Pope Gregory shortened October of A.D. 1582 by ten days. Thursday,
46 October 4, 1582 (Julian) was followed immediately by Friday, October 15,
47 1582 (Gregorian).
49 Since 0.06, we handle this correctly, so:
51 adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582)
52 == 24 * 3600 (1 day)
54 =============================================================================
56 COPYRIGHT
58 (c) 2003 John Lim and released under BSD-style license except for code by jackbbs,
59 which includes adodb_mktime, adodb_get_gmt_diff, adodb_is_leap_year
60 and originally found at http://www.php.net/manual/en/function.mktime.php
62 =============================================================================
64 BUG REPORTS
66 These should be posted to the ADOdb forums at
68 http://phplens.com/lens/lensforum/topics.php?id=4
70 =============================================================================
72 FUNCTION DESCRIPTIONS
75 FUNCTION adodb_getdate($date=false)
77 Returns an array containing date information, as getdate(), but supports
78 dates greater than 1901 to 2038.
81 FUNCTION adodb_date($fmt, $timestamp = false)
83 Convert a timestamp to a formatted local date. If $timestamp is not defined, the
84 current timestamp is used. Unlike the function date(), it supports dates
85 outside the 1901 to 2038 range.
87 The format fields that adodb_date supports:
89 <pre>
90 a - "am" or "pm"
91 A - "AM" or "PM"
92 d - day of the month, 2 digits with leading zeros; i.e. "01" to "31"
93 D - day of the week, textual, 3 letters; e.g. "Fri"
94 F - month, textual, long; e.g. "January"
95 g - hour, 12-hour format without leading zeros; i.e. "1" to "12"
96 G - hour, 24-hour format without leading zeros; i.e. "0" to "23"
97 h - hour, 12-hour format; i.e. "01" to "12"
98 H - hour, 24-hour format; i.e. "00" to "23"
99 i - minutes; i.e. "00" to "59"
100 j - day of the month without leading zeros; i.e. "1" to "31"
101 l (lowercase 'L') - day of the week, textual, long; e.g. "Friday"
102 L - boolean for whether it is a leap year; i.e. "0" or "1"
103 m - month; i.e. "01" to "12"
104 M - month, textual, 3 letters; e.g. "Jan"
105 n - month without leading zeros; i.e. "1" to "12"
106 O - Difference to Greenwich time in hours; e.g. "+0200"
107 Q - Quarter, as in 1, 2, 3, 4
108 r - RFC 822 formatted date; e.g. "Thu, 21 Dec 2000 16:01:07 +0200"
109 s - seconds; i.e. "00" to "59"
110 S - English ordinal suffix for the day of the month, 2 characters;
111 i.e. "st", "nd", "rd" or "th"
112 t - number of days in the given month; i.e. "28" to "31"
113 T - Timezone setting of this machine; e.g. "EST" or "MDT"
114 U - seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
115 w - day of the week, numeric, i.e. "0" (Sunday) to "6" (Saturday)
116 Y - year, 4 digits; e.g. "1999"
117 y - year, 2 digits; e.g. "99"
118 z - day of the year; i.e. "0" to "365"
119 Z - timezone offset in seconds (i.e. "-43200" to "43200").
120 The offset for timezones west of UTC is always negative,
121 and for those east of UTC is always positive.
122 </pre>
124 Unsupported:
125 <pre>
126 B - Swatch Internet time
127 I (capital i) - "1" if Daylight Savings Time, "0" otherwise.
128 W - ISO-8601 week number of year, weeks starting on Monday
130 </pre>
132 FUNCTION adodb_date2($fmt, $isoDateString = false)
133 Same as adodb_date, but 2nd parameter accepts iso date, eg.
135 adodb_date2('d-M-Y H:i','2003-12-25 13:01:34');
137 FUNCTION adodb_gmdate($fmt, $timestamp = false)
139 Convert a timestamp to a formatted GMT date. If $timestamp is not defined, the
140 current timestamp is used. Unlike the function date(), it supports dates
141 outside the 1901 to 2038 range.
144 FUNCTION adodb_mktime($hr, $min, $sec, $month, $day, $year)
146 Converts a local date to a unix timestamp. Unlike the function mktime(), it supports
147 dates outside the 1901 to 2038 range. Differs from mktime() in that all parameters
148 are currently compulsory.
150 FUNCTION adodb_gmmktime($hr, $min, $sec, $month, $day, $year)
152 Converts a gmt date to a unix timestamp. Unlike the function gmmktime(), it supports
153 dates outside the 1901 to 2038 range. Differs from gmmktime() in that all parameters
154 are currently compulsory.
156 =============================================================================
158 NOTES
160 Useful url for generating test timestamps:
161 http://www.4webhelp.net/us/timestamp.php
163 Possible future optimizations include
165 a. Using an algorithm similar to Plauger's in "The Standard C Library"
166 (page 428, xttotm.c _Ttotm() function). Plauger's algorithm will not
167 work outside 32-bit signed range, so i decided not to implement it.
169 b. Iterate over a block of years (say 12) when searching for the
170 correct year.
172 c. Implement daylight savings, which looks awfully complicated, see
173 http://webexhibits.org/daylightsaving/
176 CHANGELOG
177 - 26 Oct 2003 0.11
178 Because of daylight savings problems (some systems apply daylight savings to
179 January!!!), changed adodb_get_gmt_diff() to ignore daylight savings.
181 - 9 Aug 2003 0.10
182 Fixed bug with dates after 2038.
183 See http://phplens.com/lens/lensforum/msgs.php?id=6980
185 - 1 July 2003 0.09
186 Added support for Q (Quarter).
187 Added adodb_date2(), which accepts ISO date in 2nd param
189 - 3 March 2003 0.08
190 Added support for 'S' adodb_date() format char. Added constant ADODB_ALLOW_NEGATIVE_TS
191 if you want PHP to handle negative timestamps between 1901 to 1969.
193 - 27 Feb 2003 0.07
194 All negative numbers handled by adodb now because of RH 7.3+ problems.
195 See http://bugs.php.net/bug.php?id=20048&edit=2
197 - 4 Feb 2003 0.06
198 Fixed a typo, 1852 changed to 1582! This means that pre-1852 dates
199 are now correctly handled.
201 - 29 Jan 2003 0.05
203 Leap year checking differs under Julian calendar (pre 1582). Also
204 leap year code optimized by checking for most common case first.
206 We also handle month overflow correctly in mktime (eg month set to 13).
208 Day overflow for less than one month's days is supported.
210 - 28 Jan 2003 0.04
212 Gregorian correction handled. In PHP5, we might throw an error if
213 mktime uses invalid dates around 5-14 Oct 1582. Released with ADOdb 3.10.
214 Added limbo 5-14 Oct 1582 check, when we set to 15 Oct 1582.
216 - 27 Jan 2003 0.03
218 Fixed some more month problems due to gmt issues. Added constant ADODB_DATE_VERSION.
219 Fixed calculation of days since start of year for <1970.
221 - 27 Jan 2003 0.02
223 Changed _adodb_getdate() to inline leap year checking for better performance.
224 Fixed problem with time-zones west of GMT +0000.
226 - 24 Jan 2003 0.01
228 First implementation.
232 /* Initialization */
235 Version Number
237 define('ADODB_DATE_VERSION',0.11);
240 We check for Windows as only +ve ints are accepted as dates on Windows.
242 Apparently this problem happens also with Linux, RH 7.3 and later!
244 glibc-2.2.5-34 and greater has been changed to return -1 for dates <
245 1970. This used to work. The problem exists with RedHat 7.3 and 8.0
246 echo (mktime(0, 0, 0, 1, 1, 1960)); // prints -1
248 References:
249 http://bugs.php.net/bug.php?id=20048&edit=2
250 http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html
253 if (!defined('ADODB_ALLOW_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1);
255 function adodb_date_test_date($y1,$m)
257 //print " $y1/$m ";
258 $t = adodb_mktime(0,0,0,$m,13,$y1);
259 if ("$y1-$m-13 00:00:00" != adodb_date('Y-n-d H:i:s',$t)) {
260 print "<b>$y1 error</b><br>";
261 return false;
263 return true;
266 Test Suite
268 function adodb_date_test()
271 error_reporting(E_ALL);
272 print "<h4>Testing adodb_date and adodb_mktime. version=".ADODB_DATE_VERSION. "</h4>";
273 set_time_limit(0);
274 $fail = false;
276 // This flag disables calling of PHP native functions, so we can properly test the code
277 if (!defined('ADODB_TEST_DATES')) define('ADODB_TEST_DATES',1);
279 print "<p>Testing gregorian <=> julian conversion<p>";
280 $t = adodb_mktime(0,0,0,10,11,1492);
281 //http://www.holidayorigins.com/html/columbus_day.html - Friday check
282 if (!(adodb_date('D Y-m-d',$t) == 'Fri 1492-10-11')) print 'Error in Columbus landing<br>';
284 $t = adodb_mktime(0,0,0,2,29,1500);
285 if (!(adodb_date('Y-m-d',$t) == '1500-02-29')) print 'Error in julian leap years<br>';
287 $t = adodb_mktime(0,0,0,2,29,1700);
288 if (!(adodb_date('Y-m-d',$t) == '1700-03-01')) print 'Error in gregorian leap years<br>';
290 print adodb_mktime(0,0,0,10,4,1582).' ';
291 print adodb_mktime(0,0,0,10,15,1582);
292 $diff = (adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582));
293 if ($diff != 3600*24) print " <b>Error in gregorian correction = ".($diff/3600/24)." days </b><br>";
295 print " 15 Oct 1582, Fri=".(adodb_dow(1582,10,15) == 5 ? 'Fri' : '<b>Error</b>')."<br>";
296 print " 4 Oct 1582, Thu=".(adodb_dow(1582,10,4) == 4 ? 'Thu' : '<b>Error</b>')."<br>";
298 print "<p>Testing overflow<p>";
300 $t = adodb_mktime(0,0,0,3,33,1965);
301 if (!(adodb_date('Y-m-d',$t) == '1965-04-02')) print 'Error in day overflow 1 <br>';
302 $t = adodb_mktime(0,0,0,4,33,1971);
303 if (!(adodb_date('Y-m-d',$t) == '1971-05-03')) print 'Error in day overflow 2 <br>';
304 $t = adodb_mktime(0,0,0,1,60,1965);
305 if (!(adodb_date('Y-m-d',$t) == '1965-03-01')) print 'Error in day overflow 3 '.adodb_date('Y-m-d',$t).' <br>';
306 $t = adodb_mktime(0,0,0,12,32,1965);
307 if (!(adodb_date('Y-m-d',$t) == '1966-01-01')) print 'Error in day overflow 4 '.adodb_date('Y-m-d',$t).' <br>';
308 $t = adodb_mktime(0,0,0,12,63,1965);
309 if (!(adodb_date('Y-m-d',$t) == '1966-02-01')) print 'Error in day overflow 5 '.adodb_date('Y-m-d',$t).' <br>';
310 $t = adodb_mktime(0,0,0,13,3,1965);
311 if (!(adodb_date('Y-m-d',$t) == '1966-01-03')) print 'Error in mth overflow 1 <br>';
313 print "Testing 2-digit => 4-digit year conversion<p>";
314 if (adodb_year_digit_check(00) != 2000) print "Err 2-digit 2000<br>";
315 if (adodb_year_digit_check(10) != 2010) print "Err 2-digit 2010<br>";
316 if (adodb_year_digit_check(20) != 2020) print "Err 2-digit 2020<br>";
317 if (adodb_year_digit_check(30) != 2030) print "Err 2-digit 2030<br>";
318 if (adodb_year_digit_check(40) != 1940) print "Err 2-digit 1940<br>";
319 if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950<br>";
320 if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990<br>";
322 // Test string formating
323 print "<p>Testing date formating</p>";
324 $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\C822 r s t U w y Y z Z 2003';
325 $s1 = date($fmt,0);
326 $s2 = adodb_date($fmt,0);
327 if ($s1 != $s2) {
328 print " date() 0 failed<br>$s1<br>$s2<br>";
330 flush();
331 for ($i=100; --$i > 0; ) {
333 $ts = 3600.0*((rand()%60000)+(rand()%60000))+(rand()%60000);
334 $s1 = date($fmt,$ts);
335 $s2 = adodb_date($fmt,$ts);
336 //print "$s1 <br>$s2 <p>";
337 $pos = strcmp($s1,$s2);
339 if (($s1) != ($s2)) {
340 for ($j=0,$k=strlen($s1); $j < $k; $j++) {
341 if ($s1[$j] != $s2[$j]) {
342 print substr($s1,$j).' ';
343 break;
346 print "<b>Error date(): $ts<br><pre>
347 &nbsp; \"$s1\" (date len=".strlen($s1).")
348 &nbsp; \"$s2\" (adodb_date len=".strlen($s2).")</b></pre><br>";
349 $fail = true;
352 $a1 = getdate($ts);
353 $a2 = adodb_getdate($ts);
354 $rez = array_diff($a1,$a2);
355 if (sizeof($rez)>0) {
356 print "<b>Error getdate() $ts</b><br>";
357 print_r($a1);
358 print "<br>";
359 print_r($a2);
360 print "<p>";
361 $fail = true;
365 // Test generation of dates outside 1901-2038
366 print "<p>Testing random dates between 100 and 4000</p>";
367 adodb_date_test_date(100,1);
368 for ($i=100; --$i >= 0;) {
369 $y1 = 100+rand(0,1970-100);
370 $m = rand(1,12);
371 adodb_date_test_date($y1,$m);
373 $y1 = 3000-rand(0,3000-1970);
374 adodb_date_test_date($y1,$m);
376 print '<p>';
377 $start = 1960+rand(0,10);
378 $yrs = 12;
379 $i = 365.25*86400*($start-1970);
380 $offset = 36000+rand(10000,60000);
381 $max = 365*$yrs*86400;
382 $lastyear = 0;
384 // we generate a timestamp, convert it to a date, and convert it back to a timestamp
385 // and check if the roundtrip broke the original timestamp value.
386 print "Testing $start to ".($start+$yrs).", or $max seconds, offset=$offset: ";
388 for ($max += $i; $i < $max; $i += $offset) {
389 $ret = adodb_date('m,d,Y,H,i,s',$i);
390 $arr = explode(',',$ret);
391 if ($lastyear != $arr[2]) {
392 $lastyear = $arr[2];
393 print " $lastyear ";
394 flush();
396 $newi = adodb_mktime($arr[3],$arr[4],$arr[5],$arr[0],$arr[1],$arr[2]);
397 if ($i != $newi) {
398 print "Error at $i, adodb_mktime returned $newi ($ret)";
399 $fail = true;
400 break;
404 if (!$fail) print "<p>Passed !</p>";
405 else print "<p><b>Failed</b> :-(</p>";
409 Returns day of week, 0 = Sunday,... 6=Saturday.
410 Algorithm from PEAR::Date_Calc
412 function adodb_dow($year, $month, $day)
415 Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and
416 proclaimed that from that time onwards 3 days would be dropped from the calendar
417 every 400 years.
419 Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian).
421 if ($year <= 1582) {
422 if ($year < 1582 ||
423 ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) $greg_correction = 3;
424 else
425 $greg_correction = 0;
426 } else
427 $greg_correction = 0;
429 if($month > 2)
430 $month -= 2;
431 else {
432 $month += 10;
433 $year--;
436 $day = ( floor((13 * $month - 1) / 5) +
437 $day + ($year % 100) +
438 floor(($year % 100) / 4) +
439 floor(($year / 100) / 4) - 2 *
440 floor($year / 100) + 77);
442 return (($day - 7 * floor($day / 7))) + $greg_correction;
447 Checks for leap year, returns true if it is. No 2-digit year check. Also
448 handles julian calendar correctly.
450 function _adodb_is_leap_year($year)
452 if ($year % 4 != 0) return false;
454 if ($year % 400 == 0) {
455 return true;
456 // if gregorian calendar (>1582), century not-divisible by 400 is not leap
457 } else if ($year > 1582 && $year % 100 == 0 ) {
458 return false;
461 return true;
465 checks for leap year, returns true if it is. Has 2-digit year check
467 function adodb_is_leap_year($year)
469 return _adodb_is_leap_year(adodb_year_digit_check($year));
473 Fix 2-digit years. Works for any century.
474 Assumes that if 2-digit is more than 30 years in future, then previous century.
476 function adodb_year_digit_check($y)
478 if ($y < 100) {
480 $yr = (integer) date("Y");
481 $century = (integer) ($yr /100);
483 if ($yr%100 > 50) {
484 $c1 = $century + 1;
485 $c0 = $century;
486 } else {
487 $c1 = $century;
488 $c0 = $century - 1;
490 $c1 *= 100;
491 // if 2-digit year is less than 30 years in future, set it to this century
492 // otherwise if more than 30 years in future, then we set 2-digit year to the prev century.
493 if (($y + $c1) < $yr+30) $y = $y + $c1;
494 else $y = $y + $c0*100;
496 return $y;
500 get local time zone offset from GMT
502 function adodb_get_gmt_diff()
504 static $TZ;
505 if (isset($TZ)) return $TZ;
507 $TZ = mktime(0,0,0,1,2,1970,0) - gmmktime(0,0,0,1,2,1970,0);
508 return $TZ;
512 Returns an array with date info.
514 function adodb_getdate($d=false,$fast=false)
516 if ($d === false) return getdate();
517 if (!defined('ADODB_TEST_DATES')) {
518 if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
519 if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer
520 return @getdate($d);
523 return _adodb_getdate($d);
527 Low-level function that returns the getdate() array. We have a special
528 $fast flag, which if set to true, will return fewer array values,
529 and is much faster as it does not calculate dow, etc.
531 function _adodb_getdate($origd=false,$fast=false,$is_gmt=false)
533 $d = $origd - ($is_gmt ? 0 : adodb_get_gmt_diff());
535 $_day_power = 86400;
536 $_hour_power = 3600;
537 $_min_power = 60;
539 if ($d < -12219321600) $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction
541 $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31);
542 $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31);
544 if ($d < 0) {
545 $origd = $d;
546 // The valid range of a 32bit signed timestamp is typically from
547 // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT
548 for ($a = 1970 ; --$a >= 0;) {
549 $lastd = $d;
551 if ($leaf = _adodb_is_leap_year($a)) {
552 $d += $_day_power * 366;
553 } else
554 $d += $_day_power * 365;
555 if ($d >= 0) {
556 $year = $a;
557 break;
561 $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd;
563 $d = $lastd;
564 $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;
565 for ($a = 13 ; --$a > 0;) {
566 $lastd = $d;
567 $d += $mtab[$a] * $_day_power;
568 if ($d >= 0) {
569 $month = $a;
570 $ndays = $mtab[$a];
571 break;
575 $d = $lastd;
576 $day = $ndays + ceil(($d+1) / ($_day_power));
578 $d += ($ndays - $day+1)* $_day_power;
579 $hour = floor($d/$_hour_power);
581 } else {
583 for ($a = 1970 ;; $a++) {
584 $lastd = $d;
586 if ($leaf = _adodb_is_leap_year($a)) {
587 $d -= $_day_power * 366;
588 } else
589 $d -= $_day_power * 365;
590 if ($d < 0) {
591 $year = $a;
592 break;
595 $secsInYear = $lastd;
596 $d = $lastd;
597 $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;
598 for ($a = 1 ; $a <= 12; $a++) {
599 $lastd = $d;
600 $d -= $mtab[$a] * $_day_power;
601 if ($d <= 0) {
602 $month = $a;
603 $ndays = $mtab[$a];
604 break;
607 $d = $lastd;
608 $day = ceil(($d+1) / $_day_power);
609 $d = $d - ($day-1) * $_day_power;
610 $hour = floor($d /$_hour_power);
613 $d -= $hour * $_hour_power;
614 $min = floor($d/$_min_power);
615 $secs = $d - $min * $_min_power;
616 if ($fast) {
617 return array(
618 'seconds' => $secs,
619 'minutes' => $min,
620 'hours' => $hour,
621 'mday' => $day,
622 'mon' => $month,
623 'year' => $year,
624 'yday' => floor($secsInYear/$_day_power),
625 'leap' => $leaf,
626 'ndays' => $ndays
631 $dow = adodb_dow($year,$month,$day);
633 return array(
634 'seconds' => $secs,
635 'minutes' => $min,
636 'hours' => $hour,
637 'mday' => $day,
638 'wday' => $dow,
639 'mon' => $month,
640 'year' => $year,
641 'yday' => floor($secsInYear/$_day_power),
642 'weekday' => gmdate('l',$_day_power*(3+$dow)),
643 'month' => gmdate('F',mktime(0,0,0,$month,2,1971)),
644 0 => $origd
648 function adodb_gmdate($fmt,$d=false)
650 return adodb_date($fmt,$d,true);
653 function adodb_date2($fmt, $d=false, $is_gmt=false)
655 if ($d !== false) {
656 if (!preg_match(
657 "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
658 ($d), $rr)) return adodb_date($fmt,false,$is_gmt);
660 if ($rr[1] <= 100 && $rr[2]<= 1) return adodb_date($fmt,false,$is_gmt);
662 // h-m-s-MM-DD-YY
663 if (!isset($rr[5])) $d = adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
664 else $d = @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
667 return adodb_date($fmt,$d,$is_gmt);
671 Return formatted date based on timestamp $d
673 function adodb_date($fmt,$d=false,$is_gmt=false)
675 if ($d === false) return date($fmt);
676 if (!defined('ADODB_TEST_DATES')) {
677 if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
678 if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer
679 return @date($fmt,$d);
682 $_day_power = 86400;
684 $arr = _adodb_getdate($d,true,$is_gmt);
685 $year = $arr['year'];
686 $month = $arr['mon'];
687 $day = $arr['mday'];
688 $hour = $arr['hours'];
689 $min = $arr['minutes'];
690 $secs = $arr['seconds'];
692 $max = strlen($fmt);
693 $dates = '';
696 at this point, we have the following integer vars to manipulate:
697 $year, $month, $day, $hour, $min, $secs
699 for ($i=0; $i < $max; $i++) {
700 switch($fmt[$i]) {
701 case 'T': $dates .= date('T');break;
702 // YEAR
703 case 'L': $dates .= $arr['leap'] ? '1' : '0'; break;
704 case 'r': // Thu, 21 Dec 2000 16:01:07 +0200
706 $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))).', '
707 . ($day<10?' '.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' ';
709 if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour;
711 if ($min < 10) $dates .= ':0'.$min; else $dates .= ':'.$min;
713 if ($secs < 10) $dates .= ':0'.$secs; else $dates .= ':'.$secs;
715 $gmt = adodb_get_gmt_diff();
716 $dates .= sprintf(' %s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break;
718 case 'Y': $dates .= $year; break;
719 case 'y': $dates .= substr($year,strlen($year)-2,2); break;
720 // MONTH
721 case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break;
722 case 'Q': $dates .= ($month+3)>>2; break;
723 case 'n': $dates .= $month; break;
724 case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break;
725 case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break;
726 // DAY
727 case 't': $dates .= $arr['ndays']; break;
728 case 'z': $dates .= $arr['yday']; break;
729 case 'w': $dates .= adodb_dow($year,$month,$day); break;
730 case 'l': $dates .= gmdate('l',$_day_power*(3+adodb_dow($year,$month,$day))); break;
731 case 'D': $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))); break;
732 case 'j': $dates .= $day; break;
733 case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break;
734 case 'S':
735 $d10 = $day % 10;
736 if ($d10 == 1) $dates .= 'st';
737 else if ($d10 == 2) $dates .= 'nd';
738 else if ($d10 == 3) $dates .= 'rd';
739 else $dates .= 'th';
740 break;
742 // HOUR
743 case 'Z':
744 $dates .= ($is_gmt) ? 0 : -adodb_get_gmt_diff(); break;
745 case 'O':
746 $gmt = ($is_gmt) ? 0 : adodb_get_gmt_diff();
747 $dates .= sprintf('%s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break;
749 case 'H':
750 if ($hour < 10) $dates .= '0'.$hour;
751 else $dates .= $hour;
752 break;
753 case 'h':
754 if ($hour > 12) $hh = $hour - 12;
755 else {
756 if ($hour == 0) $hh = '12';
757 else $hh = $hour;
760 if ($hh < 10) $dates .= '0'.$hh;
761 else $dates .= $hh;
762 break;
764 case 'G':
765 $dates .= $hour;
766 break;
768 case 'g':
769 if ($hour > 12) $hh = $hour - 12;
770 else {
771 if ($hour == 0) $hh = '12';
772 else $hh = $hour;
774 $dates .= $hh;
775 break;
776 // MINUTES
777 case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break;
778 // SECONDS
779 case 'U': $dates .= $d; break;
780 case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break;
781 // AM/PM
782 // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM
783 case 'a':
784 if ($hour>=12) $dates .= 'pm';
785 else $dates .= 'am';
786 break;
787 case 'A':
788 if ($hour>=12) $dates .= 'PM';
789 else $dates .= 'AM';
790 break;
791 default:
792 $dates .= $fmt[$i]; break;
793 // ESCAPE
794 case "\\":
795 $i++;
796 if ($i < $max) $dates .= $fmt[$i];
797 break;
800 return $dates;
804 Returns a timestamp given a GMT/UTC time.
805 Note that $is_dst is not implemented and is ignored.
807 function adodb_gmmktime($hr,$min,$sec,$mon,$day,$year,$is_dst=false)
809 return adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst,true);
813 Return a timestamp given a local time. Originally by jackbbs.
814 Note that $is_dst is not implemented and is ignored.
816 function adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst=false,$is_gmt=false)
818 if (!defined('ADODB_TEST_DATES')) {
819 // for windows, we don't check 1970 because with timezone differences,
820 // 1 Jan 1970 could generate negative timestamp, which is illegal
821 if (!defined('ADODB_NO_NEGATIVE_TS') || ($year >= 1971))
822 if (1901 < $year && $year < 2038)
823 return @mktime($hr,$min,$sec,$mon,$day,$year);
826 $gmt_different = ($is_gmt) ? 0 : adodb_get_gmt_diff();
828 $hr = intval($hr);
829 $min = intval($min);
830 $sec = intval($sec);
831 $mon = intval($mon);
832 $day = intval($day);
833 $year = intval($year);
836 $year = adodb_year_digit_check($year);
838 if ($mon > 12) {
839 $y = floor($mon / 12);
840 $year += $y;
841 $mon -= $y*12;
844 $_day_power = 86400;
845 $_hour_power = 3600;
846 $_min_power = 60;
848 $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31);
849 $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31);
851 $_total_date = 0;
852 if ($year >= 1970) {
853 for ($a = 1970 ; $a <= $year; $a++) {
854 $leaf = _adodb_is_leap_year($a);
855 if ($leaf == true) {
856 $loop_table = $_month_table_leaf;
857 $_add_date = 366;
858 } else {
859 $loop_table = $_month_table_normal;
860 $_add_date = 365;
862 if ($a < $year) {
863 $_total_date += $_add_date;
864 } else {
865 for($b=1;$b<$mon;$b++) {
866 $_total_date += $loop_table[$b];
870 $_total_date +=$day-1;
871 $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different;
873 } else {
874 for ($a = 1969 ; $a >= $year; $a--) {
875 $leaf = _adodb_is_leap_year($a);
876 if ($leaf == true) {
877 $loop_table = $_month_table_leaf;
878 $_add_date = 366;
879 } else {
880 $loop_table = $_month_table_normal;
881 $_add_date = 365;
883 if ($a > $year) { $_total_date += $_add_date;
884 } else {
885 for($b=12;$b>$mon;$b--) {
886 $_total_date += $loop_table[$b];
890 $_total_date += $loop_table[$mon] - $day;
892 $_day_time = $hr * $_hour_power + $min * $_min_power + $sec;
893 $_day_time = $_day_power - $_day_time;
894 $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different);
895 if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction
896 else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582.
898 //print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret;
899 return $ret;