Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

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