Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401]

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