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