Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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