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