source: trunk/phpgwapi/inc/adodb/adodb-time.inc.php @ 2

Revision 2, 27.9 KB checked in by niltonneto, 17 years ago (diff)

Removida todas as tags usadas pelo CVS ($Id, $Source).
Primeira versão no CVS externo.

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