]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiDB/adodb/adodb-time.inc.php
elseif
[SourceForge/phpwiki.git] / lib / WikiDB / adodb / adodb-time.inc.php
1 <?php
2 /**
3 ADOdb Date Library, part of the ADOdb abstraction library
4 Download: http://php.weblogs.com/adodb_date_time_library
5
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).
12
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().
18
19 This library replaces native functions as follows:
20
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>
28
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.
33
34 This library is independant of the rest of ADOdb, and can be used
35 as standalone code.
36
37 PERFORMANCE
38
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.
42
43 GREGORIAN CORRECTION
44
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).
48
49 Since 0.06, we handle this correctly, so:
50
51 adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582)
52     == 24 * 3600 (1 day)
53
54 =============================================================================
55
56 COPYRIGHT
57
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
61
62 =============================================================================
63
64 BUG REPORTS
65
66 These should be posted to the ADOdb forums at
67
68     http://phplens.com/lens/lensforum/topics.php?id=4
69
70 =============================================================================
71
72 FUNCTION DESCRIPTIONS
73
74 FUNCTION adodb_getdate($date=false)
75
76 Returns an array containing date information, as getdate(), but supports
77 dates greater than 1901 to 2038.
78
79 FUNCTION adodb_date($fmt, $timestamp = false)
80
81 Convert a timestamp to a formatted local date. If $timestamp is not defined, the
82 current timestamp is used. Unlike the function date(), it supports dates
83 outside the 1901 to 2038 range.
84
85 The format fields that adodb_date supports:
86
87 <pre>
88 a - "am" or "pm"
89 A - "AM" or "PM"
90 d - day of the month, 2 digits with leading zeros; i.e. "01" to "31"
91 D - day of the week, textual, 3 letters; e.g. "Fri"
92 F - month, textual, long; e.g. "January"
93 g - hour, 12-hour format without leading zeros; i.e. "1" to "12"
94 G - hour, 24-hour format without leading zeros; i.e. "0" to "23"
95 h - hour, 12-hour format; i.e. "01" to "12"
96 H - hour, 24-hour format; i.e. "00" to "23"
97 i - minutes; i.e. "00" to "59"
98 j - day of the month without leading zeros; i.e. "1" to "31"
99 l (lowercase 'L') - day of the week, textual, long; e.g. "Friday"
100 L - boolean for whether it is a leap year; i.e. "0" or "1"
101 m - month; i.e. "01" to "12"
102 M - month, textual, 3 letters; e.g. "Jan"
103 n - month without leading zeros; i.e. "1" to "12"
104 O - Difference to Greenwich time in hours; e.g. "+0200"
105 Q - Quarter, as in 1, 2, 3, 4
106 r - RFC 822 formatted date; e.g. "Thu, 21 Dec 2000 16:01:07 +0200"
107 s - seconds; i.e. "00" to "59"
108 S - English ordinal suffix for the day of the month, 2 characters;
109                i.e. "st", "nd", "rd" or "th"
110 t - number of days in the given month; i.e. "28" to "31"
111 T - Timezone setting of this machine; e.g. "EST" or "MDT"
112 U - seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
113 w - day of the week, numeric, i.e. "0" (Sunday) to "6" (Saturday)
114 Y - year, 4 digits; e.g. "1999"
115 y - year, 2 digits; e.g. "99"
116 z - day of the year; i.e. "0" to "365"
117 Z - timezone offset in seconds (i.e. "-43200" to "43200").
118                The offset for timezones west of UTC is always negative,
119             and for those east of UTC is always positive.
120 </pre>
121
122 Unsupported:
123 <pre>
124 B - Swatch Internet time
125 I (capital i) - "1" if Daylight Savings Time, "0" otherwise.
126 W - ISO-8601 week number of year, weeks starting on Monday
127
128 </pre>
129
130 FUNCTION adodb_date2($fmt, $isoDateString = false)
131 Same as adodb_date, but 2nd parameter accepts iso date, eg.
132
133   adodb_date2('d-M-Y H:i','2003-12-25 13:01:34');
134
135 FUNCTION adodb_gmdate($fmt, $timestamp = false)
136
137 Convert a timestamp to a formatted GMT date. If $timestamp is not defined, the
138 current timestamp is used. Unlike the function date(), it supports dates
139 outside the 1901 to 2038 range.
140
141 FUNCTION adodb_mktime($hr, $min, $sec, $month, $day, $year)
142
143 Converts a local date to a unix timestamp.  Unlike the function mktime(), it supports
144 dates outside the 1901 to 2038 range. Differs from mktime() in that all parameters
145 are currently compulsory.
146
147 FUNCTION adodb_gmmktime($hr, $min, $sec, $month, $day, $year)
148
149 Converts a gmt date to a unix timestamp.  Unlike the function gmmktime(), it supports
150 dates outside the 1901 to 2038 range. Differs from gmmktime() in that all parameters
151 are currently compulsory.
152
153 =============================================================================
154
155 NOTES
156
157 Useful url for generating test timestamps:
158     http://www.4webhelp.net/us/timestamp.php
159
160 Possible future optimizations include
161
162 a. Using an algorithm similar to Plauger's in "The Standard C Library"
163 (page 428, xttotm.c _Ttotm() function). Plauger's algorithm will not
164 work outside 32-bit signed range, so i decided not to implement it.
165
166 b. Iterate over a block of years (say 12) when searching for the
167 correct year.
168
169 c. Implement daylight savings, which looks awfully complicated, see
170     http://webexhibits.org/daylightsaving/
171
172 CHANGELOG
173 - 20 Mar 2004 0.12
174 Fixed month calculation error in adodb_date. 2102-June-01 appeared as 2102-May-32.
175
176 - 26 Oct 2003 0.11
177 Because of daylight savings problems (some systems apply daylight savings to
178 January!!!), changed adodb_get_gmt_diff() to ignore daylight savings.
179
180 - 9 Aug 2003 0.10
181 Fixed bug with dates after 2038.
182 See http://phplens.com/lens/lensforum/msgs.php?id=6980
183
184 - 1 July 2003 0.09
185 Added support for Q (Quarter).
186 Added adodb_date2(), which accepts ISO date in 2nd param
187
188 - 3 March 2003 0.08
189 Added support for 'S' adodb_date() format char. Added constant ADODB_ALLOW_NEGATIVE_TS
190 if you want PHP to handle negative timestamps between 1901 to 1969.
191
192 - 27 Feb 2003 0.07
193 All negative numbers handled by adodb now because of RH 7.3+ problems.
194 See http://bugs.php.net/bug.php?id=20048&edit=2
195
196 - 4 Feb 2003 0.06
197 Fixed a typo, 1852 changed to 1582! This means that pre-1852 dates
198 are now correctly handled.
199
200 - 29 Jan 2003 0.05
201
202 Leap year checking differs under Julian calendar (pre 1582). Also
203 leap year code optimized by checking for most common case first.
204
205 We also handle month overflow correctly in mktime (eg month set to 13).
206
207 Day overflow for less than one month's days is supported.
208
209 - 28 Jan 2003 0.04
210
211 Gregorian correction handled. In PHP5, we might throw an error if
212 mktime uses invalid dates around 5-14 Oct 1582. Released with ADOdb 3.10.
213 Added limbo 5-14 Oct 1582 check, when we set to 15 Oct 1582.
214
215 - 27 Jan 2003 0.03
216
217 Fixed some more month problems due to gmt issues. Added constant ADODB_DATE_VERSION.
218 Fixed calculation of days since start of year for <1970.
219
220 - 27 Jan 2003 0.02
221
222 Changed _adodb_getdate() to inline leap year checking for better performance.
223 Fixed problem with time-zones west of GMT +0000.
224
225 - 24 Jan 2003 0.01
226
227 First implementation.
228 */
229
230
231 /* Initialization */
232
233 /*
234     Version Number
235 */
236 define('ADODB_DATE_VERSION',0.12);
237
238 /*
239     We check for Windows as only +ve ints are accepted as dates on Windows.
240
241     Apparently this problem happens also with Linux, RH 7.3 and later!
242
243     glibc-2.2.5-34 and greater has been changed to return -1 for dates <
244     1970.  This used to work.  The problem exists with RedHat 7.3 and 8.0
245     echo (mktime(0, 0, 0, 1, 1, 1960));  // prints -1
246
247     References:
248      http://bugs.php.net/bug.php?id=20048&edit=2
249      http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html
250 */
251
252 if (!defined('ADODB_ALLOW_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1);
253
254 function adodb_date_test_date($y1,$m)
255 {
256     //print " $y1/$m ";
257     $t = adodb_mktime(0,0,0,$m,13,$y1);
258     if ("$y1-$m-13 00:00:00" != adodb_date('Y-n-d H:i:s',$t)) {
259         print "<b>$y1 error</b><br>";
260         return false;
261     }
262     return true;
263 }
264 /**
265      Test Suite
266 */
267 function adodb_date_test()
268 {
269
270     error_reporting(E_ALL);
271     print "<h4>Testing adodb_date and adodb_mktime. version=".ADODB_DATE_VERSION. "</h4>";
272     set_time_limit(0);
273     $fail = false;
274
275     // This flag disables calling of PHP native functions, so we can properly test the code
276     if (!defined('ADODB_TEST_DATES')) define('ADODB_TEST_DATES',1);
277
278     $t = adodb_mktime(0,0,0,6,1,2102);
279     if (!(adodb_date('Y-m-d',$t) == '2102-06-01')) print 'Error in '.adodb_date('Y-m-d',$t).'<br>';
280
281     $t = adodb_mktime(0,0,0,2,1,2102);
282     if (!(adodb_date('Y-m-d',$t) == '2102-02-01')) print 'Error in '.adodb_date('Y-m-d',$t).'<br>';
283
284
285     print "<p>Testing gregorian <=> julian conversion<p>";
286     $t = adodb_mktime(0,0,0,10,11,1492);
287     //http://www.holidayorigins.com/html/columbus_day.html - Friday check
288     if (!(adodb_date('D Y-m-d',$t) == 'Fri 1492-10-11')) print 'Error in Columbus landing<br>';
289
290     $t = adodb_mktime(0,0,0,2,29,1500);
291     if (!(adodb_date('Y-m-d',$t) == '1500-02-29')) print 'Error in julian leap years<br>';
292
293     $t = adodb_mktime(0,0,0,2,29,1700);
294     if (!(adodb_date('Y-m-d',$t) == '1700-03-01')) print 'Error in gregorian leap years<br>';
295
296     print  adodb_mktime(0,0,0,10,4,1582).' ';
297     print adodb_mktime(0,0,0,10,15,1582);
298     $diff = (adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582));
299     if ($diff != 3600*24) print " <b>Error in gregorian correction = ".($diff/3600/24)." days </b><br>";
300
301     print " 15 Oct 1582, Fri=".(adodb_dow(1582,10,15) == 5 ? 'Fri' : '<b>Error</b>')."<br>";
302     print " 4 Oct 1582, Thu=".(adodb_dow(1582,10,4) == 4 ? 'Thu' : '<b>Error</b>')."<br>";
303
304     print "<p>Testing overflow<p>";
305
306     $t = adodb_mktime(0,0,0,3,33,1965);
307     if (!(adodb_date('Y-m-d',$t) == '1965-04-02')) print 'Error in day overflow 1 <br>';
308     $t = adodb_mktime(0,0,0,4,33,1971);
309     if (!(adodb_date('Y-m-d',$t) == '1971-05-03')) print 'Error in day overflow 2 <br>';
310     $t = adodb_mktime(0,0,0,1,60,1965);
311     if (!(adodb_date('Y-m-d',$t) == '1965-03-01')) print 'Error in day overflow 3 '.adodb_date('Y-m-d',$t).' <br>';
312     $t = adodb_mktime(0,0,0,12,32,1965);
313     if (!(adodb_date('Y-m-d',$t) == '1966-01-01')) print 'Error in day overflow 4 '.adodb_date('Y-m-d',$t).' <br>';
314     $t = adodb_mktime(0,0,0,12,63,1965);
315     if (!(adodb_date('Y-m-d',$t) == '1966-02-01')) print 'Error in day overflow 5 '.adodb_date('Y-m-d',$t).' <br>';
316     $t = adodb_mktime(0,0,0,13,3,1965);
317     if (!(adodb_date('Y-m-d',$t) == '1966-01-03')) print 'Error in mth overflow 1 <br>';
318
319     print "Testing 2-digit => 4-digit year conversion<p>";
320     if (adodb_year_digit_check(00) != 2000) print "Err 2-digit 2000<br>";
321     if (adodb_year_digit_check(10) != 2010) print "Err 2-digit 2010<br>";
322     if (adodb_year_digit_check(20) != 2020) print "Err 2-digit 2020<br>";
323     if (adodb_year_digit_check(30) != 2030) print "Err 2-digit 2030<br>";
324     if (adodb_year_digit_check(40) != 1940) print "Err 2-digit 1940<br>";
325     if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950<br>";
326     if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990<br>";
327
328     // Test string formating
329     print "<p>Testing date formating</p>";
330     $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';
331     $s1 = date($fmt,0);
332     $s2 = adodb_date($fmt,0);
333     if ($s1 != $s2) {
334         print " date() 0 failed<br>$s1<br>$s2<br>";
335     }
336     flush();
337     for ($i=100; --$i > 0; ) {
338
339         $ts = 3600.0*((rand()%60000)+(rand()%60000))+(rand()%60000);
340         $s1 = date($fmt,$ts);
341         $s2 = adodb_date($fmt,$ts);
342         //print "$s1 <br>$s2 <p>";
343         $pos = strcmp($s1,$s2);
344
345         if (($s1) != ($s2)) {
346             for ($j=0,$k=strlen($s1); $j < $k; $j++) {
347                 if ($s1[$j] != $s2[$j]) {
348                     print substr($s1,$j).' ';
349                     break;
350                 }
351             }
352             print "<b>Error date(): $ts<br><pre>
353 &nbsp; \"$s1\" (date len=".strlen($s1).")
354 &nbsp; \"$s2\" (adodb_date len=".strlen($s2).")</b></pre><br>";
355             $fail = true;
356         }
357
358         $a1 = getdate($ts);
359         $a2 = adodb_getdate($ts);
360         $rez = array_diff($a1,$a2);
361         if (sizeof($rez)>0) {
362             print "<b>Error getdate() $ts</b><br>";
363                 print_r($a1);
364             print "<br>";
365                 print_r($a2);
366             print "<p>";
367             $fail = true;
368         }
369     }
370
371     // Test generation of dates outside 1901-2038
372     print "<p>Testing random dates between 100 and 4000</p>";
373     adodb_date_test_date(100,1);
374     for ($i=100; --$i >= 0;) {
375         $y1 = 100+rand(0,1970-100);
376         $m = rand(1,12);
377         adodb_date_test_date($y1,$m);
378
379         $y1 = 3000-rand(0,3000-1970);
380         adodb_date_test_date($y1,$m);
381     }
382     print '<p>';
383     $start = 1960+rand(0,10);
384     $yrs = 12;
385     $i = 365.25*86400*($start-1970);
386     $offset = 36000+rand(10000,60000);
387     $max = 365*$yrs*86400;
388     $lastyear = 0;
389
390     // we generate a timestamp, convert it to a date, and convert it back to a timestamp
391     // and check if the roundtrip broke the original timestamp value.
392     print "Testing $start to ".($start+$yrs).", or $max seconds, offset=$offset: ";
393
394     for ($max += $i; $i < $max; $i += $offset) {
395         $ret = adodb_date('m,d,Y,H,i,s',$i);
396         $arr = explode(',',$ret);
397         if ($lastyear != $arr[2]) {
398             $lastyear = $arr[2];
399             print " $lastyear ";
400             flush();
401         }
402         $newi = adodb_mktime($arr[3],$arr[4],$arr[5],$arr[0],$arr[1],$arr[2]);
403         if ($i != $newi) {
404             print "Error at $i, adodb_mktime returned $newi ($ret)";
405             $fail = true;
406             break;
407         }
408     }
409
410     if (!$fail) print "<p>Passed !</p>";
411     else print "<p><b>Failed</b> :-(</p>";
412 }
413
414 /**
415     Returns day of week, 0 = Sunday,... 6=Saturday.
416     Algorithm from PEAR::Date_Calc
417 */
418 function adodb_dow($year, $month, $day)
419 {
420 /*
421 Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and
422 proclaimed that from that time onwards 3 days would be dropped from the calendar
423 every 400 years.
424
425 Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian).
426 */
427     if ($year <= 1582) {
428         if ($year < 1582 ||
429             ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) $greg_correction = 3;
430          else
431             $greg_correction = 0;
432     } else
433         $greg_correction = 0;
434
435     if($month > 2)
436         $month -= 2;
437     else {
438         $month += 10;
439         $year--;
440     }
441
442     $day =  ( floor((13 * $month - 1) / 5) +
443             $day + ($year % 100) +
444             floor(($year % 100) / 4) +
445             floor(($year / 100) / 4) - 2 *
446             floor($year / 100) + 77);
447
448     return (($day - 7 * floor($day / 7))) + $greg_correction;
449 }
450
451
452 /**
453  Checks for leap year, returns true if it is. No 2-digit year check. Also
454  handles julian calendar correctly.
455 */
456 function _adodb_is_leap_year($year)
457 {
458     if ($year % 4 != 0) return false;
459
460     if ($year % 400 == 0) {
461         return true;
462     // if gregorian calendar (>1582), century not-divisible by 400 is not leap
463     } elseif ($year > 1582 && $year % 100 == 0 ) {
464         return false;
465     }
466
467     return true;
468 }
469
470 /**
471  checks for leap year, returns true if it is. Has 2-digit year check
472 */
473 function adodb_is_leap_year($year)
474 {
475     return  _adodb_is_leap_year(adodb_year_digit_check($year));
476 }
477
478 /**
479     Fix 2-digit years. Works for any century.
480      Assumes that if 2-digit is more than 30 years in future, then previous century.
481 */
482 function adodb_year_digit_check($y)
483 {
484     if ($y < 100) {
485
486         $yr = (integer) date("Y");
487         $century = (integer) ($yr /100);
488
489         if ($yr%100 > 50) {
490             $c1 = $century + 1;
491             $c0 = $century;
492         } else {
493             $c1 = $century;
494             $c0 = $century - 1;
495         }
496         $c1 *= 100;
497         // if 2-digit year is less than 30 years in future, set it to this century
498         // otherwise if more than 30 years in future, then we set 2-digit year to the prev century.
499         if (($y + $c1) < $yr+30) $y = $y + $c1;
500         else $y = $y + $c0*100;
501     }
502     return $y;
503 }
504
505 /**
506  get local time zone offset from GMT
507 */
508 function adodb_get_gmt_diff()
509 {
510 static $TZ;
511     if (isset($TZ)) return $TZ;
512
513     $TZ = mktime(0,0,0,1,2,1970,0) - gmmktime(0,0,0,1,2,1970,0);
514     return $TZ;
515 }
516
517 /**
518     Returns an array with date info.
519 */
520 function adodb_getdate($d=false,$fast=false)
521 {
522     if ($d === false) return getdate();
523     if (!defined('ADODB_TEST_DATES')) {
524         if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
525             if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer
526                 return @getdate($d);
527         }
528     }
529     return _adodb_getdate($d);
530 }
531
532 /**
533     Low-level function that returns the getdate() array. We have a special
534     $fast flag, which if set to true, will return fewer array values,
535     and is much faster as it does not calculate dow, etc.
536 */
537 function _adodb_getdate($origd=false,$fast=false,$is_gmt=false)
538 {
539     $d =  $origd - ($is_gmt ? 0 : adodb_get_gmt_diff());
540
541     $_day_power = 86400;
542     $_hour_power = 3600;
543     $_min_power = 60;
544
545     if ($d < -12219321600) $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction
546
547     $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31);
548     $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31);
549
550     if ($d < 0) {
551         $origd = $d;
552         // The valid range of a 32bit signed timestamp is typically from
553         // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT
554         for ($a = 1970 ; --$a >= 0;) {
555             $lastd = $d;
556
557             if ($leaf = _adodb_is_leap_year($a)) {
558                 $d += $_day_power * 366;
559             } else
560                 $d += $_day_power * 365;
561             if ($d >= 0) {
562                 $year = $a;
563                 break;
564             }
565         }
566
567         $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd;
568
569         $d = $lastd;
570         $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;
571         for ($a = 13 ; --$a > 0;) {
572             $lastd = $d;
573             $d += $mtab[$a] * $_day_power;
574             if ($d >= 0) {
575                 $month = $a;
576                 $ndays = $mtab[$a];
577                 break;
578             }
579         }
580
581         $d = $lastd;
582         $day = $ndays + ceil(($d+1) / ($_day_power));
583
584         $d += ($ndays - $day+1)* $_day_power;
585         $hour = floor($d/$_hour_power);
586
587     } else {
588
589         for ($a = 1970 ;; $a++) {
590             $lastd = $d;
591
592             if ($leaf = _adodb_is_leap_year($a)) {
593                 $d -= $_day_power * 366;
594             } else
595                 $d -= $_day_power * 365;
596             if ($d < 0) {
597                 $year = $a;
598                 break;
599             }
600         }
601         $secsInYear = $lastd;
602         $d = $lastd;
603         $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal;
604         for ($a = 1 ; $a <= 12; $a++) {
605             $lastd = $d;
606             $d -= $mtab[$a] * $_day_power;
607             if ($d < 0) {
608                 $month = $a;
609                 $ndays = $mtab[$a];
610                 break;
611             }
612         }
613         $d = $lastd;
614         $day = ceil(($d+1) / $_day_power);
615         $d = $d - ($day-1) * $_day_power;
616         $hour = floor($d /$_hour_power);
617     }
618
619     $d -= $hour * $_hour_power;
620     $min = floor($d/$_min_power);
621     $secs = $d - $min * $_min_power;
622     if ($fast) {
623         return array(
624         'seconds' => $secs,
625         'minutes' => $min,
626         'hours' => $hour,
627         'mday' => $day,
628         'mon' => $month,
629         'year' => $year,
630         'yday' => floor($secsInYear/$_day_power),
631         'leap' => $leaf,
632         'ndays' => $ndays
633         );
634     }
635
636
637     $dow = adodb_dow($year,$month,$day);
638
639     return array(
640         'seconds' => $secs,
641         'minutes' => $min,
642         'hours' => $hour,
643         'mday' => $day,
644         'wday' => $dow,
645         'mon' => $month,
646         'year' => $year,
647         'yday' => floor($secsInYear/$_day_power),
648         'weekday' => gmdate('l',$_day_power*(3+$dow)),
649         'month' => gmdate('F',mktime(0,0,0,$month,2,1971)),
650         0 => $origd
651     );
652 }
653
654 function adodb_gmdate($fmt,$d=false)
655 {
656     return adodb_date($fmt,$d,true);
657 }
658
659 function adodb_date2($fmt, $d=false, $is_gmt=false)
660 {
661     if ($d !== false) {
662         if (!preg_match(
663             "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
664             ($d), $rr)) return adodb_date($fmt,false,$is_gmt);
665
666         if ($rr[1] <= 100 && $rr[2]<= 1) return adodb_date($fmt,false,$is_gmt);
667
668         // h-m-s-MM-DD-YY
669         if (!isset($rr[5])) $d = adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
670         else $d = @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
671     }
672
673     return adodb_date($fmt,$d,$is_gmt);
674 }
675
676 /**
677     Return formatted date based on timestamp $d
678 */
679 function adodb_date($fmt,$d=false,$is_gmt=false)
680 {
681     if ($d === false) return date($fmt);
682     if (!defined('ADODB_TEST_DATES')) {
683         if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
684             if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer
685                 return @date($fmt,$d);
686         }
687     }
688     $_day_power = 86400;
689
690     $arr = _adodb_getdate($d,true,$is_gmt);
691     $year = $arr['year'];
692     $month = $arr['mon'];
693     $day = $arr['mday'];
694     $hour = $arr['hours'];
695     $min = $arr['minutes'];
696     $secs = $arr['seconds'];
697
698     $max = strlen($fmt);
699     $dates = '';
700
701     /*
702         at this point, we have the following integer vars to manipulate:
703         $year, $month, $day, $hour, $min, $secs
704     */
705     for ($i=0; $i < $max; $i++) {
706         switch($fmt[$i]) {
707         case 'T': $dates .= date('T');break;
708         // YEAR
709         case 'L': $dates .= $arr['leap'] ? '1' : '0'; break;
710         case 'r': // Thu, 21 Dec 2000 16:01:07 +0200
711
712             $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))).', '
713                 . ($day<10?' '.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' ';
714
715             if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour;
716
717             if ($min < 10) $dates .= ':0'.$min; else $dates .= ':'.$min;
718
719             if ($secs < 10) $dates .= ':0'.$secs; else $dates .= ':'.$secs;
720
721             $gmt = adodb_get_gmt_diff();
722             $dates .= sprintf(' %s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break;
723
724         case 'Y': $dates .= $year; break;
725         case 'y': $dates .= substr($year,strlen($year)-2,2); break;
726         // MONTH
727         case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break;
728         case 'Q': $dates .= ($month+3)>>2; break;
729         case 'n': $dates .= $month; break;
730         case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break;
731         case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break;
732         // DAY
733         case 't': $dates .= $arr['ndays']; break;
734         case 'z': $dates .= $arr['yday']; break;
735         case 'w': $dates .= adodb_dow($year,$month,$day); break;
736         case 'l': $dates .= gmdate('l',$_day_power*(3+adodb_dow($year,$month,$day))); break;
737         case 'D': $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))); break;
738         case 'j': $dates .= $day; break;
739         case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break;
740         case 'S':
741             $d10 = $day % 10;
742             if ($d10 == 1) $dates .= 'st';
743             else if ($d10 == 2) $dates .= 'nd';
744             else if ($d10 == 3) $dates .= 'rd';
745             else $dates .= 'th';
746             break;
747
748         // HOUR
749         case 'Z':
750             $dates .= ($is_gmt) ? 0 : -adodb_get_gmt_diff(); break;
751         case 'O':
752             $gmt = ($is_gmt) ? 0 : adodb_get_gmt_diff();
753             $dates .= sprintf('%s%04d',($gmt<0)?'+':'-',abs($gmt)/36); break;
754
755         case 'H':
756             if ($hour < 10) $dates .= '0'.$hour;
757             else $dates .= $hour;
758             break;
759         case 'h':
760             if ($hour > 12) $hh = $hour - 12;
761             else {
762                 if ($hour == 0) $hh = '12';
763                 else $hh = $hour;
764             }
765
766             if ($hh < 10) $dates .= '0'.$hh;
767             else $dates .= $hh;
768             break;
769
770         case 'G':
771             $dates .= $hour;
772             break;
773
774         case 'g':
775             if ($hour > 12) $hh = $hour - 12;
776             else {
777                 if ($hour == 0) $hh = '12';
778                 else $hh = $hour;
779             }
780             $dates .= $hh;
781             break;
782         // MINUTES
783         case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break;
784         // SECONDS
785         case 'U': $dates .= $d; break;
786         case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break;
787         // AM/PM
788         // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM
789         case 'a':
790             if ($hour>=12) $dates .= 'pm';
791             else $dates .= 'am';
792             break;
793         case 'A':
794             if ($hour>=12) $dates .= 'PM';
795             else $dates .= 'AM';
796             break;
797         default:
798             $dates .= $fmt[$i]; break;
799         // ESCAPE
800         case "\\":
801             $i++;
802             if ($i < $max) $dates .= $fmt[$i];
803             break;
804         }
805     }
806     return $dates;
807 }
808
809 /**
810     Returns a timestamp given a GMT/UTC time.
811     Note that $is_dst is not implemented and is ignored.
812 */
813 function adodb_gmmktime($hr,$min,$sec,$mon,$day,$year,$is_dst=false)
814 {
815     return adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst,true);
816 }
817
818 /**
819     Return a timestamp given a local time. Originally by jackbbs.
820     Note that $is_dst is not implemented and is ignored.
821 */
822 function adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst=false,$is_gmt=false)
823 {
824     if (!defined('ADODB_TEST_DATES')) {
825         // for windows, we don't check 1970 because with timezone differences,
826         // 1 Jan 1970 could generate negative timestamp, which is illegal
827         if (!defined('ADODB_NO_NEGATIVE_TS') || ($year >= 1971))
828             if (1901 < $year && $year < 2038)
829                 return @mktime($hr,$min,$sec,$mon,$day,$year);
830     }
831
832     $gmt_different = ($is_gmt) ? 0 : adodb_get_gmt_diff();
833
834     $hr = intval($hr);
835     $min = intval($min);
836     $sec = intval($sec);
837     $mon = intval($mon);
838     $day = intval($day);
839     $year = intval($year);
840
841     $year = adodb_year_digit_check($year);
842
843     if ($mon > 12) {
844         $y = floor($mon / 12);
845         $year += $y;
846         $mon -= $y*12;
847     }
848
849     $_day_power = 86400;
850     $_hour_power = 3600;
851     $_min_power = 60;
852
853     $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31);
854     $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31);
855
856     $_total_date = 0;
857     if ($year >= 1970) {
858         for ($a = 1970 ; $a <= $year; $a++) {
859             $leaf = _adodb_is_leap_year($a);
860             if ($leaf == true) {
861                 $loop_table = $_month_table_leaf;
862                 $_add_date = 366;
863             } else {
864                 $loop_table = $_month_table_normal;
865                 $_add_date = 365;
866             }
867             if ($a < $year) {
868                 $_total_date += $_add_date;
869             } else {
870                 for($b=1;$b<$mon;$b++) {
871                     $_total_date += $loop_table[$b];
872                 }
873             }
874         }
875         $_total_date +=$day-1;
876         $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different;
877
878     } else {
879         for ($a = 1969 ; $a >= $year; $a--) {
880             $leaf = _adodb_is_leap_year($a);
881             if ($leaf == true) {
882                 $loop_table = $_month_table_leaf;
883                 $_add_date = 366;
884             } else {
885                 $loop_table = $_month_table_normal;
886                 $_add_date = 365;
887             }
888             if ($a > $year) { $_total_date += $_add_date;
889             } else {
890                 for($b=12;$b>$mon;$b--) {
891                     $_total_date += $loop_table[$b];
892                 }
893             }
894         }
895         $_total_date += $loop_table[$mon] - $day;
896
897         $_day_time = $hr * $_hour_power + $min * $_min_power + $sec;
898         $_day_time = $_day_power - $_day_time;
899         $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different);
900         if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction
901         else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582.
902     }
903     //print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret;
904     return $ret;
905 }
906
907 ?>