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