Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

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