1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Derick Rethans <derick@derickrethans.nl>                    |
16   +----------------------------------------------------------------------+
17 */
18
19/* $Id$ */
20
21#include "php.h"
22#include "php_streams.h"
23#include "php_main.h"
24#include "php_globals.h"
25#include "php_ini.h"
26#include "ext/standard/info.h"
27#include "ext/standard/php_versioning.h"
28#include "ext/standard/php_math.h"
29#include "php_date.h"
30#include "zend_interfaces.h"
31#include "lib/timelib.h"
32#include <time.h>
33
34#ifdef PHP_WIN32
35static __inline __int64 php_date_llabs( __int64 i ) { return i >= 0? i: -i; }
36#elif defined(__GNUC__) && __GNUC__ < 3
37static __inline __int64_t php_date_llabs( __int64_t i ) { return i >= 0 ? i : -i; }
38#else
39static inline long long php_date_llabs( long long i ) { return i >= 0 ? i : -i; }
40#endif
41
42#ifdef PHP_WIN32
43#define DATE_I64_BUF_LEN 65
44# define DATE_I64A(i, s, len) _i64toa_s(i, s, len, 10)
45# define DATE_A64I(i, s) i = _atoi64(s)
46#else
47#define DATE_I64_BUF_LEN 65
48# define DATE_I64A(i, s, len) \
49    do { \
50        int st = snprintf(s, len, "%lld", i); \
51        s[st] = '\0'; \
52    } while (0);
53#ifdef HAVE_ATOLL
54# define DATE_A64I(i, s) i = atoll(s)
55#else
56# define DATE_A64I(i, s) i = strtoll(s, NULL, 10)
57#endif
58#endif
59
60/* {{{ arginfo */
61ZEND_BEGIN_ARG_INFO_EX(arginfo_date, 0, 0, 1)
62    ZEND_ARG_INFO(0, format)
63    ZEND_ARG_INFO(0, timestamp)
64ZEND_END_ARG_INFO()
65
66ZEND_BEGIN_ARG_INFO_EX(arginfo_gmdate, 0, 0, 1)
67    ZEND_ARG_INFO(0, format)
68    ZEND_ARG_INFO(0, timestamp)
69ZEND_END_ARG_INFO()
70
71ZEND_BEGIN_ARG_INFO_EX(arginfo_idate, 0, 0, 1)
72    ZEND_ARG_INFO(0, format)
73    ZEND_ARG_INFO(0, timestamp)
74ZEND_END_ARG_INFO()
75
76ZEND_BEGIN_ARG_INFO_EX(arginfo_strtotime, 0, 0, 1)
77    ZEND_ARG_INFO(0, time)
78    ZEND_ARG_INFO(0, now)
79ZEND_END_ARG_INFO()
80
81ZEND_BEGIN_ARG_INFO_EX(arginfo_mktime, 0, 0, 0)
82    ZEND_ARG_INFO(0, hour)
83    ZEND_ARG_INFO(0, min)
84    ZEND_ARG_INFO(0, sec)
85    ZEND_ARG_INFO(0, mon)
86    ZEND_ARG_INFO(0, day)
87    ZEND_ARG_INFO(0, year)
88ZEND_END_ARG_INFO()
89
90ZEND_BEGIN_ARG_INFO_EX(arginfo_gmmktime, 0, 0, 0)
91    ZEND_ARG_INFO(0, hour)
92    ZEND_ARG_INFO(0, min)
93    ZEND_ARG_INFO(0, sec)
94    ZEND_ARG_INFO(0, mon)
95    ZEND_ARG_INFO(0, day)
96    ZEND_ARG_INFO(0, year)
97ZEND_END_ARG_INFO()
98
99ZEND_BEGIN_ARG_INFO(arginfo_checkdate, 0)
100    ZEND_ARG_INFO(0, month)
101    ZEND_ARG_INFO(0, day)
102    ZEND_ARG_INFO(0, year)
103ZEND_END_ARG_INFO()
104
105ZEND_BEGIN_ARG_INFO_EX(arginfo_strftime, 0, 0, 1)
106    ZEND_ARG_INFO(0, format)
107    ZEND_ARG_INFO(0, timestamp)
108ZEND_END_ARG_INFO()
109
110ZEND_BEGIN_ARG_INFO_EX(arginfo_gmstrftime, 0, 0, 1)
111    ZEND_ARG_INFO(0, format)
112    ZEND_ARG_INFO(0, timestamp)
113ZEND_END_ARG_INFO()
114
115ZEND_BEGIN_ARG_INFO(arginfo_time, 0)
116ZEND_END_ARG_INFO()
117
118ZEND_BEGIN_ARG_INFO_EX(arginfo_localtime, 0, 0, 0)
119    ZEND_ARG_INFO(0, timestamp)
120    ZEND_ARG_INFO(0, associative_array)
121ZEND_END_ARG_INFO()
122
123ZEND_BEGIN_ARG_INFO_EX(arginfo_getdate, 0, 0, 0)
124    ZEND_ARG_INFO(0, timestamp)
125ZEND_END_ARG_INFO()
126
127ZEND_BEGIN_ARG_INFO(arginfo_date_default_timezone_set, 0)
128    ZEND_ARG_INFO(0, timezone_identifier)
129ZEND_END_ARG_INFO()
130
131ZEND_BEGIN_ARG_INFO(arginfo_date_default_timezone_get, 0)
132ZEND_END_ARG_INFO()
133
134ZEND_BEGIN_ARG_INFO_EX(arginfo_date_sunrise, 0, 0, 1)
135    ZEND_ARG_INFO(0, time)
136    ZEND_ARG_INFO(0, format)
137    ZEND_ARG_INFO(0, latitude)
138    ZEND_ARG_INFO(0, longitude)
139    ZEND_ARG_INFO(0, zenith)
140    ZEND_ARG_INFO(0, gmt_offset)
141ZEND_END_ARG_INFO()
142
143ZEND_BEGIN_ARG_INFO_EX(arginfo_date_sunset, 0, 0, 1)
144    ZEND_ARG_INFO(0, time)
145    ZEND_ARG_INFO(0, format)
146    ZEND_ARG_INFO(0, latitude)
147    ZEND_ARG_INFO(0, longitude)
148    ZEND_ARG_INFO(0, zenith)
149    ZEND_ARG_INFO(0, gmt_offset)
150ZEND_END_ARG_INFO()
151
152ZEND_BEGIN_ARG_INFO(arginfo_date_sun_info, 0)
153    ZEND_ARG_INFO(0, time)
154    ZEND_ARG_INFO(0, latitude)
155    ZEND_ARG_INFO(0, longitude)
156ZEND_END_ARG_INFO()
157
158ZEND_BEGIN_ARG_INFO_EX(arginfo_date_create, 0, 0, 0)
159    ZEND_ARG_INFO(0, time)
160    ZEND_ARG_INFO(0, object)
161ZEND_END_ARG_INFO()
162
163ZEND_BEGIN_ARG_INFO_EX(arginfo_date_create_from_format, 0, 0, 2)
164    ZEND_ARG_INFO(0, format)
165    ZEND_ARG_INFO(0, time)
166    ZEND_ARG_INFO(0, object)
167ZEND_END_ARG_INFO()
168
169ZEND_BEGIN_ARG_INFO_EX(arginfo_date_parse, 0, 0, 1)
170    ZEND_ARG_INFO(0, date)
171ZEND_END_ARG_INFO()
172
173ZEND_BEGIN_ARG_INFO_EX(arginfo_date_parse_from_format, 0, 0, 2)
174    ZEND_ARG_INFO(0, format)
175    ZEND_ARG_INFO(0, date)
176ZEND_END_ARG_INFO()
177
178ZEND_BEGIN_ARG_INFO(arginfo_date_get_last_errors, 0)
179ZEND_END_ARG_INFO()
180
181ZEND_BEGIN_ARG_INFO_EX(arginfo_date_format, 0, 0, 2)
182    ZEND_ARG_INFO(0, object)
183    ZEND_ARG_INFO(0, format)
184ZEND_END_ARG_INFO()
185
186ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_format, 0, 0, 1)
187    ZEND_ARG_INFO(0, format)
188ZEND_END_ARG_INFO()
189
190ZEND_BEGIN_ARG_INFO_EX(arginfo_date_modify, 0, 0, 2)
191    ZEND_ARG_INFO(0, object)
192    ZEND_ARG_INFO(0, modify)
193ZEND_END_ARG_INFO()
194
195ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_modify, 0, 0, 1)
196    ZEND_ARG_INFO(0, modify)
197ZEND_END_ARG_INFO()
198
199ZEND_BEGIN_ARG_INFO_EX(arginfo_date_add, 0, 0, 2)
200    ZEND_ARG_INFO(0, object)
201    ZEND_ARG_INFO(0, interval)
202ZEND_END_ARG_INFO()
203
204ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_add, 0, 0, 1)
205    ZEND_ARG_INFO(0, interval)
206ZEND_END_ARG_INFO()
207
208ZEND_BEGIN_ARG_INFO_EX(arginfo_date_sub, 0, 0, 2)
209    ZEND_ARG_INFO(0, object)
210    ZEND_ARG_INFO(0, interval)
211ZEND_END_ARG_INFO()
212
213ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_sub, 0, 0, 1)
214    ZEND_ARG_INFO(0, interval)
215ZEND_END_ARG_INFO()
216
217ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timezone_get, 0, 0, 1)
218    ZEND_ARG_INFO(0, object)
219ZEND_END_ARG_INFO()
220
221ZEND_BEGIN_ARG_INFO(arginfo_date_method_timezone_get, 0)
222ZEND_END_ARG_INFO()
223
224ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timezone_set, 0, 0, 2)
225    ZEND_ARG_INFO(0, object)
226    ZEND_ARG_INFO(0, timezone)
227ZEND_END_ARG_INFO()
228
229ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_timezone_set, 0, 0, 1)
230    ZEND_ARG_INFO(0, timezone)
231ZEND_END_ARG_INFO()
232
233ZEND_BEGIN_ARG_INFO_EX(arginfo_date_offset_get, 0, 0, 1)
234    ZEND_ARG_INFO(0, object)
235ZEND_END_ARG_INFO()
236
237ZEND_BEGIN_ARG_INFO(arginfo_date_method_offset_get, 0)
238ZEND_END_ARG_INFO()
239
240ZEND_BEGIN_ARG_INFO_EX(arginfo_date_diff, 0, 0, 2)
241    ZEND_ARG_INFO(0, object)
242    ZEND_ARG_INFO(0, object2)
243    ZEND_ARG_INFO(0, absolute)
244ZEND_END_ARG_INFO()
245
246ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_diff, 0, 0, 1)
247    ZEND_ARG_INFO(0, object)
248    ZEND_ARG_INFO(0, absolute)
249ZEND_END_ARG_INFO()
250
251ZEND_BEGIN_ARG_INFO_EX(arginfo_date_time_set, 0, 0, 3)
252    ZEND_ARG_INFO(0, object)
253    ZEND_ARG_INFO(0, hour)
254    ZEND_ARG_INFO(0, minute)
255    ZEND_ARG_INFO(0, second)
256ZEND_END_ARG_INFO()
257
258ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_time_set, 0, 0, 2)
259    ZEND_ARG_INFO(0, hour)
260    ZEND_ARG_INFO(0, minute)
261    ZEND_ARG_INFO(0, second)
262ZEND_END_ARG_INFO()
263
264ZEND_BEGIN_ARG_INFO_EX(arginfo_date_date_set, 0, 0, 4)
265    ZEND_ARG_INFO(0, object)
266    ZEND_ARG_INFO(0, year)
267    ZEND_ARG_INFO(0, month)
268    ZEND_ARG_INFO(0, day)
269ZEND_END_ARG_INFO()
270
271ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_date_set, 0, 0, 3)
272    ZEND_ARG_INFO(0, year)
273    ZEND_ARG_INFO(0, month)
274    ZEND_ARG_INFO(0, day)
275ZEND_END_ARG_INFO()
276
277ZEND_BEGIN_ARG_INFO_EX(arginfo_date_isodate_set, 0, 0, 3)
278    ZEND_ARG_INFO(0, object)
279    ZEND_ARG_INFO(0, year)
280    ZEND_ARG_INFO(0, week)
281    ZEND_ARG_INFO(0, day)
282ZEND_END_ARG_INFO()
283
284ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_isodate_set, 0, 0, 2)
285    ZEND_ARG_INFO(0, year)
286    ZEND_ARG_INFO(0, week)
287    ZEND_ARG_INFO(0, day)
288ZEND_END_ARG_INFO()
289
290ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timestamp_set, 0, 0, 2)
291    ZEND_ARG_INFO(0, object)
292    ZEND_ARG_INFO(0, unixtimestamp)
293ZEND_END_ARG_INFO()
294
295ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_timestamp_set, 0, 0, 1)
296    ZEND_ARG_INFO(0, unixtimestamp)
297ZEND_END_ARG_INFO()
298
299ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timestamp_get, 0, 0, 1)
300    ZEND_ARG_INFO(0, object)
301ZEND_END_ARG_INFO()
302
303ZEND_BEGIN_ARG_INFO(arginfo_date_method_timestamp_get, 0)
304ZEND_END_ARG_INFO()
305
306ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_open, 0, 0, 1)
307    ZEND_ARG_INFO(0, timezone)
308ZEND_END_ARG_INFO()
309
310ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_name_get, 0, 0, 1)
311    ZEND_ARG_INFO(0, object)
312ZEND_END_ARG_INFO()
313
314ZEND_BEGIN_ARG_INFO(arginfo_timezone_method_name_get, 0)
315ZEND_END_ARG_INFO()
316
317ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_name_from_abbr, 0, 0, 1)
318    ZEND_ARG_INFO(0, abbr)
319    ZEND_ARG_INFO(0, gmtoffset)
320    ZEND_ARG_INFO(0, isdst)
321ZEND_END_ARG_INFO()
322
323ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_offset_get, 0, 0, 2)
324    ZEND_ARG_INFO(0, object)
325    ZEND_ARG_INFO(0, datetime)
326ZEND_END_ARG_INFO()
327
328ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_method_offset_get, 0, 0, 1)
329    ZEND_ARG_INFO(0, datetime)
330ZEND_END_ARG_INFO()
331
332ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_transitions_get, 0, 0, 1)
333    ZEND_ARG_INFO(0, object)
334    ZEND_ARG_INFO(0, timestamp_begin)
335    ZEND_ARG_INFO(0, timestamp_end)
336ZEND_END_ARG_INFO()
337
338ZEND_BEGIN_ARG_INFO(arginfo_timezone_method_transitions_get, 0)
339    ZEND_ARG_INFO(0, timestamp_begin)
340    ZEND_ARG_INFO(0, timestamp_end)
341ZEND_END_ARG_INFO()
342
343ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_location_get, 0, 0, 1)
344    ZEND_ARG_INFO(0, object)
345ZEND_END_ARG_INFO()
346
347ZEND_BEGIN_ARG_INFO(arginfo_timezone_method_location_get, 0)
348ZEND_END_ARG_INFO()
349
350ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_identifiers_list, 0, 0, 0)
351    ZEND_ARG_INFO(0, what)
352    ZEND_ARG_INFO(0, country)
353ZEND_END_ARG_INFO()
354
355ZEND_BEGIN_ARG_INFO(arginfo_timezone_abbreviations_list, 0)
356ZEND_END_ARG_INFO()
357
358ZEND_BEGIN_ARG_INFO(arginfo_timezone_version_get, 0)
359ZEND_END_ARG_INFO()
360
361ZEND_BEGIN_ARG_INFO_EX(arginfo_date_interval_create_from_date_string, 0, 0, 1)
362    ZEND_ARG_INFO(0, time)
363ZEND_END_ARG_INFO()
364
365ZEND_BEGIN_ARG_INFO_EX(arginfo_date_interval_format, 0, 0, 2)
366    ZEND_ARG_INFO(0, object)
367    ZEND_ARG_INFO(0, format)
368ZEND_END_ARG_INFO()
369
370ZEND_BEGIN_ARG_INFO(arginfo_date_method_interval_format, 0)
371    ZEND_ARG_INFO(0, format)
372ZEND_END_ARG_INFO()
373
374ZEND_BEGIN_ARG_INFO_EX(arginfo_date_period_construct, 0, 0, 3)
375    ZEND_ARG_INFO(0, start)
376    ZEND_ARG_INFO(0, interval)
377    ZEND_ARG_INFO(0, end)
378ZEND_END_ARG_INFO()
379
380ZEND_BEGIN_ARG_INFO_EX(arginfo_date_interval_construct, 0, 0, 0)
381    ZEND_ARG_INFO(0, interval_spec)
382ZEND_END_ARG_INFO()
383/* }}} */
384
385/* {{{ Function table */
386const zend_function_entry date_functions[] = {
387    PHP_FE(strtotime, arginfo_strtotime)
388    PHP_FE(date, arginfo_date)
389    PHP_FE(idate, arginfo_idate)
390    PHP_FE(gmdate, arginfo_gmdate)
391    PHP_FE(mktime, arginfo_mktime)
392    PHP_FE(gmmktime, arginfo_gmmktime)
393    PHP_FE(checkdate, arginfo_checkdate)
394
395#ifdef HAVE_STRFTIME
396    PHP_FE(strftime, arginfo_strftime)
397    PHP_FE(gmstrftime, arginfo_gmstrftime)
398#endif
399
400    PHP_FE(time, arginfo_time)
401    PHP_FE(localtime, arginfo_localtime)
402    PHP_FE(getdate, arginfo_getdate)
403
404    /* Advanced Interface */
405    PHP_FE(date_create, arginfo_date_create)
406    PHP_FE(date_create_immutable, arginfo_date_create)
407    PHP_FE(date_create_from_format, arginfo_date_create_from_format)
408    PHP_FE(date_create_immutable_from_format, arginfo_date_create_from_format)
409    PHP_FE(date_parse, arginfo_date_parse)
410    PHP_FE(date_parse_from_format, arginfo_date_parse_from_format)
411    PHP_FE(date_get_last_errors, arginfo_date_get_last_errors)
412    PHP_FE(date_format, arginfo_date_format)
413    PHP_FE(date_modify, arginfo_date_modify)
414    PHP_FE(date_add, arginfo_date_add)
415    PHP_FE(date_sub, arginfo_date_sub)
416    PHP_FE(date_timezone_get, arginfo_date_timezone_get)
417    PHP_FE(date_timezone_set, arginfo_date_timezone_set)
418    PHP_FE(date_offset_get, arginfo_date_offset_get)
419    PHP_FE(date_diff, arginfo_date_diff)
420
421    PHP_FE(date_time_set, arginfo_date_time_set)
422    PHP_FE(date_date_set, arginfo_date_date_set)
423    PHP_FE(date_isodate_set, arginfo_date_isodate_set)
424    PHP_FE(date_timestamp_set, arginfo_date_timestamp_set)
425    PHP_FE(date_timestamp_get, arginfo_date_timestamp_get)
426
427    PHP_FE(timezone_open, arginfo_timezone_open)
428    PHP_FE(timezone_name_get, arginfo_timezone_name_get)
429    PHP_FE(timezone_name_from_abbr, arginfo_timezone_name_from_abbr)
430    PHP_FE(timezone_offset_get, arginfo_timezone_offset_get)
431    PHP_FE(timezone_transitions_get, arginfo_timezone_transitions_get)
432    PHP_FE(timezone_location_get, arginfo_timezone_location_get)
433    PHP_FE(timezone_identifiers_list, arginfo_timezone_identifiers_list)
434    PHP_FE(timezone_abbreviations_list, arginfo_timezone_abbreviations_list)
435    PHP_FE(timezone_version_get, arginfo_timezone_version_get)
436
437    PHP_FE(date_interval_create_from_date_string, arginfo_date_interval_create_from_date_string)
438    PHP_FE(date_interval_format, arginfo_date_interval_format)
439
440    /* Options and Configuration */
441    PHP_FE(date_default_timezone_set, arginfo_date_default_timezone_set)
442    PHP_FE(date_default_timezone_get, arginfo_date_default_timezone_get)
443
444    /* Astronomical functions */
445    PHP_FE(date_sunrise, arginfo_date_sunrise)
446    PHP_FE(date_sunset, arginfo_date_sunset)
447    PHP_FE(date_sun_info, arginfo_date_sun_info)
448    PHP_FE_END
449};
450
451static const zend_function_entry date_funcs_interface[] = {
452    PHP_ABSTRACT_ME(DateTimeInterface, format, arginfo_date_method_format)
453    PHP_ABSTRACT_ME(DateTimeInterface, getTimezone, arginfo_date_method_timezone_get)
454    PHP_ABSTRACT_ME(DateTimeInterface, getOffset, arginfo_date_method_offset_get)
455    PHP_ABSTRACT_ME(DateTimeInterface, getTimestamp, arginfo_date_method_timestamp_get)
456    PHP_ABSTRACT_ME(DateTimeInterface, diff, arginfo_date_method_diff)
457    PHP_ABSTRACT_ME(DateTimeInterface, __wakeup, NULL)
458    PHP_FE_END
459};
460
461const zend_function_entry date_funcs_date[] = {
462    PHP_ME(DateTime,            __construct,        arginfo_date_create, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
463    PHP_ME(DateTime,            __wakeup,           NULL, ZEND_ACC_PUBLIC)
464    PHP_ME(DateTime,            __set_state,        NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
465    PHP_ME_MAPPING(createFromFormat, date_create_from_format,   arginfo_date_create_from_format, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
466    PHP_ME_MAPPING(getLastErrors, date_get_last_errors, arginfo_date_get_last_errors, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
467    PHP_ME_MAPPING(format,      date_format,        arginfo_date_method_format, 0)
468    PHP_ME_MAPPING(modify,      date_modify,        arginfo_date_method_modify, 0)
469    PHP_ME_MAPPING(add,         date_add,           arginfo_date_method_add, 0)
470    PHP_ME_MAPPING(sub,         date_sub,           arginfo_date_method_sub, 0)
471    PHP_ME_MAPPING(getTimezone, date_timezone_get,  arginfo_date_method_timezone_get, 0)
472    PHP_ME_MAPPING(setTimezone, date_timezone_set,  arginfo_date_method_timezone_set, 0)
473    PHP_ME_MAPPING(getOffset,   date_offset_get,    arginfo_date_method_offset_get, 0)
474    PHP_ME_MAPPING(setTime,     date_time_set,      arginfo_date_method_time_set, 0)
475    PHP_ME_MAPPING(setDate,     date_date_set,      arginfo_date_method_date_set, 0)
476    PHP_ME_MAPPING(setISODate,  date_isodate_set,   arginfo_date_method_isodate_set, 0)
477    PHP_ME_MAPPING(setTimestamp,    date_timestamp_set, arginfo_date_method_timestamp_set, 0)
478    PHP_ME_MAPPING(getTimestamp,    date_timestamp_get, arginfo_date_method_timestamp_get, 0)
479    PHP_ME_MAPPING(diff,            date_diff, arginfo_date_method_diff, 0)
480    PHP_FE_END
481};
482
483const zend_function_entry date_funcs_immutable[] = {
484    PHP_ME(DateTimeImmutable, __construct,   arginfo_date_create, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
485    PHP_ME(DateTime, __wakeup,       NULL, ZEND_ACC_PUBLIC)
486    PHP_ME(DateTimeImmutable, __set_state,   NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
487    PHP_ME_MAPPING(createFromFormat, date_create_immutable_from_format, arginfo_date_create_from_format, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
488    PHP_ME_MAPPING(getLastErrors,    date_get_last_errors,    arginfo_date_get_last_errors, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
489    PHP_ME_MAPPING(format,           date_format,             arginfo_date_method_format, 0)
490    PHP_ME_MAPPING(getTimezone, date_timezone_get,  arginfo_date_method_timezone_get, 0)
491    PHP_ME_MAPPING(getOffset,   date_offset_get,    arginfo_date_method_offset_get, 0)
492    PHP_ME_MAPPING(getTimestamp,    date_timestamp_get, arginfo_date_method_timestamp_get, 0)
493    PHP_ME_MAPPING(diff,            date_diff, arginfo_date_method_diff, 0)
494    PHP_ME(DateTimeImmutable, modify,        arginfo_date_method_modify, 0)
495    PHP_ME(DateTimeImmutable, add,           arginfo_date_method_add, 0)
496    PHP_ME(DateTimeImmutable, sub,           arginfo_date_method_sub, 0)
497    PHP_ME(DateTimeImmutable, setTimezone,   arginfo_date_method_timezone_set, 0)
498    PHP_ME(DateTimeImmutable, setTime,       arginfo_date_method_time_set, 0)
499    PHP_ME(DateTimeImmutable, setDate,       arginfo_date_method_date_set, 0)
500    PHP_ME(DateTimeImmutable, setISODate,    arginfo_date_method_isodate_set, 0)
501    PHP_ME(DateTimeImmutable, setTimestamp,  arginfo_date_method_timestamp_set, 0)
502    PHP_FE_END
503};
504
505const zend_function_entry date_funcs_timezone[] = {
506    PHP_ME(DateTimeZone,              __construct,                 arginfo_timezone_open, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
507    PHP_ME(DateTimeZone,              __wakeup,                    NULL, ZEND_ACC_PUBLIC)
508    PHP_ME(DateTimeZone,              __set_state,                 NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
509    PHP_ME_MAPPING(getName,           timezone_name_get,           arginfo_timezone_method_name_get, 0)
510    PHP_ME_MAPPING(getOffset,         timezone_offset_get,         arginfo_timezone_method_offset_get, 0)
511    PHP_ME_MAPPING(getTransitions,    timezone_transitions_get,    arginfo_timezone_method_transitions_get, 0)
512    PHP_ME_MAPPING(getLocation,       timezone_location_get,       arginfo_timezone_method_location_get, 0)
513    PHP_ME_MAPPING(listAbbreviations, timezone_abbreviations_list, arginfo_timezone_abbreviations_list, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
514    PHP_ME_MAPPING(listIdentifiers,   timezone_identifiers_list,   arginfo_timezone_identifiers_list, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
515    PHP_FE_END
516};
517
518const zend_function_entry date_funcs_interval[] = {
519    PHP_ME(DateInterval,              __construct,                 arginfo_date_interval_construct, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
520    PHP_ME(DateInterval,              __wakeup,                    NULL, ZEND_ACC_PUBLIC)
521    PHP_ME(DateInterval,              __set_state,                 NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
522    PHP_ME_MAPPING(format,            date_interval_format,        arginfo_date_method_interval_format, 0)
523    PHP_ME_MAPPING(createFromDateString, date_interval_create_from_date_string, arginfo_date_interval_create_from_date_string, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
524    PHP_FE_END
525};
526
527const zend_function_entry date_funcs_period[] = {
528    PHP_ME(DatePeriod,                __construct,                 arginfo_date_period_construct, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
529    PHP_ME(DatePeriod,                __wakeup,                    NULL, ZEND_ACC_PUBLIC)
530    PHP_ME(DatePeriod,                __set_state,                 NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
531    PHP_FE_END
532};
533
534static char* guess_timezone(const timelib_tzdb *tzdb TSRMLS_DC);
535static void date_register_classes(TSRMLS_D);
536/* }}} */
537
538ZEND_DECLARE_MODULE_GLOBALS(date)
539static PHP_GINIT_FUNCTION(date);
540
541/* True global */
542timelib_tzdb *php_date_global_timezone_db;
543int php_date_global_timezone_db_enabled;
544
545#define DATE_DEFAULT_LATITUDE "31.7667"
546#define DATE_DEFAULT_LONGITUDE "35.2333"
547
548/* on 90'35; common sunset declaration (start of sun body appear) */
549#define DATE_SUNSET_ZENITH "90.583333"
550
551/* on 90'35; common sunrise declaration (sun body disappeared) */
552#define DATE_SUNRISE_ZENITH "90.583333"
553
554static PHP_INI_MH(OnUpdate_date_timezone);
555
556/* {{{ INI Settings */
557PHP_INI_BEGIN()
558    STD_PHP_INI_ENTRY("date.timezone", "", PHP_INI_ALL, OnUpdate_date_timezone, default_timezone, zend_date_globals, date_globals)
559    PHP_INI_ENTRY("date.default_latitude",           DATE_DEFAULT_LATITUDE,        PHP_INI_ALL, NULL)
560    PHP_INI_ENTRY("date.default_longitude",          DATE_DEFAULT_LONGITUDE,       PHP_INI_ALL, NULL)
561    PHP_INI_ENTRY("date.sunset_zenith",              DATE_SUNSET_ZENITH,           PHP_INI_ALL, NULL)
562    PHP_INI_ENTRY("date.sunrise_zenith",             DATE_SUNRISE_ZENITH,          PHP_INI_ALL, NULL)
563PHP_INI_END()
564/* }}} */
565
566zend_class_entry *date_ce_date, *date_ce_timezone, *date_ce_interval, *date_ce_period;
567zend_class_entry *date_ce_immutable, *date_ce_interface;
568
569
570PHPAPI zend_class_entry *php_date_get_date_ce(void)
571{
572    return date_ce_date;
573}
574
575PHPAPI zend_class_entry *php_date_get_immutable_ce(void)
576{
577    return date_ce_immutable;
578}
579
580PHPAPI zend_class_entry *php_date_get_timezone_ce(void)
581{
582    return date_ce_timezone;
583}
584
585static zend_object_handlers date_object_handlers_date;
586static zend_object_handlers date_object_handlers_immutable;
587static zend_object_handlers date_object_handlers_timezone;
588static zend_object_handlers date_object_handlers_interval;
589static zend_object_handlers date_object_handlers_period;
590
591#define DATE_SET_CONTEXT \
592    zval *object; \
593    object = getThis(); \
594
595#define DATE_FETCH_OBJECT   \
596    php_date_obj *obj;  \
597    DATE_SET_CONTEXT; \
598    if (object) {   \
599        if (zend_parse_parameters_none() == FAILURE) {  \
600            return; \
601        }   \
602    } else {    \
603        if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, NULL, "O", &object, date_ce_date) == FAILURE) { \
604            RETURN_FALSE;   \
605        }   \
606    }   \
607    obj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);  \
608
609#define DATE_CHECK_INITIALIZED(member, class_name) \
610    if (!(member)) { \
611        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The " #class_name " object has not been correctly initialized by its constructor"); \
612        RETURN_FALSE; \
613    }
614
615static void date_object_free_storage_date(void *object TSRMLS_DC);
616static void date_object_free_storage_timezone(void *object TSRMLS_DC);
617static void date_object_free_storage_interval(void *object TSRMLS_DC);
618static void date_object_free_storage_period(void *object TSRMLS_DC);
619
620static zend_object_value date_object_new_date(zend_class_entry *class_type TSRMLS_DC);
621static zend_object_value date_object_new_timezone(zend_class_entry *class_type TSRMLS_DC);
622static zend_object_value date_object_new_interval(zend_class_entry *class_type TSRMLS_DC);
623static zend_object_value date_object_new_period(zend_class_entry *class_type TSRMLS_DC);
624
625static zend_object_value date_object_clone_date(zval *this_ptr TSRMLS_DC);
626static zend_object_value date_object_clone_timezone(zval *this_ptr TSRMLS_DC);
627static zend_object_value date_object_clone_interval(zval *this_ptr TSRMLS_DC);
628static zend_object_value date_object_clone_period(zval *this_ptr TSRMLS_DC);
629
630static int date_object_compare_date(zval *d1, zval *d2 TSRMLS_DC);
631static HashTable *date_object_get_gc(zval *object, zval ***table, int *n TSRMLS_DC);
632static HashTable *date_object_get_properties(zval *object TSRMLS_DC);
633static HashTable *date_object_get_gc_interval(zval *object, zval ***table, int *n TSRMLS_DC);
634static HashTable *date_object_get_properties_interval(zval *object TSRMLS_DC);
635static HashTable *date_object_get_gc_period(zval *object, zval ***table, int *n TSRMLS_DC);
636static HashTable *date_object_get_properties_period(zval *object TSRMLS_DC);
637static HashTable *date_object_get_properties_timezone(zval *object TSRMLS_DC);
638static HashTable *date_object_get_gc_timezone(zval *object, zval ***table, int *n TSRMLS_DC);
639
640zval *date_interval_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC);
641void date_interval_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC);
642static zval *date_period_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC);
643static void date_period_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC);
644
645/* {{{ Module struct */
646zend_module_entry date_module_entry = {
647    STANDARD_MODULE_HEADER_EX,
648    NULL,
649    NULL,
650    "date",                     /* extension name */
651    date_functions,             /* function list */
652    PHP_MINIT(date),            /* process startup */
653    PHP_MSHUTDOWN(date),        /* process shutdown */
654    PHP_RINIT(date),            /* request startup */
655    PHP_RSHUTDOWN(date),        /* request shutdown */
656    PHP_MINFO(date),            /* extension info */
657    PHP_VERSION,                /* extension version */
658    PHP_MODULE_GLOBALS(date),   /* globals descriptor */
659    PHP_GINIT(date),            /* globals ctor */
660    NULL,                       /* globals dtor */
661    NULL,                       /* post deactivate */
662    STANDARD_MODULE_PROPERTIES_EX
663};
664/* }}} */
665
666
667/* {{{ PHP_GINIT_FUNCTION */
668static PHP_GINIT_FUNCTION(date)
669{
670    date_globals->default_timezone = NULL;
671    date_globals->timezone = NULL;
672    date_globals->tzcache = NULL;
673    date_globals->timezone_valid = 0;
674}
675/* }}} */
676
677
678static void _php_date_tzinfo_dtor(void *tzinfo)
679{
680    timelib_tzinfo **tzi = (timelib_tzinfo **)tzinfo;
681
682    timelib_tzinfo_dtor(*tzi);
683}
684
685/* {{{ PHP_RINIT_FUNCTION */
686PHP_RINIT_FUNCTION(date)
687{
688    if (DATEG(timezone)) {
689        efree(DATEG(timezone));
690    }
691    DATEG(timezone) = NULL;
692    DATEG(tzcache) = NULL;
693    DATEG(last_errors) = NULL;
694
695    return SUCCESS;
696}
697/* }}} */
698
699/* {{{ PHP_RSHUTDOWN_FUNCTION */
700PHP_RSHUTDOWN_FUNCTION(date)
701{
702    if (DATEG(timezone)) {
703        efree(DATEG(timezone));
704    }
705    DATEG(timezone) = NULL;
706    if(DATEG(tzcache)) {
707        zend_hash_destroy(DATEG(tzcache));
708        FREE_HASHTABLE(DATEG(tzcache));
709        DATEG(tzcache) = NULL;
710    }
711    if (DATEG(last_errors)) {
712        timelib_error_container_dtor(DATEG(last_errors));
713        DATEG(last_errors) = NULL;
714    }
715
716    return SUCCESS;
717}
718/* }}} */
719
720#define DATE_TIMEZONEDB      php_date_global_timezone_db ? php_date_global_timezone_db : timelib_builtin_db()
721
722/*
723 * RFC822, Section 5.1: http://www.ietf.org/rfc/rfc822.txt
724 *  date-time   =  [ day "," ] date time        ; dd mm yy hh:mm:ss zzz
725 *  day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"  /  "Fri"  / "Sat" /  "Sun"
726 *  date        =  1*2DIGIT month 2DIGIT        ; day month year e.g. 20 Jun 82
727 *  month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"  /  "May"  /  "Jun" /  "Jul"  /  "Aug"  /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
728 *  time        =  hour zone                    ; ANSI and Military
729 *  hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT] ; 00:00:00 - 23:59:59
730 *  zone        =  "UT"  / "GMT"  /  "EST" / "EDT"  /  "CST" / "CDT"  /  "MST" / "MDT"  /  "PST" / "PDT"  /  1ALPHA  / ( ("+" / "-") 4DIGIT )
731 */
732#define DATE_FORMAT_RFC822   "D, d M y H:i:s O"
733
734/*
735 * RFC850, Section 2.1.4: http://www.ietf.org/rfc/rfc850.txt
736 *  Format must be acceptable both to the ARPANET and to the getdate routine.
737 *  One format that is acceptable to both is Weekday, DD-Mon-YY HH:MM:SS TIMEZONE
738 *  TIMEZONE can be any timezone name (3 or more letters)
739 */
740#define DATE_FORMAT_RFC850   "l, d-M-y H:i:s T"
741
742/*
743 * RFC1036, Section 2.1.2: http://www.ietf.org/rfc/rfc1036.txt
744 *  Its format must be acceptable both in RFC-822 and to the getdate(3)
745 *  Wdy, DD Mon YY HH:MM:SS TIMEZONE
746 *  There is no hope of having a complete list of timezones.  Universal
747 *  Time (GMT), the North American timezones (PST, PDT, MST, MDT, CST,
748 *  CDT, EST, EDT) and the +/-hhmm offset specifed in RFC-822 should be supported.
749 */
750#define DATE_FORMAT_RFC1036  "D, d M y H:i:s O"
751
752/*
753 * RFC1123, Section 5.2.14: http://www.ietf.org/rfc/rfc1123.txt
754 *  RFC-822 Date and Time Specification: RFC-822 Section 5
755 *  The syntax for the date is hereby changed to: date = 1*2DIGIT month 2*4DIGIT
756 */
757#define DATE_FORMAT_RFC1123  "D, d M Y H:i:s O"
758
759/*
760 * RFC2822, Section 3.3: http://www.ietf.org/rfc/rfc2822.txt
761 *  FWS             =       ([*WSP CRLF] 1*WSP) /   ; Folding white space
762 *  CFWS            =       *([FWS] comment) (([FWS] comment) / FWS)
763 *
764 *  date-time       =       [ day-of-week "," ] date FWS time [CFWS]
765 *  day-of-week     =       ([FWS] day-name)
766 *  day-name        =       "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
767 *  date            =       day month year
768 *  year            =       4*DIGIT
769 *  month           =       (FWS month-name FWS)
770 *  month-name      =       "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
771 *  day             =       ([FWS] 1*2DIGIT)
772 *  time            =       time-of-day FWS zone
773 *  time-of-day     =       hour ":" minute [ ":" second ]
774 *  hour            =       2DIGIT
775 *  minute          =       2DIGIT
776 *  second          =       2DIGIT
777 *  zone            =       (( "+" / "-" ) 4DIGIT)
778 */
779#define DATE_FORMAT_RFC2822  "D, d M Y H:i:s O"
780/*
781 * RFC3339, Section 5.6: http://www.ietf.org/rfc/rfc3339.txt
782 *  date-fullyear   = 4DIGIT
783 *  date-month      = 2DIGIT  ; 01-12
784 *  date-mday       = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on month/year
785 *
786 *  time-hour       = 2DIGIT  ; 00-23
787 *  time-minute     = 2DIGIT  ; 00-59
788 *  time-second     = 2DIGIT  ; 00-58, 00-59, 00-60 based on leap second rules
789 *
790 *  time-secfrac    = "." 1*DIGIT
791 *  time-numoffset  = ("+" / "-") time-hour ":" time-minute
792 *  time-offset     = "Z" / time-numoffset
793 *
794 *  partial-time    = time-hour ":" time-minute ":" time-second [time-secfrac]
795 *  full-date       = date-fullyear "-" date-month "-" date-mday
796 *  full-time       = partial-time time-offset
797 *
798 *  date-time       = full-date "T" full-time
799 */
800#define DATE_FORMAT_RFC3339  "Y-m-d\\TH:i:sP"
801
802#define DATE_FORMAT_ISO8601  "Y-m-d\\TH:i:sO"
803
804/*
805 * This comes from various sources that like to contradict. I'm going with the
806 * format here because of:
807 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa384321%28v=vs.85%29.aspx
808 * and http://curl.haxx.se/rfc/cookie_spec.html
809 */
810#define DATE_FORMAT_COOKIE   "l, d-M-Y H:i:s T"
811
812#define DATE_TZ_ERRMSG \
813    "It is not safe to rely on the system's timezone settings. You are " \
814    "*required* to use the date.timezone setting or the " \
815    "date_default_timezone_set() function. In case you used any of those " \
816    "methods and you are still getting this warning, you most likely " \
817    "misspelled the timezone identifier. "
818
819#define SUNFUNCS_RET_TIMESTAMP 0
820#define SUNFUNCS_RET_STRING    1
821#define SUNFUNCS_RET_DOUBLE    2
822
823/* {{{ PHP_MINIT_FUNCTION */
824PHP_MINIT_FUNCTION(date)
825{
826    REGISTER_INI_ENTRIES();
827    date_register_classes(TSRMLS_C);
828/*
829 * RFC4287, Section 3.3: http://www.ietf.org/rfc/rfc4287.txt
830 *   A Date construct is an element whose content MUST conform to the
831 *   "date-time" production in [RFC3339].  In addition, an uppercase "T"
832 *   character MUST be used to separate date and time, and an uppercase
833 *   "Z" character MUST be present in the absence of a numeric time zone offset.
834 */
835    REGISTER_STRING_CONSTANT("DATE_ATOM",    DATE_FORMAT_RFC3339, CONST_CS | CONST_PERSISTENT);
836/*
837 * Preliminary specification: http://wp.netscape.com/newsref/std/cookie_spec.html
838 *   "This is based on RFC 822, RFC 850,  RFC 1036, and  RFC 1123,
839 *   with the variations that the only legal time zone is GMT
840 *   and the separators between the elements of the date must be dashes."
841 */
842    REGISTER_STRING_CONSTANT("DATE_COOKIE",  DATE_FORMAT_COOKIE,  CONST_CS | CONST_PERSISTENT);
843    REGISTER_STRING_CONSTANT("DATE_ISO8601", DATE_FORMAT_ISO8601, CONST_CS | CONST_PERSISTENT);
844    REGISTER_STRING_CONSTANT("DATE_RFC822",  DATE_FORMAT_RFC822,  CONST_CS | CONST_PERSISTENT);
845    REGISTER_STRING_CONSTANT("DATE_RFC850",  DATE_FORMAT_RFC850,  CONST_CS | CONST_PERSISTENT);
846    REGISTER_STRING_CONSTANT("DATE_RFC1036", DATE_FORMAT_RFC1036, CONST_CS | CONST_PERSISTENT);
847    REGISTER_STRING_CONSTANT("DATE_RFC1123", DATE_FORMAT_RFC1123, CONST_CS | CONST_PERSISTENT);
848    REGISTER_STRING_CONSTANT("DATE_RFC2822", DATE_FORMAT_RFC2822, CONST_CS | CONST_PERSISTENT);
849    REGISTER_STRING_CONSTANT("DATE_RFC3339", DATE_FORMAT_RFC3339, CONST_CS | CONST_PERSISTENT);
850/*
851 * RSS 2.0 Specification: http://blogs.law.harvard.edu/tech/rss
852 *   "All date-times in RSS conform to the Date and Time Specification of RFC 822,
853 *   with the exception that the year may be expressed with two characters or four characters (four preferred)"
854 */
855    REGISTER_STRING_CONSTANT("DATE_RSS",     DATE_FORMAT_RFC1123, CONST_CS | CONST_PERSISTENT);
856    REGISTER_STRING_CONSTANT("DATE_W3C",     DATE_FORMAT_RFC3339, CONST_CS | CONST_PERSISTENT);
857
858    REGISTER_LONG_CONSTANT("SUNFUNCS_RET_TIMESTAMP", SUNFUNCS_RET_TIMESTAMP, CONST_CS | CONST_PERSISTENT);
859    REGISTER_LONG_CONSTANT("SUNFUNCS_RET_STRING", SUNFUNCS_RET_STRING, CONST_CS | CONST_PERSISTENT);
860    REGISTER_LONG_CONSTANT("SUNFUNCS_RET_DOUBLE", SUNFUNCS_RET_DOUBLE, CONST_CS | CONST_PERSISTENT);
861
862    php_date_global_timezone_db = NULL;
863    php_date_global_timezone_db_enabled = 0;
864    DATEG(last_errors) = NULL;
865    return SUCCESS;
866}
867/* }}} */
868
869/* {{{ PHP_MSHUTDOWN_FUNCTION */
870PHP_MSHUTDOWN_FUNCTION(date)
871{
872    UNREGISTER_INI_ENTRIES();
873
874    if (DATEG(last_errors)) {
875        timelib_error_container_dtor(DATEG(last_errors));
876    }
877
878    return SUCCESS;
879}
880/* }}} */
881
882/* {{{ PHP_MINFO_FUNCTION */
883PHP_MINFO_FUNCTION(date)
884{
885    const timelib_tzdb *tzdb = DATE_TIMEZONEDB;
886
887    php_info_print_table_start();
888    php_info_print_table_row(2, "date/time support", "enabled");
889    php_info_print_table_row(2, "\"Olson\" Timezone Database Version", tzdb->version);
890    php_info_print_table_row(2, "Timezone Database", php_date_global_timezone_db_enabled ? "external" : "internal");
891    php_info_print_table_row(2, "Default timezone", guess_timezone(tzdb TSRMLS_CC));
892    php_info_print_table_end();
893
894    DISPLAY_INI_ENTRIES();
895}
896/* }}} */
897
898/* {{{ Timezone Cache functions */
899static timelib_tzinfo *php_date_parse_tzfile(char *formal_tzname, const timelib_tzdb *tzdb TSRMLS_DC)
900{
901    timelib_tzinfo *tzi, **ptzi;
902
903    if(!DATEG(tzcache)) {
904        ALLOC_HASHTABLE(DATEG(tzcache));
905        zend_hash_init(DATEG(tzcache), 4, NULL, _php_date_tzinfo_dtor, 0);
906    }
907
908    if (zend_hash_find(DATEG(tzcache), formal_tzname, strlen(formal_tzname) + 1, (void **) &ptzi) == SUCCESS) {
909        return *ptzi;
910    }
911
912    tzi = timelib_parse_tzfile(formal_tzname, tzdb);
913    if (tzi) {
914        zend_hash_add(DATEG(tzcache), formal_tzname, strlen(formal_tzname) + 1, (void *) &tzi, sizeof(timelib_tzinfo*), NULL);
915    }
916    return tzi;
917}
918
919timelib_tzinfo *php_date_parse_tzfile_wrapper(char *formal_tzname, const timelib_tzdb *tzdb)
920{
921    TSRMLS_FETCH();
922    return php_date_parse_tzfile(formal_tzname, tzdb TSRMLS_CC);
923}
924/* }}} */
925
926/* Callback to check the date.timezone only when changed increases performance */
927/* {{{ static PHP_INI_MH(OnUpdate_date_timezone) */
928static PHP_INI_MH(OnUpdate_date_timezone)
929{
930    if (OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC) == FAILURE) {
931        return FAILURE;
932    }
933
934    DATEG(timezone_valid) = 0;
935    if (stage == PHP_INI_STAGE_RUNTIME) {
936        if (!timelib_timezone_id_is_valid(DATEG(default_timezone), DATE_TIMEZONEDB)) {
937            php_error_docref(NULL TSRMLS_CC, E_WARNING, DATE_TZ_ERRMSG);
938        } else {
939            DATEG(timezone_valid) = 1;
940        }
941    }
942
943    return SUCCESS;
944}
945/* }}} */
946
947/* {{{ Helper functions */
948static char* guess_timezone(const timelib_tzdb *tzdb TSRMLS_DC)
949{
950    /* Checking configure timezone */
951    if (DATEG(timezone) && (strlen(DATEG(timezone))) > 0) {
952        return DATEG(timezone);
953    }
954    /* Check config setting for default timezone */
955    if (!DATEG(default_timezone)) {
956        /* Special case: ext/date wasn't initialized yet */
957        zval ztz;
958
959        if (SUCCESS == zend_get_configuration_directive("date.timezone", sizeof("date.timezone"), &ztz)
960            && Z_TYPE(ztz) == IS_STRING && Z_STRLEN(ztz) > 0 && timelib_timezone_id_is_valid(Z_STRVAL(ztz), tzdb)) {
961            return Z_STRVAL(ztz);
962        }
963    } else if (*DATEG(default_timezone)) {
964        if (DATEG(timezone_valid) == 1) {
965            return DATEG(default_timezone);
966        }
967
968        if (!timelib_timezone_id_is_valid(DATEG(default_timezone), tzdb)) {
969            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid date.timezone value '%s', we selected the timezone 'UTC' for now.", DATEG(default_timezone));
970            return "UTC";
971        }
972
973        DATEG(timezone_valid) = 1;
974        return DATEG(default_timezone);
975    }
976    /* Fallback to UTC */
977    php_error_docref(NULL TSRMLS_CC, E_WARNING, DATE_TZ_ERRMSG "We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone.");
978    return "UTC";
979}
980
981PHPAPI timelib_tzinfo *get_timezone_info(TSRMLS_D)
982{
983    char *tz;
984    timelib_tzinfo *tzi;
985
986    tz = guess_timezone(DATE_TIMEZONEDB TSRMLS_CC);
987    tzi = php_date_parse_tzfile(tz, DATE_TIMEZONEDB TSRMLS_CC);
988    if (! tzi) {
989        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Timezone database is corrupt - this should *never* happen!");
990    }
991    return tzi;
992}
993/* }}} */
994
995
996/* {{{ date() and gmdate() data */
997#include "ext/standard/php_smart_str.h"
998
999static char *mon_full_names[] = {
1000    "January", "February", "March", "April",
1001    "May", "June", "July", "August",
1002    "September", "October", "November", "December"
1003};
1004
1005static char *mon_short_names[] = {
1006    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1007};
1008
1009static char *day_full_names[] = {
1010    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
1011};
1012
1013static char *day_short_names[] = {
1014    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1015};
1016
1017static char *english_suffix(timelib_sll number)
1018{
1019    if (number >= 10 && number <= 19) {
1020        return "th";
1021    } else {
1022        switch (number % 10) {
1023            case 1: return "st";
1024            case 2: return "nd";
1025            case 3: return "rd";
1026        }
1027    }
1028    return "th";
1029}
1030/* }}} */
1031
1032/* {{{ day of week helpers */
1033char *php_date_full_day_name(timelib_sll y, timelib_sll m, timelib_sll d)
1034{
1035    timelib_sll day_of_week = timelib_day_of_week(y, m, d);
1036    if (day_of_week < 0) {
1037        return "Unknown";
1038    }
1039    return day_full_names[day_of_week];
1040}
1041
1042char *php_date_short_day_name(timelib_sll y, timelib_sll m, timelib_sll d)
1043{
1044    timelib_sll day_of_week = timelib_day_of_week(y, m, d);
1045    if (day_of_week < 0) {
1046        return "Unknown";
1047    }
1048    return day_short_names[day_of_week];
1049}
1050/* }}} */
1051
1052/* {{{ date_format - (gm)date helper */
1053static char *date_format(char *format, int format_len, timelib_time *t, int localtime)
1054{
1055    smart_str            string = {0};
1056    int                  i, length = 0;
1057    char                 buffer[97];
1058    timelib_time_offset *offset = NULL;
1059    timelib_sll          isoweek, isoyear;
1060    int                  rfc_colon;
1061    int                  weekYearSet = 0;
1062
1063    if (!format_len) {
1064        return estrdup("");
1065    }
1066
1067    if (localtime) {
1068        if (t->zone_type == TIMELIB_ZONETYPE_ABBR) {
1069            offset = timelib_time_offset_ctor();
1070            offset->offset = (t->z - (t->dst * 60)) * -60;
1071            offset->leap_secs = 0;
1072            offset->is_dst = t->dst;
1073            offset->abbr = strdup(t->tz_abbr);
1074        } else if (t->zone_type == TIMELIB_ZONETYPE_OFFSET) {
1075            offset = timelib_time_offset_ctor();
1076            offset->offset = (t->z) * -60;
1077            offset->leap_secs = 0;
1078            offset->is_dst = 0;
1079            offset->abbr = malloc(9); /* GMT�xxxx\0 */
1080            snprintf(offset->abbr, 9, "GMT%c%02d%02d",
1081                                      localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
1082                                      localtime ? abs(offset->offset / 3600) : 0,
1083                                      localtime ? abs((offset->offset % 3600) / 60) : 0 );
1084        } else {
1085            offset = timelib_get_time_zone_info(t->sse, t->tz_info);
1086        }
1087    }
1088
1089    for (i = 0; i < format_len; i++) {
1090        rfc_colon = 0;
1091        switch (format[i]) {
1092            /* day */
1093            case 'd': length = slprintf(buffer, 32, "%02d", (int) t->d); break;
1094            case 'D': length = slprintf(buffer, 32, "%s", php_date_short_day_name(t->y, t->m, t->d)); break;
1095            case 'j': length = slprintf(buffer, 32, "%d", (int) t->d); break;
1096            case 'l': length = slprintf(buffer, 32, "%s", php_date_full_day_name(t->y, t->m, t->d)); break;
1097            case 'S': length = slprintf(buffer, 32, "%s", english_suffix(t->d)); break;
1098            case 'w': length = slprintf(buffer, 32, "%d", (int) timelib_day_of_week(t->y, t->m, t->d)); break;
1099            case 'N': length = slprintf(buffer, 32, "%d", (int) timelib_iso_day_of_week(t->y, t->m, t->d)); break;
1100            case 'z': length = slprintf(buffer, 32, "%d", (int) timelib_day_of_year(t->y, t->m, t->d)); break;
1101
1102            /* week */
1103            case 'W':
1104                if(!weekYearSet) { timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear); weekYearSet = 1; }
1105                length = slprintf(buffer, 32, "%02d", (int) isoweek); break; /* iso weeknr */
1106            case 'o':
1107                if(!weekYearSet) { timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear); weekYearSet = 1; }
1108                length = slprintf(buffer, 32, "%d", (int) isoyear); break; /* iso year */
1109
1110            /* month */
1111            case 'F': length = slprintf(buffer, 32, "%s", mon_full_names[t->m - 1]); break;
1112            case 'm': length = slprintf(buffer, 32, "%02d", (int) t->m); break;
1113            case 'M': length = slprintf(buffer, 32, "%s", mon_short_names[t->m - 1]); break;
1114            case 'n': length = slprintf(buffer, 32, "%d", (int) t->m); break;
1115            case 't': length = slprintf(buffer, 32, "%d", (int) timelib_days_in_month(t->y, t->m)); break;
1116
1117            /* year */
1118            case 'L': length = slprintf(buffer, 32, "%d", timelib_is_leap((int) t->y)); break;
1119            case 'y': length = slprintf(buffer, 32, "%02d", (int) t->y % 100); break;
1120            case 'Y': length = slprintf(buffer, 32, "%s%04lld", t->y < 0 ? "-" : "", php_date_llabs((timelib_sll) t->y)); break;
1121
1122            /* time */
1123            case 'a': length = slprintf(buffer, 32, "%s", t->h >= 12 ? "pm" : "am"); break;
1124            case 'A': length = slprintf(buffer, 32, "%s", t->h >= 12 ? "PM" : "AM"); break;
1125            case 'B': {
1126                int retval = (((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10) / 864);
1127                while (retval < 0) {
1128                    retval += 1000;
1129                }
1130                retval = retval % 1000;
1131                length = slprintf(buffer, 32, "%03d", retval);
1132                break;
1133            }
1134            case 'g': length = slprintf(buffer, 32, "%d", (t->h % 12) ? (int) t->h % 12 : 12); break;
1135            case 'G': length = slprintf(buffer, 32, "%d", (int) t->h); break;
1136            case 'h': length = slprintf(buffer, 32, "%02d", (t->h % 12) ? (int) t->h % 12 : 12); break;
1137            case 'H': length = slprintf(buffer, 32, "%02d", (int) t->h); break;
1138            case 'i': length = slprintf(buffer, 32, "%02d", (int) t->i); break;
1139            case 's': length = slprintf(buffer, 32, "%02d", (int) t->s); break;
1140            case 'u': length = slprintf(buffer, 32, "%06d", (int) floor(t->f * 1000000 + 0.5)); break;
1141
1142            /* timezone */
1143            case 'I': length = slprintf(buffer, 32, "%d", localtime ? offset->is_dst : 0); break;
1144            case 'P': rfc_colon = 1; /* break intentionally missing */
1145            case 'O': length = slprintf(buffer, 32, "%c%02d%s%02d",
1146                                            localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
1147                                            localtime ? abs(offset->offset / 3600) : 0,
1148                                            rfc_colon ? ":" : "",
1149                                            localtime ? abs((offset->offset % 3600) / 60) : 0
1150                              );
1151                      break;
1152            case 'T': length = slprintf(buffer, 32, "%s", localtime ? offset->abbr : "GMT"); break;
1153            case 'e': if (!localtime) {
1154                          length = slprintf(buffer, 32, "%s", "UTC");
1155                      } else {
1156                          switch (t->zone_type) {
1157                              case TIMELIB_ZONETYPE_ID:
1158                                  length = slprintf(buffer, 32, "%s", t->tz_info->name);
1159                                  break;
1160                              case TIMELIB_ZONETYPE_ABBR:
1161                                  length = slprintf(buffer, 32, "%s", offset->abbr);
1162                                  break;
1163                              case TIMELIB_ZONETYPE_OFFSET:
1164                                  length = slprintf(buffer, 32, "%c%02d:%02d",
1165                                                ((offset->offset < 0) ? '-' : '+'),
1166                                                abs(offset->offset / 3600),
1167                                                abs((offset->offset % 3600) / 60)
1168                                           );
1169                                  break;
1170                          }
1171                      }
1172                      break;
1173            case 'Z': length = slprintf(buffer, 32, "%d", localtime ? offset->offset : 0); break;
1174
1175            /* full date/time */
1176            case 'c': length = slprintf(buffer, 96, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
1177                                            (int) t->y, (int) t->m, (int) t->d,
1178                                            (int) t->h, (int) t->i, (int) t->s,
1179                                            localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
1180                                            localtime ? abs(offset->offset / 3600) : 0,
1181                                            localtime ? abs((offset->offset % 3600) / 60) : 0
1182                              );
1183                      break;
1184            case 'r': length = slprintf(buffer, 96, "%3s, %02d %3s %04d %02d:%02d:%02d %c%02d%02d",
1185                                            php_date_short_day_name(t->y, t->m, t->d),
1186                                            (int) t->d, mon_short_names[t->m - 1],
1187                                            (int) t->y, (int) t->h, (int) t->i, (int) t->s,
1188                                            localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
1189                                            localtime ? abs(offset->offset / 3600) : 0,
1190                                            localtime ? abs((offset->offset % 3600) / 60) : 0
1191                              );
1192                      break;
1193            case 'U': length = slprintf(buffer, 32, "%lld", (timelib_sll) t->sse); break;
1194
1195            case '\\': if (i < format_len) i++; /* break intentionally missing */
1196
1197            default: buffer[0] = format[i]; buffer[1] = '\0'; length = 1; break;
1198        }
1199        smart_str_appendl(&string, buffer, length);
1200    }
1201
1202    smart_str_0(&string);
1203
1204    if (localtime) {
1205        timelib_time_offset_dtor(offset);
1206    }
1207
1208    return string.c;
1209}
1210
1211static void php_date(INTERNAL_FUNCTION_PARAMETERS, int localtime)
1212{
1213    char   *format;
1214    int     format_len;
1215    long    ts;
1216    char   *string;
1217
1218    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &format, &format_len, &ts) == FAILURE) {
1219        RETURN_FALSE;
1220    }
1221    if (ZEND_NUM_ARGS() == 1) {
1222        ts = time(NULL);
1223    }
1224
1225    string = php_format_date(format, format_len, ts, localtime TSRMLS_CC);
1226
1227    RETVAL_STRING(string, 0);
1228}
1229/* }}} */
1230
1231PHPAPI char *php_format_date(char *format, int format_len, time_t ts, int localtime TSRMLS_DC) /* {{{ */
1232{
1233    timelib_time   *t;
1234    timelib_tzinfo *tzi;
1235    char *string;
1236
1237    t = timelib_time_ctor();
1238
1239    if (localtime) {
1240        tzi = get_timezone_info(TSRMLS_C);
1241        t->tz_info = tzi;
1242        t->zone_type = TIMELIB_ZONETYPE_ID;
1243        timelib_unixtime2local(t, ts);
1244    } else {
1245        tzi = NULL;
1246        timelib_unixtime2gmt(t, ts);
1247    }
1248
1249    string = date_format(format, format_len, t, localtime);
1250
1251    timelib_time_dtor(t);
1252    return string;
1253}
1254/* }}} */
1255
1256/* {{{ php_idate
1257 */
1258PHPAPI int php_idate(char format, time_t ts, int localtime TSRMLS_DC)
1259{
1260    timelib_time   *t;
1261    timelib_tzinfo *tzi;
1262    int retval = -1;
1263    timelib_time_offset *offset = NULL;
1264    timelib_sll isoweek, isoyear;
1265
1266    t = timelib_time_ctor();
1267
1268    if (!localtime) {
1269        tzi = get_timezone_info(TSRMLS_C);
1270        t->tz_info = tzi;
1271        t->zone_type = TIMELIB_ZONETYPE_ID;
1272        timelib_unixtime2local(t, ts);
1273    } else {
1274        tzi = NULL;
1275        timelib_unixtime2gmt(t, ts);
1276    }
1277
1278    if (!localtime) {
1279        if (t->zone_type == TIMELIB_ZONETYPE_ABBR) {
1280            offset = timelib_time_offset_ctor();
1281            offset->offset = (t->z - (t->dst * 60)) * -60;
1282            offset->leap_secs = 0;
1283            offset->is_dst = t->dst;
1284            offset->abbr = strdup(t->tz_abbr);
1285        } else if (t->zone_type == TIMELIB_ZONETYPE_OFFSET) {
1286            offset = timelib_time_offset_ctor();
1287            offset->offset = (t->z - (t->dst * 60)) * -60;
1288            offset->leap_secs = 0;
1289            offset->is_dst = t->dst;
1290            offset->abbr = malloc(9); /* GMT�xxxx\0 */
1291            snprintf(offset->abbr, 9, "GMT%c%02d%02d",
1292                                      !localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
1293                                      !localtime ? abs(offset->offset / 3600) : 0,
1294                                      !localtime ? abs((offset->offset % 3600) / 60) : 0 );
1295        } else {
1296            offset = timelib_get_time_zone_info(t->sse, t->tz_info);
1297        }
1298    }
1299
1300    timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear);
1301
1302    switch (format) {
1303        /* day */
1304        case 'd': case 'j': retval = (int) t->d; break;
1305
1306        case 'w': retval = (int) timelib_day_of_week(t->y, t->m, t->d); break;
1307        case 'z': retval = (int) timelib_day_of_year(t->y, t->m, t->d); break;
1308
1309        /* week */
1310        case 'W': retval = (int) isoweek; break; /* iso weeknr */
1311
1312        /* month */
1313        case 'm': case 'n': retval = (int) t->m; break;
1314        case 't': retval = (int) timelib_days_in_month(t->y, t->m); break;
1315
1316        /* year */
1317        case 'L': retval = (int) timelib_is_leap((int) t->y); break;
1318        case 'y': retval = (int) (t->y % 100); break;
1319        case 'Y': retval = (int) t->y; break;
1320
1321        /* Swatch Beat a.k.a. Internet Time */
1322        case 'B':
1323            retval = (((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10) / 864);
1324            while (retval < 0) {
1325                retval += 1000;
1326            }
1327            retval = retval % 1000;
1328            break;
1329
1330        /* time */
1331        case 'g': case 'h': retval = (int) ((t->h % 12) ? (int) t->h % 12 : 12); break;
1332        case 'H': case 'G': retval = (int) t->h; break;
1333        case 'i': retval = (int) t->i; break;
1334        case 's': retval = (int) t->s; break;
1335
1336        /* timezone */
1337        case 'I': retval = (int) (!localtime ? offset->is_dst : 0); break;
1338        case 'Z': retval = (int) (!localtime ? offset->offset : 0); break;
1339
1340        case 'U': retval = (int) t->sse; break;
1341    }
1342
1343    if (!localtime) {
1344        timelib_time_offset_dtor(offset);
1345    }
1346    timelib_time_dtor(t);
1347
1348    return retval;
1349}
1350/* }}} */
1351
1352/* {{{ proto string date(string format [, long timestamp])
1353   Format a local date/time */
1354PHP_FUNCTION(date)
1355{
1356    php_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1357}
1358/* }}} */
1359
1360/* {{{ proto string gmdate(string format [, long timestamp])
1361   Format a GMT date/time */
1362PHP_FUNCTION(gmdate)
1363{
1364    php_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1365}
1366/* }}} */
1367
1368/* {{{ proto int idate(string format [, int timestamp])
1369   Format a local time/date as integer */
1370PHP_FUNCTION(idate)
1371{
1372    char   *format;
1373    int     format_len;
1374    long    ts = 0;
1375    int ret;
1376
1377    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &format, &format_len, &ts) == FAILURE) {
1378        RETURN_FALSE;
1379    }
1380
1381    if (format_len != 1) {
1382        php_error_docref(NULL TSRMLS_CC, E_WARNING, "idate format is one char");
1383        RETURN_FALSE;
1384    }
1385
1386    if (ZEND_NUM_ARGS() == 1) {
1387        ts = time(NULL);
1388    }
1389
1390    ret = php_idate(format[0], ts, 0 TSRMLS_CC);
1391    if (ret == -1) {
1392        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unrecognized date format token.");
1393        RETURN_FALSE;
1394    }
1395    RETURN_LONG(ret);
1396}
1397/* }}} */
1398
1399/* {{{ php_date_set_tzdb - NOT THREADSAFE */
1400PHPAPI void php_date_set_tzdb(timelib_tzdb *tzdb)
1401{
1402    const timelib_tzdb *builtin = timelib_builtin_db();
1403
1404    if (php_version_compare(tzdb->version, builtin->version) > 0) {
1405        php_date_global_timezone_db = tzdb;
1406        php_date_global_timezone_db_enabled = 1;
1407    }
1408}
1409/* }}} */
1410
1411/* {{{ php_parse_date: Backwards compatibility function */
1412PHPAPI signed long php_parse_date(char *string, signed long *now)
1413{
1414    timelib_time *parsed_time;
1415    timelib_error_container *error = NULL;
1416    int           error2;
1417    signed long   retval;
1418
1419    parsed_time = timelib_strtotime(string, strlen(string), &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
1420    if (error->error_count) {
1421        timelib_time_dtor(parsed_time);
1422        timelib_error_container_dtor(error);
1423        return -1;
1424    }
1425    timelib_error_container_dtor(error);
1426    timelib_update_ts(parsed_time, NULL);
1427    retval = timelib_date_to_int(parsed_time, &error2);
1428    timelib_time_dtor(parsed_time);
1429    if (error2) {
1430        return -1;
1431    }
1432    return retval;
1433}
1434/* }}} */
1435
1436/* {{{ proto int strtotime(string time [, int now ])
1437   Convert string representation of date and time to a timestamp */
1438PHP_FUNCTION(strtotime)
1439{
1440    char *times, *initial_ts;
1441    int   time_len, error1, error2;
1442    struct timelib_error_container *error;
1443    long  preset_ts = 0, ts;
1444
1445    timelib_time *t, *now;
1446    timelib_tzinfo *tzi;
1447
1448    tzi = get_timezone_info(TSRMLS_C);
1449
1450    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sl", &times, &time_len, &preset_ts) != FAILURE) {
1451        /* We have an initial timestamp */
1452        now = timelib_time_ctor();
1453
1454        initial_ts = emalloc(25);
1455        snprintf(initial_ts, 24, "@%ld UTC", preset_ts);
1456        t = timelib_strtotime(initial_ts, strlen(initial_ts), NULL, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper); /* we ignore the error here, as this should never fail */
1457        timelib_update_ts(t, tzi);
1458        now->tz_info = tzi;
1459        now->zone_type = TIMELIB_ZONETYPE_ID;
1460        timelib_unixtime2local(now, t->sse);
1461        timelib_time_dtor(t);
1462        efree(initial_ts);
1463    } else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &times, &time_len, &preset_ts) != FAILURE) {
1464        /* We have no initial timestamp */
1465        now = timelib_time_ctor();
1466        now->tz_info = tzi;
1467        now->zone_type = TIMELIB_ZONETYPE_ID;
1468        timelib_unixtime2local(now, (timelib_sll) time(NULL));
1469    } else {
1470        RETURN_FALSE;
1471    }
1472
1473    if (!time_len) {
1474        timelib_time_dtor(now);
1475        RETURN_FALSE;
1476    }
1477
1478    t = timelib_strtotime(times, time_len, &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
1479    error1 = error->error_count;
1480    timelib_error_container_dtor(error);
1481    timelib_fill_holes(t, now, TIMELIB_NO_CLONE);
1482    timelib_update_ts(t, tzi);
1483    ts = timelib_date_to_int(t, &error2);
1484
1485    timelib_time_dtor(now);
1486    timelib_time_dtor(t);
1487
1488    if (error1 || error2) {
1489        RETURN_FALSE;
1490    } else {
1491        RETURN_LONG(ts);
1492    }
1493}
1494/* }}} */
1495
1496/* {{{ php_mktime - (gm)mktime helper */
1497PHPAPI void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gmt)
1498{
1499    long hou = 0, min = 0, sec = 0, mon = 0, day = 0, yea = 0, dst = -1;
1500    timelib_time *now;
1501    timelib_tzinfo *tzi = NULL;
1502    long ts, adjust_seconds = 0;
1503    int error;
1504
1505    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lllllll", &hou, &min, &sec, &mon, &day, &yea, &dst) == FAILURE) {
1506        RETURN_FALSE;
1507    }
1508    /* Initialize structure with current time */
1509    now = timelib_time_ctor();
1510    if (gmt) {
1511        timelib_unixtime2gmt(now, (timelib_sll) time(NULL));
1512    } else {
1513        tzi = get_timezone_info(TSRMLS_C);
1514        now->tz_info = tzi;
1515        now->zone_type = TIMELIB_ZONETYPE_ID;
1516        timelib_unixtime2local(now, (timelib_sll) time(NULL));
1517    }
1518    /* Fill in the new data */
1519    switch (ZEND_NUM_ARGS()) {
1520        case 7:
1521            /* break intentionally missing */
1522        case 6:
1523            if (yea >= 0 && yea < 70) {
1524                yea += 2000;
1525            } else if (yea >= 70 && yea <= 100) {
1526                yea += 1900;
1527            }
1528            now->y = yea;
1529            /* break intentionally missing again */
1530        case 5:
1531            now->d = day;
1532            /* break missing intentionally here too */
1533        case 4:
1534            now->m = mon;
1535            /* and here */
1536        case 3:
1537            now->s = sec;
1538            /* yup, this break isn't here on purpose too */
1539        case 2:
1540            now->i = min;
1541            /* last intentionally missing break */
1542        case 1:
1543            now->h = hou;
1544            break;
1545        default:
1546            php_error_docref(NULL TSRMLS_CC, E_STRICT, "You should be using the time() function instead");
1547    }
1548    /* Update the timestamp */
1549    if (gmt) {
1550        timelib_update_ts(now, NULL);
1551    } else {
1552        timelib_update_ts(now, tzi);
1553    }
1554    /* Support for the deprecated is_dst parameter */
1555    if (dst != -1) {
1556        php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "The is_dst parameter is deprecated");
1557        if (gmt) {
1558            /* GMT never uses DST */
1559            if (dst == 1) {
1560                adjust_seconds = -3600;
1561            }
1562        } else {
1563            /* Figure out is_dst for current TS */
1564            timelib_time_offset *tmp_offset;
1565            tmp_offset = timelib_get_time_zone_info(now->sse, tzi);
1566            if (dst == 1 && tmp_offset->is_dst == 0) {
1567                adjust_seconds = -3600;
1568            }
1569            if (dst == 0 && tmp_offset->is_dst == 1) {
1570                adjust_seconds = +3600;
1571            }
1572            timelib_time_offset_dtor(tmp_offset);
1573        }
1574    }
1575    /* Clean up and return */
1576    ts = timelib_date_to_int(now, &error);
1577    ts += adjust_seconds;
1578    timelib_time_dtor(now);
1579
1580    if (error) {
1581        RETURN_FALSE;
1582    } else {
1583        RETURN_LONG(ts);
1584    }
1585}
1586/* }}} */
1587
1588/* {{{ proto int mktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]])
1589   Get UNIX timestamp for a date */
1590PHP_FUNCTION(mktime)
1591{
1592    php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1593}
1594/* }}} */
1595
1596/* {{{ proto int gmmktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]])
1597   Get UNIX timestamp for a GMT date */
1598PHP_FUNCTION(gmmktime)
1599{
1600    php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1601}
1602/* }}} */
1603
1604/* {{{ proto bool checkdate(int month, int day, int year)
1605   Returns true(1) if it is a valid date in gregorian calendar */
1606PHP_FUNCTION(checkdate)
1607{
1608    long m, d, y;
1609
1610    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &m, &d, &y) == FAILURE) {
1611        RETURN_FALSE;
1612    }
1613
1614    if (y < 1 || y > 32767 || !timelib_valid_date(y, m, d)) {
1615        RETURN_FALSE;
1616    }
1617    RETURN_TRUE;    /* True : This month, day, year arguments are valid */
1618}
1619/* }}} */
1620
1621#ifdef HAVE_STRFTIME
1622/* {{{ php_strftime - (gm)strftime helper */
1623PHPAPI void php_strftime(INTERNAL_FUNCTION_PARAMETERS, int gmt)
1624{
1625    char                *format, *buf;
1626    int                  format_len;
1627    long                 timestamp = 0;
1628    struct tm            ta;
1629    int                  max_reallocs = 5;
1630    size_t               buf_len = 256, real_len;
1631    timelib_time        *ts;
1632    timelib_tzinfo      *tzi;
1633    timelib_time_offset *offset = NULL;
1634
1635    timestamp = (long) time(NULL);
1636
1637    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &format, &format_len, &timestamp) == FAILURE) {
1638        RETURN_FALSE;
1639    }
1640
1641    if (format_len == 0) {
1642        RETURN_FALSE;
1643    }
1644
1645    ts = timelib_time_ctor();
1646    if (gmt) {
1647        tzi = NULL;
1648        timelib_unixtime2gmt(ts, (timelib_sll) timestamp);
1649    } else {
1650        tzi = get_timezone_info(TSRMLS_C);
1651        ts->tz_info = tzi;
1652        ts->zone_type = TIMELIB_ZONETYPE_ID;
1653        timelib_unixtime2local(ts, (timelib_sll) timestamp);
1654    }
1655    ta.tm_sec   = ts->s;
1656    ta.tm_min   = ts->i;
1657    ta.tm_hour  = ts->h;
1658    ta.tm_mday  = ts->d;
1659    ta.tm_mon   = ts->m - 1;
1660    ta.tm_year  = ts->y - 1900;
1661    ta.tm_wday  = timelib_day_of_week(ts->y, ts->m, ts->d);
1662    ta.tm_yday  = timelib_day_of_year(ts->y, ts->m, ts->d);
1663    if (gmt) {
1664        ta.tm_isdst = 0;
1665#if HAVE_TM_GMTOFF
1666        ta.tm_gmtoff = 0;
1667#endif
1668#if HAVE_TM_ZONE
1669        ta.tm_zone = "GMT";
1670#endif
1671    } else {
1672        offset = timelib_get_time_zone_info(timestamp, tzi);
1673
1674        ta.tm_isdst = offset->is_dst;
1675#if HAVE_TM_GMTOFF
1676        ta.tm_gmtoff = offset->offset;
1677#endif
1678#if HAVE_TM_ZONE
1679        ta.tm_zone = offset->abbr;
1680#endif
1681    }
1682
1683    /* VS2012 crt has a bug where strftime crash with %z and %Z format when the
1684       initial buffer is too small. See
1685       http://connect.microsoft.com/VisualStudio/feedback/details/759720/vs2012-strftime-crash-with-z-formatting-code */
1686    buf = (char *) emalloc(buf_len);
1687    while ((real_len=strftime(buf, buf_len, format, &ta))==buf_len || real_len==0) {
1688        buf_len *= 2;
1689        buf = (char *) erealloc(buf, buf_len);
1690        if (!--max_reallocs) {
1691            break;
1692        }
1693    }
1694#if defined(PHP_WIN32) && _MSC_VER >= 1700
1695    /* VS2012 strftime() returns number of characters, not bytes.
1696        See VC++11 bug id 766205. */
1697    if (real_len > 0) {
1698        real_len = strlen(buf);
1699    }
1700#endif
1701
1702    timelib_time_dtor(ts);
1703    if (!gmt) {
1704        timelib_time_offset_dtor(offset);
1705    }
1706
1707    if (real_len && real_len != buf_len) {
1708        buf = (char *) erealloc(buf, real_len + 1);
1709        RETURN_STRINGL(buf, real_len, 0);
1710    }
1711    efree(buf);
1712    RETURN_FALSE;
1713}
1714/* }}} */
1715
1716/* {{{ proto string strftime(string format [, int timestamp])
1717   Format a local time/date according to locale settings */
1718PHP_FUNCTION(strftime)
1719{
1720    php_strftime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1721}
1722/* }}} */
1723
1724/* {{{ proto string gmstrftime(string format [, int timestamp])
1725   Format a GMT/UCT time/date according to locale settings */
1726PHP_FUNCTION(gmstrftime)
1727{
1728    php_strftime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1729}
1730/* }}} */
1731#endif
1732
1733/* {{{ proto int time(void)
1734   Return current UNIX timestamp */
1735PHP_FUNCTION(time)
1736{
1737    RETURN_LONG((long)time(NULL));
1738}
1739/* }}} */
1740
1741/* {{{ proto array localtime([int timestamp [, bool associative_array]])
1742   Returns the results of the C system call localtime as an associative array if the associative_array argument is set to 1 other wise it is a regular array */
1743PHP_FUNCTION(localtime)
1744{
1745    long timestamp = (long)time(NULL);
1746    zend_bool associative = 0;
1747    timelib_tzinfo *tzi;
1748    timelib_time   *ts;
1749
1750    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lb", &timestamp, &associative) == FAILURE) {
1751        RETURN_FALSE;
1752    }
1753
1754    tzi = get_timezone_info(TSRMLS_C);
1755    ts = timelib_time_ctor();
1756    ts->tz_info = tzi;
1757    ts->zone_type = TIMELIB_ZONETYPE_ID;
1758    timelib_unixtime2local(ts, (timelib_sll) timestamp);
1759
1760    array_init(return_value);
1761
1762    if (associative) {
1763        add_assoc_long(return_value, "tm_sec",   ts->s);
1764        add_assoc_long(return_value, "tm_min",   ts->i);
1765        add_assoc_long(return_value, "tm_hour",  ts->h);
1766        add_assoc_long(return_value, "tm_mday",  ts->d);
1767        add_assoc_long(return_value, "tm_mon",   ts->m - 1);
1768        add_assoc_long(return_value, "tm_year",  ts->y - 1900);
1769        add_assoc_long(return_value, "tm_wday",  timelib_day_of_week(ts->y, ts->m, ts->d));
1770        add_assoc_long(return_value, "tm_yday",  timelib_day_of_year(ts->y, ts->m, ts->d));
1771        add_assoc_long(return_value, "tm_isdst", ts->dst);
1772    } else {
1773        add_next_index_long(return_value, ts->s);
1774        add_next_index_long(return_value, ts->i);
1775        add_next_index_long(return_value, ts->h);
1776        add_next_index_long(return_value, ts->d);
1777        add_next_index_long(return_value, ts->m - 1);
1778        add_next_index_long(return_value, ts->y- 1900);
1779        add_next_index_long(return_value, timelib_day_of_week(ts->y, ts->m, ts->d));
1780        add_next_index_long(return_value, timelib_day_of_year(ts->y, ts->m, ts->d));
1781        add_next_index_long(return_value, ts->dst);
1782    }
1783
1784    timelib_time_dtor(ts);
1785}
1786/* }}} */
1787
1788/* {{{ proto array getdate([int timestamp])
1789   Get date/time information */
1790PHP_FUNCTION(getdate)
1791{
1792    long timestamp = (long)time(NULL);
1793    timelib_tzinfo *tzi;
1794    timelib_time   *ts;
1795
1796    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &timestamp) == FAILURE) {
1797        RETURN_FALSE;
1798    }
1799
1800    tzi = get_timezone_info(TSRMLS_C);
1801    ts = timelib_time_ctor();
1802    ts->tz_info = tzi;
1803    ts->zone_type = TIMELIB_ZONETYPE_ID;
1804    timelib_unixtime2local(ts, (timelib_sll) timestamp);
1805
1806    array_init(return_value);
1807
1808    add_assoc_long(return_value, "seconds", ts->s);
1809    add_assoc_long(return_value, "minutes", ts->i);
1810    add_assoc_long(return_value, "hours", ts->h);
1811    add_assoc_long(return_value, "mday", ts->d);
1812    add_assoc_long(return_value, "wday", timelib_day_of_week(ts->y, ts->m, ts->d));
1813    add_assoc_long(return_value, "mon", ts->m);
1814    add_assoc_long(return_value, "year", ts->y);
1815    add_assoc_long(return_value, "yday", timelib_day_of_year(ts->y, ts->m, ts->d));
1816    add_assoc_string(return_value, "weekday", php_date_full_day_name(ts->y, ts->m, ts->d), 1);
1817    add_assoc_string(return_value, "month", mon_full_names[ts->m - 1], 1);
1818    add_index_long(return_value, 0, timestamp);
1819
1820    timelib_time_dtor(ts);
1821}
1822/* }}} */
1823
1824#define PHP_DATE_TIMEZONE_GROUP_AFRICA     0x0001
1825#define PHP_DATE_TIMEZONE_GROUP_AMERICA    0x0002
1826#define PHP_DATE_TIMEZONE_GROUP_ANTARCTICA 0x0004
1827#define PHP_DATE_TIMEZONE_GROUP_ARCTIC     0x0008
1828#define PHP_DATE_TIMEZONE_GROUP_ASIA       0x0010
1829#define PHP_DATE_TIMEZONE_GROUP_ATLANTIC   0x0020
1830#define PHP_DATE_TIMEZONE_GROUP_AUSTRALIA  0x0040
1831#define PHP_DATE_TIMEZONE_GROUP_EUROPE     0x0080
1832#define PHP_DATE_TIMEZONE_GROUP_INDIAN     0x0100
1833#define PHP_DATE_TIMEZONE_GROUP_PACIFIC    0x0200
1834#define PHP_DATE_TIMEZONE_GROUP_UTC        0x0400
1835#define PHP_DATE_TIMEZONE_GROUP_ALL        0x07FF
1836#define PHP_DATE_TIMEZONE_GROUP_ALL_W_BC   0x0FFF
1837#define PHP_DATE_TIMEZONE_PER_COUNTRY      0x1000
1838
1839#define PHP_DATE_PERIOD_EXCLUDE_START_DATE 0x0001
1840
1841
1842/* define an overloaded iterator structure */
1843typedef struct {
1844    zend_object_iterator  intern;
1845    zval                 *date_period_zval;
1846    zval                 *current;
1847    php_period_obj       *object;
1848    int                   current_index;
1849} date_period_it;
1850
1851/* {{{ date_period_it_invalidate_current */
1852static void date_period_it_invalidate_current(zend_object_iterator *iter TSRMLS_DC)
1853{
1854    date_period_it *iterator = (date_period_it *)iter;
1855
1856    if (iterator->current) {
1857        zval_ptr_dtor(&iterator->current);
1858        iterator->current = NULL;
1859    }
1860}
1861/* }}} */
1862
1863
1864/* {{{ date_period_it_dtor */
1865static void date_period_it_dtor(zend_object_iterator *iter TSRMLS_DC)
1866{
1867    date_period_it *iterator = (date_period_it *)iter;
1868
1869    date_period_it_invalidate_current(iter TSRMLS_CC);
1870
1871    zval_ptr_dtor(&iterator->date_period_zval);
1872
1873    efree(iterator);
1874}
1875/* }}} */
1876
1877
1878/* {{{ date_period_it_has_more */
1879static int date_period_it_has_more(zend_object_iterator *iter TSRMLS_DC)
1880{
1881    date_period_it *iterator = (date_period_it *)iter;
1882    php_period_obj *object   = iterator->object;
1883    timelib_time   *it_time = object->current;
1884
1885    /* apply modification if it's not the first iteration */
1886    if (!object->include_start_date || iterator->current_index > 0) {
1887        it_time->have_relative = 1;
1888        it_time->relative = *object->interval;
1889        it_time->sse_uptodate = 0;
1890        timelib_update_ts(it_time, NULL);
1891        timelib_update_from_sse(it_time);
1892    }
1893
1894    if (object->end) {
1895        return object->current->sse < object->end->sse ? SUCCESS : FAILURE;
1896    } else {
1897        return (iterator->current_index < object->recurrences) ? SUCCESS : FAILURE;
1898    }
1899}
1900/* }}} */
1901
1902
1903/* {{{ date_period_it_current_data */
1904static void date_period_it_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
1905{
1906    date_period_it *iterator = (date_period_it *)iter;
1907    php_period_obj *object   = iterator->object;
1908    timelib_time   *it_time = object->current;
1909    php_date_obj   *newdateobj;
1910
1911    /* Create new object */
1912    MAKE_STD_ZVAL(iterator->current);
1913    php_date_instantiate(object->start_ce, iterator->current TSRMLS_CC);
1914    newdateobj = (php_date_obj *) zend_object_store_get_object(iterator->current TSRMLS_CC);
1915    newdateobj->time = timelib_time_ctor();
1916    *newdateobj->time = *it_time;
1917    if (it_time->tz_abbr) {
1918        newdateobj->time->tz_abbr = strdup(it_time->tz_abbr);
1919    }
1920    if (it_time->tz_info) {
1921        newdateobj->time->tz_info = it_time->tz_info;
1922    }
1923
1924    *data = &iterator->current;
1925}
1926/* }}} */
1927
1928
1929/* {{{ date_period_it_current_key */
1930static void date_period_it_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC)
1931{
1932    date_period_it *iterator = (date_period_it *)iter;
1933    ZVAL_LONG(key, iterator->current_index);
1934}
1935/* }}} */
1936
1937
1938/* {{{ date_period_it_move_forward */
1939static void date_period_it_move_forward(zend_object_iterator *iter TSRMLS_DC)
1940{
1941    date_period_it   *iterator = (date_period_it *)iter;
1942
1943    iterator->current_index++;
1944    date_period_it_invalidate_current(iter TSRMLS_CC);
1945}
1946/* }}} */
1947
1948
1949/* {{{ date_period_it_rewind */
1950static void date_period_it_rewind(zend_object_iterator *iter TSRMLS_DC)
1951{
1952    date_period_it   *iterator = (date_period_it *)iter;
1953
1954    iterator->current_index = 0;
1955    if (iterator->object->current) {
1956        timelib_time_dtor(iterator->object->current);
1957    }
1958    iterator->object->current = timelib_time_clone(iterator->object->start);
1959    date_period_it_invalidate_current(iter TSRMLS_CC);
1960}
1961/* }}} */
1962
1963
1964/* iterator handler table */
1965zend_object_iterator_funcs date_period_it_funcs = {
1966    date_period_it_dtor,
1967    date_period_it_has_more,
1968    date_period_it_current_data,
1969    date_period_it_current_key,
1970    date_period_it_move_forward,
1971    date_period_it_rewind,
1972    date_period_it_invalidate_current
1973};
1974
1975
1976
1977zend_object_iterator *date_object_period_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
1978{
1979    date_period_it  *iterator = emalloc(sizeof(date_period_it));
1980    php_period_obj  *dpobj    = (php_period_obj *)zend_object_store_get_object(object TSRMLS_CC);
1981
1982    if (by_ref) {
1983        zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
1984    }
1985
1986    Z_ADDREF_P(object);
1987    iterator->intern.data = (void*) dpobj;
1988    iterator->intern.funcs = &date_period_it_funcs;
1989    iterator->date_period_zval = object;
1990    iterator->object = dpobj;
1991    iterator->current = NULL;
1992
1993    return (zend_object_iterator*)iterator;
1994}
1995
1996static int implement_date_interface_handler(zend_class_entry *interface, zend_class_entry *implementor TSRMLS_DC)
1997{
1998    if (implementor->type == ZEND_USER_CLASS &&
1999        !instanceof_function(implementor, date_ce_date TSRMLS_CC) &&
2000        !instanceof_function(implementor, date_ce_immutable TSRMLS_CC)
2001    ) {
2002        zend_error(E_ERROR, "DateTimeInterface can't be implemented by user classes");
2003    }
2004
2005    return SUCCESS;
2006}
2007
2008static void date_register_classes(TSRMLS_D)
2009{
2010    zend_class_entry ce_date, ce_immutable, ce_timezone, ce_interval, ce_period, ce_interface;
2011
2012    INIT_CLASS_ENTRY(ce_interface, "DateTimeInterface", date_funcs_interface);
2013    date_ce_interface = zend_register_internal_interface(&ce_interface TSRMLS_CC);
2014    date_ce_interface->interface_gets_implemented = implement_date_interface_handler;
2015
2016    INIT_CLASS_ENTRY(ce_date, "DateTime", date_funcs_date);
2017    ce_date.create_object = date_object_new_date;
2018    date_ce_date = zend_register_internal_class_ex(&ce_date, NULL, NULL TSRMLS_CC);
2019    memcpy(&date_object_handlers_date, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2020    date_object_handlers_date.clone_obj = date_object_clone_date;
2021    date_object_handlers_date.compare_objects = date_object_compare_date;
2022    date_object_handlers_date.get_properties = date_object_get_properties;
2023    date_object_handlers_date.get_gc = date_object_get_gc;
2024    zend_class_implements(date_ce_date TSRMLS_CC, 1, date_ce_interface);
2025
2026#define REGISTER_DATE_CLASS_CONST_STRING(const_name, value) \
2027    zend_declare_class_constant_stringl(date_ce_date, const_name, sizeof(const_name)-1, value, sizeof(value)-1 TSRMLS_CC);
2028
2029    REGISTER_DATE_CLASS_CONST_STRING("ATOM",    DATE_FORMAT_RFC3339);
2030    REGISTER_DATE_CLASS_CONST_STRING("COOKIE",  DATE_FORMAT_COOKIE);
2031    REGISTER_DATE_CLASS_CONST_STRING("ISO8601", DATE_FORMAT_ISO8601);
2032    REGISTER_DATE_CLASS_CONST_STRING("RFC822",  DATE_FORMAT_RFC822);
2033    REGISTER_DATE_CLASS_CONST_STRING("RFC850",  DATE_FORMAT_RFC850);
2034    REGISTER_DATE_CLASS_CONST_STRING("RFC1036", DATE_FORMAT_RFC1036);
2035    REGISTER_DATE_CLASS_CONST_STRING("RFC1123", DATE_FORMAT_RFC1123);
2036    REGISTER_DATE_CLASS_CONST_STRING("RFC2822", DATE_FORMAT_RFC2822);
2037    REGISTER_DATE_CLASS_CONST_STRING("RFC3339", DATE_FORMAT_RFC3339);
2038    REGISTER_DATE_CLASS_CONST_STRING("RSS",     DATE_FORMAT_RFC1123);
2039    REGISTER_DATE_CLASS_CONST_STRING("W3C",     DATE_FORMAT_RFC3339);
2040
2041    INIT_CLASS_ENTRY(ce_immutable, "DateTimeImmutable", date_funcs_immutable);
2042    ce_immutable.create_object = date_object_new_date;
2043    date_ce_immutable = zend_register_internal_class_ex(&ce_immutable, NULL, NULL TSRMLS_CC);
2044    memcpy(&date_object_handlers_immutable, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2045    date_object_handlers_immutable.clone_obj = date_object_clone_date;
2046    date_object_handlers_immutable.compare_objects = date_object_compare_date;
2047    date_object_handlers_immutable.get_properties = date_object_get_properties;
2048    zend_class_implements(date_ce_immutable TSRMLS_CC, 1, date_ce_interface);
2049
2050    INIT_CLASS_ENTRY(ce_timezone, "DateTimeZone", date_funcs_timezone);
2051    ce_timezone.create_object = date_object_new_timezone;
2052    date_ce_timezone = zend_register_internal_class_ex(&ce_timezone, NULL, NULL TSRMLS_CC);
2053    memcpy(&date_object_handlers_timezone, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2054    date_object_handlers_timezone.clone_obj = date_object_clone_timezone;
2055    date_object_handlers_timezone.get_properties = date_object_get_properties_timezone;
2056    date_object_handlers_timezone.get_gc = date_object_get_gc_timezone;
2057
2058#define REGISTER_TIMEZONE_CLASS_CONST_STRING(const_name, value) \
2059    zend_declare_class_constant_long(date_ce_timezone, const_name, sizeof(const_name)-1, value TSRMLS_CC);
2060
2061    REGISTER_TIMEZONE_CLASS_CONST_STRING("AFRICA",      PHP_DATE_TIMEZONE_GROUP_AFRICA);
2062    REGISTER_TIMEZONE_CLASS_CONST_STRING("AMERICA",     PHP_DATE_TIMEZONE_GROUP_AMERICA);
2063    REGISTER_TIMEZONE_CLASS_CONST_STRING("ANTARCTICA",  PHP_DATE_TIMEZONE_GROUP_ANTARCTICA);
2064    REGISTER_TIMEZONE_CLASS_CONST_STRING("ARCTIC",      PHP_DATE_TIMEZONE_GROUP_ARCTIC);
2065    REGISTER_TIMEZONE_CLASS_CONST_STRING("ASIA",        PHP_DATE_TIMEZONE_GROUP_ASIA);
2066    REGISTER_TIMEZONE_CLASS_CONST_STRING("ATLANTIC",    PHP_DATE_TIMEZONE_GROUP_ATLANTIC);
2067    REGISTER_TIMEZONE_CLASS_CONST_STRING("AUSTRALIA",   PHP_DATE_TIMEZONE_GROUP_AUSTRALIA);
2068    REGISTER_TIMEZONE_CLASS_CONST_STRING("EUROPE",      PHP_DATE_TIMEZONE_GROUP_EUROPE);
2069    REGISTER_TIMEZONE_CLASS_CONST_STRING("INDIAN",      PHP_DATE_TIMEZONE_GROUP_INDIAN);
2070    REGISTER_TIMEZONE_CLASS_CONST_STRING("PACIFIC",     PHP_DATE_TIMEZONE_GROUP_PACIFIC);
2071    REGISTER_TIMEZONE_CLASS_CONST_STRING("UTC",         PHP_DATE_TIMEZONE_GROUP_UTC);
2072    REGISTER_TIMEZONE_CLASS_CONST_STRING("ALL",         PHP_DATE_TIMEZONE_GROUP_ALL);
2073    REGISTER_TIMEZONE_CLASS_CONST_STRING("ALL_WITH_BC", PHP_DATE_TIMEZONE_GROUP_ALL_W_BC);
2074    REGISTER_TIMEZONE_CLASS_CONST_STRING("PER_COUNTRY", PHP_DATE_TIMEZONE_PER_COUNTRY);
2075
2076    INIT_CLASS_ENTRY(ce_interval, "DateInterval", date_funcs_interval);
2077    ce_interval.create_object = date_object_new_interval;
2078    date_ce_interval = zend_register_internal_class_ex(&ce_interval, NULL, NULL TSRMLS_CC);
2079    memcpy(&date_object_handlers_interval, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2080    date_object_handlers_interval.clone_obj = date_object_clone_interval;
2081    date_object_handlers_interval.read_property = date_interval_read_property;
2082    date_object_handlers_interval.write_property = date_interval_write_property;
2083    date_object_handlers_interval.get_properties = date_object_get_properties_interval;
2084    date_object_handlers_interval.get_property_ptr_ptr = NULL;
2085    date_object_handlers_interval.get_gc = date_object_get_gc_interval;
2086
2087    INIT_CLASS_ENTRY(ce_period, "DatePeriod", date_funcs_period);
2088    ce_period.create_object = date_object_new_period;
2089    date_ce_period = zend_register_internal_class_ex(&ce_period, NULL, NULL TSRMLS_CC);
2090    date_ce_period->get_iterator = date_object_period_get_iterator;
2091    date_ce_period->iterator_funcs.funcs = &date_period_it_funcs;
2092    zend_class_implements(date_ce_period TSRMLS_CC, 1, zend_ce_traversable);
2093    memcpy(&date_object_handlers_period, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2094    date_object_handlers_period.clone_obj = date_object_clone_period;
2095    date_object_handlers_period.get_properties = date_object_get_properties_period;
2096    date_object_handlers_period.get_property_ptr_ptr = NULL;
2097    date_object_handlers_period.get_gc = date_object_get_gc_period;
2098    date_object_handlers_period.read_property = date_period_read_property;
2099    date_object_handlers_period.write_property = date_period_write_property;
2100
2101#define REGISTER_PERIOD_CLASS_CONST_STRING(const_name, value) \
2102    zend_declare_class_constant_long(date_ce_period, const_name, sizeof(const_name)-1, value TSRMLS_CC);
2103
2104    REGISTER_PERIOD_CLASS_CONST_STRING("EXCLUDE_START_DATE", PHP_DATE_PERIOD_EXCLUDE_START_DATE);
2105}
2106
2107static inline zend_object_value date_object_new_date_ex(zend_class_entry *class_type, php_date_obj **ptr TSRMLS_DC)
2108{
2109    php_date_obj *intern;
2110    zend_object_value retval;
2111
2112    intern = emalloc(sizeof(php_date_obj));
2113    memset(intern, 0, sizeof(php_date_obj));
2114    if (ptr) {
2115        *ptr = intern;
2116    }
2117
2118    zend_object_std_init(&intern->std, class_type TSRMLS_CC);
2119    object_properties_init(&intern->std, class_type);
2120
2121    retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) date_object_free_storage_date, NULL TSRMLS_CC);
2122    retval.handlers = &date_object_handlers_date;
2123
2124    return retval;
2125}
2126
2127static zend_object_value date_object_new_date(zend_class_entry *class_type TSRMLS_DC)
2128{
2129    return date_object_new_date_ex(class_type, NULL TSRMLS_CC);
2130}
2131
2132static zend_object_value date_object_clone_date(zval *this_ptr TSRMLS_DC)
2133{
2134    php_date_obj *new_obj = NULL;
2135    php_date_obj *old_obj = (php_date_obj *) zend_object_store_get_object(this_ptr TSRMLS_CC);
2136    zend_object_value new_ov = date_object_new_date_ex(old_obj->std.ce, &new_obj TSRMLS_CC);
2137
2138    zend_objects_clone_members(&new_obj->std, new_ov, &old_obj->std, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
2139    if (!old_obj->time) {
2140        return new_ov;
2141    }
2142
2143    /* this should probably moved to a new `timelib_time *timelime_time_clone(timelib_time *)` */
2144    new_obj->time = timelib_time_ctor();
2145    *new_obj->time = *old_obj->time;
2146    if (old_obj->time->tz_abbr) {
2147        new_obj->time->tz_abbr = strdup(old_obj->time->tz_abbr);
2148    }
2149    if (old_obj->time->tz_info) {
2150        new_obj->time->tz_info = old_obj->time->tz_info;
2151    }
2152
2153    return new_ov;
2154}
2155
2156static zval* date_clone_immutable(zval *object TSRMLS_DC)
2157{
2158    zval *new_object;
2159
2160    ALLOC_ZVAL(new_object);
2161    Z_OBJVAL_P(new_object) = date_object_clone_date(object TSRMLS_CC);
2162    Z_SET_REFCOUNT_P(new_object, 1);
2163    Z_SET_ISREF_P(new_object);
2164    Z_TYPE_P(new_object) = IS_OBJECT;
2165
2166    return new_object;
2167}
2168
2169static int date_object_compare_date(zval *d1, zval *d2 TSRMLS_DC)
2170{
2171    php_date_obj *o1 = zend_object_store_get_object(d1 TSRMLS_CC);
2172    php_date_obj *o2 = zend_object_store_get_object(d2 TSRMLS_CC);
2173
2174    if (!o1->time || !o2->time) {
2175        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Trying to compare an incomplete DateTime or DateTimeImmutable object");
2176        return 1;
2177    }
2178    if (!o1->time->sse_uptodate) {
2179        timelib_update_ts(o1->time, o1->time->tz_info);
2180    }
2181    if (!o2->time->sse_uptodate) {
2182        timelib_update_ts(o2->time, o2->time->tz_info);
2183    }
2184
2185    return (o1->time->sse == o2->time->sse) ? 0 : ((o1->time->sse < o2->time->sse) ? -1 : 1);
2186}
2187
2188static HashTable *date_object_get_gc(zval *object, zval ***table, int *n TSRMLS_DC)
2189{
2190    *table = NULL;
2191    *n = 0;
2192    return zend_std_get_properties(object TSRMLS_CC);
2193}
2194
2195static HashTable *date_object_get_gc_timezone(zval *object, zval ***table, int *n TSRMLS_DC)
2196{
2197
2198       *table = NULL;
2199       *n = 0;
2200       return zend_std_get_properties(object TSRMLS_CC);
2201}
2202
2203static HashTable *date_object_get_properties(zval *object TSRMLS_DC)
2204{
2205    HashTable *props;
2206    zval *zv;
2207    php_date_obj     *dateobj;
2208
2209
2210    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
2211
2212    props = zend_std_get_properties(object TSRMLS_CC);
2213
2214    if (!dateobj->time || GC_G(gc_active)) {
2215        return props;
2216    }
2217
2218    /* first we add the date and time in ISO format */
2219    MAKE_STD_ZVAL(zv);
2220    ZVAL_STRING(zv, date_format("Y-m-d H:i:s.u", 14, dateobj->time, 1), 0);
2221    zend_hash_update(props, "date", 5, &zv, sizeof(zv), NULL);
2222
2223    /* then we add the timezone name (or similar) */
2224    if (dateobj->time->is_localtime) {
2225        MAKE_STD_ZVAL(zv);
2226        ZVAL_LONG(zv, dateobj->time->zone_type);
2227        zend_hash_update(props, "timezone_type", 14, &zv, sizeof(zv), NULL);
2228
2229        MAKE_STD_ZVAL(zv);
2230        switch (dateobj->time->zone_type) {
2231            case TIMELIB_ZONETYPE_ID:
2232                ZVAL_STRING(zv, dateobj->time->tz_info->name, 1);
2233                break;
2234            case TIMELIB_ZONETYPE_OFFSET: {
2235                char *tmpstr = emalloc(sizeof("UTC+05:00"));
2236                timelib_sll utc_offset = dateobj->time->z;
2237
2238                snprintf(tmpstr, sizeof("+05:00"), "%c%02d:%02d",
2239                    utc_offset > 0 ? '-' : '+',
2240                    abs(utc_offset / 60),
2241                    abs((utc_offset % 60)));
2242
2243                ZVAL_STRING(zv, tmpstr, 0);
2244                }
2245                break;
2246            case TIMELIB_ZONETYPE_ABBR:
2247                ZVAL_STRING(zv, dateobj->time->tz_abbr, 1);
2248                break;
2249        }
2250        zend_hash_update(props, "timezone", 9, &zv, sizeof(zv), NULL);
2251    }
2252
2253    return props;
2254}
2255
2256static inline zend_object_value date_object_new_timezone_ex(zend_class_entry *class_type, php_timezone_obj **ptr TSRMLS_DC)
2257{
2258    php_timezone_obj *intern;
2259    zend_object_value retval;
2260
2261    intern = emalloc(sizeof(php_timezone_obj));
2262    memset(intern, 0, sizeof(php_timezone_obj));
2263    if (ptr) {
2264        *ptr = intern;
2265    }
2266
2267    zend_object_std_init(&intern->std, class_type TSRMLS_CC);
2268    object_properties_init(&intern->std, class_type);
2269
2270    retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) date_object_free_storage_timezone, NULL TSRMLS_CC);
2271    retval.handlers = &date_object_handlers_timezone;
2272
2273    return retval;
2274}
2275
2276static zend_object_value date_object_new_timezone(zend_class_entry *class_type TSRMLS_DC)
2277{
2278    return date_object_new_timezone_ex(class_type, NULL TSRMLS_CC);
2279}
2280
2281static zend_object_value date_object_clone_timezone(zval *this_ptr TSRMLS_DC)
2282{
2283    php_timezone_obj *new_obj = NULL;
2284    php_timezone_obj *old_obj = (php_timezone_obj *) zend_object_store_get_object(this_ptr TSRMLS_CC);
2285    zend_object_value new_ov = date_object_new_timezone_ex(old_obj->std.ce, &new_obj TSRMLS_CC);
2286
2287    zend_objects_clone_members(&new_obj->std, new_ov, &old_obj->std, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
2288    if (!old_obj->initialized) {
2289        return new_ov;
2290    }
2291
2292    new_obj->type = old_obj->type;
2293    new_obj->initialized = 1;
2294    switch (new_obj->type) {
2295        case TIMELIB_ZONETYPE_ID:
2296            new_obj->tzi.tz = old_obj->tzi.tz;
2297            break;
2298        case TIMELIB_ZONETYPE_OFFSET:
2299            new_obj->tzi.utc_offset = old_obj->tzi.utc_offset;
2300            break;
2301        case TIMELIB_ZONETYPE_ABBR:
2302            new_obj->tzi.z.utc_offset = old_obj->tzi.z.utc_offset;
2303            new_obj->tzi.z.dst        = old_obj->tzi.z.dst;
2304            new_obj->tzi.z.abbr       = strdup(old_obj->tzi.z.abbr);
2305            break;
2306    }
2307
2308    return new_ov;
2309}
2310
2311static HashTable *date_object_get_properties_timezone(zval *object TSRMLS_DC)
2312{
2313    HashTable *props;
2314    zval *zv;
2315    php_timezone_obj     *tzobj;
2316
2317
2318    tzobj = (php_timezone_obj *) zend_object_store_get_object(object TSRMLS_CC);
2319
2320    props = zend_std_get_properties(object TSRMLS_CC);
2321
2322    if (!tzobj->initialized) {
2323        return props;
2324    }
2325
2326    MAKE_STD_ZVAL(zv);
2327    ZVAL_LONG(zv, tzobj->type);
2328    zend_hash_update(props, "timezone_type", 14, &zv, sizeof(zv), NULL);
2329
2330    MAKE_STD_ZVAL(zv);
2331    switch (tzobj->type) {
2332        case TIMELIB_ZONETYPE_ID:
2333            ZVAL_STRING(zv, tzobj->tzi.tz->name, 1);
2334            break;
2335        case TIMELIB_ZONETYPE_OFFSET: {
2336            char *tmpstr = emalloc(sizeof("UTC+05:00"));
2337
2338            snprintf(tmpstr, sizeof("+05:00"), "%c%02d:%02d",
2339            tzobj->tzi.utc_offset > 0 ? '-' : '+',
2340            abs(tzobj->tzi.utc_offset / 60),
2341            abs((tzobj->tzi.utc_offset % 60)));
2342
2343            ZVAL_STRING(zv, tmpstr, 0);
2344            }
2345            break;
2346        case TIMELIB_ZONETYPE_ABBR:
2347            ZVAL_STRING(zv, tzobj->tzi.z.abbr, 1);
2348            break;
2349    }
2350    zend_hash_update(props, "timezone", 9, &zv, sizeof(zv), NULL);
2351
2352    return props;
2353}
2354
2355static inline zend_object_value date_object_new_interval_ex(zend_class_entry *class_type, php_interval_obj **ptr TSRMLS_DC)
2356{
2357    php_interval_obj *intern;
2358    zend_object_value retval;
2359
2360    intern = emalloc(sizeof(php_interval_obj));
2361    memset(intern, 0, sizeof(php_interval_obj));
2362    if (ptr) {
2363        *ptr = intern;
2364    }
2365
2366    zend_object_std_init(&intern->std, class_type TSRMLS_CC);
2367    object_properties_init(&intern->std, class_type);
2368
2369    retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) date_object_free_storage_interval, NULL TSRMLS_CC);
2370    retval.handlers = &date_object_handlers_interval;
2371
2372    return retval;
2373}
2374
2375static zend_object_value date_object_new_interval(zend_class_entry *class_type TSRMLS_DC)
2376{
2377    return date_object_new_interval_ex(class_type, NULL TSRMLS_CC);
2378}
2379
2380static zend_object_value date_object_clone_interval(zval *this_ptr TSRMLS_DC)
2381{
2382    php_interval_obj *new_obj = NULL;
2383    php_interval_obj *old_obj = (php_interval_obj *) zend_object_store_get_object(this_ptr TSRMLS_CC);
2384    zend_object_value new_ov = date_object_new_interval_ex(old_obj->std.ce, &new_obj TSRMLS_CC);
2385
2386    zend_objects_clone_members(&new_obj->std, new_ov, &old_obj->std, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
2387
2388    /** FIX ME ADD CLONE STUFF **/
2389    return new_ov;
2390}
2391
2392static HashTable *date_object_get_gc_interval(zval *object, zval ***table, int *n TSRMLS_DC)
2393{
2394
2395    *table = NULL;
2396    *n = 0;
2397    return zend_std_get_properties(object TSRMLS_CC);
2398}
2399
2400static HashTable *date_object_get_properties_interval(zval *object TSRMLS_DC)
2401{
2402    HashTable *props;
2403    zval *zv;
2404    php_interval_obj     *intervalobj;
2405
2406    intervalobj = (php_interval_obj *) zend_object_store_get_object(object TSRMLS_CC);
2407
2408    props = zend_std_get_properties(object TSRMLS_CC);
2409
2410    if (!intervalobj->initialized) {
2411        return props;
2412    }
2413
2414#define PHP_DATE_INTERVAL_ADD_PROPERTY(n,f) \
2415    MAKE_STD_ZVAL(zv); \
2416    ZVAL_LONG(zv, (long)intervalobj->diff->f); \
2417    zend_hash_update(props, n, strlen(n) + 1, &zv, sizeof(zv), NULL);
2418
2419    PHP_DATE_INTERVAL_ADD_PROPERTY("y", y);
2420    PHP_DATE_INTERVAL_ADD_PROPERTY("m", m);
2421    PHP_DATE_INTERVAL_ADD_PROPERTY("d", d);
2422    PHP_DATE_INTERVAL_ADD_PROPERTY("h", h);
2423    PHP_DATE_INTERVAL_ADD_PROPERTY("i", i);
2424    PHP_DATE_INTERVAL_ADD_PROPERTY("s", s);
2425    PHP_DATE_INTERVAL_ADD_PROPERTY("weekday", weekday);
2426    PHP_DATE_INTERVAL_ADD_PROPERTY("weekday_behavior", weekday_behavior);
2427    PHP_DATE_INTERVAL_ADD_PROPERTY("first_last_day_of", first_last_day_of);
2428    PHP_DATE_INTERVAL_ADD_PROPERTY("invert", invert);
2429    if (intervalobj->diff->days != -99999) {
2430        PHP_DATE_INTERVAL_ADD_PROPERTY("days", days);
2431    } else {
2432        MAKE_STD_ZVAL(zv);
2433        ZVAL_FALSE(zv);
2434        zend_hash_update(props, "days", 5, &zv, sizeof(zv), NULL);
2435    }
2436    PHP_DATE_INTERVAL_ADD_PROPERTY("special_type", special.type);
2437    PHP_DATE_INTERVAL_ADD_PROPERTY("special_amount", special.amount);
2438    PHP_DATE_INTERVAL_ADD_PROPERTY("have_weekday_relative", have_weekday_relative);
2439    PHP_DATE_INTERVAL_ADD_PROPERTY("have_special_relative", have_special_relative);
2440
2441    return props;
2442}
2443
2444static inline zend_object_value date_object_new_period_ex(zend_class_entry *class_type, php_period_obj **ptr TSRMLS_DC)
2445{
2446    php_period_obj *intern;
2447    zend_object_value retval;
2448
2449    intern = emalloc(sizeof(php_period_obj));
2450    memset(intern, 0, sizeof(php_period_obj));
2451    if (ptr) {
2452        *ptr = intern;
2453    }
2454
2455    zend_object_std_init(&intern->std, class_type TSRMLS_CC);
2456    object_properties_init(&intern->std, class_type);
2457
2458    retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) date_object_free_storage_period, NULL TSRMLS_CC);
2459    retval.handlers = &date_object_handlers_period;
2460
2461    return retval;
2462}
2463
2464static zend_object_value date_object_new_period(zend_class_entry *class_type TSRMLS_DC)
2465{
2466    return date_object_new_period_ex(class_type, NULL TSRMLS_CC);
2467}
2468
2469static zend_object_value date_object_clone_period(zval *this_ptr TSRMLS_DC)
2470{
2471    php_period_obj *new_obj = NULL;
2472    php_period_obj *old_obj = (php_period_obj *) zend_object_store_get_object(this_ptr TSRMLS_CC);
2473    zend_object_value new_ov = date_object_new_period_ex(old_obj->std.ce, &new_obj TSRMLS_CC);
2474
2475    zend_objects_clone_members(&new_obj->std, new_ov, &old_obj->std, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
2476
2477    /** FIX ME ADD CLONE STUFF **/
2478    return new_ov;
2479}
2480
2481static void date_object_free_storage_date(void *object TSRMLS_DC)
2482{
2483    php_date_obj *intern = (php_date_obj *)object;
2484
2485    if (intern->time) {
2486        timelib_time_dtor(intern->time);
2487    }
2488
2489    zend_object_std_dtor(&intern->std TSRMLS_CC);
2490    efree(object);
2491}
2492
2493static void date_object_free_storage_timezone(void *object TSRMLS_DC)
2494{
2495    php_timezone_obj *intern = (php_timezone_obj *)object;
2496
2497    if (intern->type == TIMELIB_ZONETYPE_ABBR) {
2498        free(intern->tzi.z.abbr);
2499    }
2500    zend_object_std_dtor(&intern->std TSRMLS_CC);
2501    efree(object);
2502}
2503
2504static void date_object_free_storage_interval(void *object TSRMLS_DC)
2505{
2506    php_interval_obj *intern = (php_interval_obj *)object;
2507
2508    timelib_rel_time_dtor(intern->diff);
2509    zend_object_std_dtor(&intern->std TSRMLS_CC);
2510    efree(object);
2511}
2512
2513static void date_object_free_storage_period(void *object TSRMLS_DC)
2514{
2515    php_period_obj *intern = (php_period_obj *)object;
2516
2517    if (intern->start) {
2518        timelib_time_dtor(intern->start);
2519    }
2520
2521    if (intern->current) {
2522        timelib_time_dtor(intern->current);
2523    }
2524
2525    if (intern->end) {
2526        timelib_time_dtor(intern->end);
2527    }
2528
2529    timelib_rel_time_dtor(intern->interval);
2530    zend_object_std_dtor(&intern->std TSRMLS_CC);
2531    efree(object);
2532}
2533
2534/* Advanced Interface */
2535PHPAPI zval *php_date_instantiate(zend_class_entry *pce, zval *object TSRMLS_DC)
2536{
2537    Z_TYPE_P(object) = IS_OBJECT;
2538    object_init_ex(object, pce);
2539    Z_SET_REFCOUNT_P(object, 1);
2540    Z_UNSET_ISREF_P(object);
2541
2542    return object;
2543}
2544
2545/* Helper function used to store the latest found warnings and errors while
2546 * parsing, from either strtotime or parse_from_format. */
2547static void update_errors_warnings(timelib_error_container *last_errors TSRMLS_DC)
2548{
2549    if (DATEG(last_errors)) {
2550        timelib_error_container_dtor(DATEG(last_errors));
2551        DATEG(last_errors) = NULL;
2552    }
2553    DATEG(last_errors) = last_errors;
2554}
2555
2556PHPAPI int php_date_initialize(php_date_obj *dateobj, /*const*/ char *time_str, int time_str_len, char *format, zval *timezone_object, int ctor TSRMLS_DC)
2557{
2558    timelib_time   *now;
2559    timelib_tzinfo *tzi = NULL;
2560    timelib_error_container *err = NULL;
2561    int type = TIMELIB_ZONETYPE_ID, new_dst = 0;
2562    char *new_abbr = NULL;
2563    timelib_sll     new_offset;
2564
2565    if (dateobj->time) {
2566        timelib_time_dtor(dateobj->time);
2567    }
2568    if (format) {
2569        dateobj->time = timelib_parse_from_format(format, time_str_len ? time_str : "", time_str_len ? time_str_len : 0, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2570    } else {
2571        dateobj->time = timelib_strtotime(time_str_len ? time_str : "now", time_str_len ? time_str_len : sizeof("now") -1, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2572    }
2573
2574    /* update last errors and warnings */
2575    update_errors_warnings(err TSRMLS_CC);
2576
2577
2578    if (ctor && err && err->error_count) {
2579        /* spit out the first library error message, at least */
2580        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", time_str,
2581            err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message);
2582    }
2583    if (err && err->error_count) {
2584        timelib_time_dtor(dateobj->time);
2585        dateobj->time = 0;
2586        return 0;
2587    }
2588
2589    if (timezone_object) {
2590        php_timezone_obj *tzobj;
2591
2592        tzobj = (php_timezone_obj *) zend_object_store_get_object(timezone_object TSRMLS_CC);
2593        switch (tzobj->type) {
2594            case TIMELIB_ZONETYPE_ID:
2595                tzi = tzobj->tzi.tz;
2596                break;
2597            case TIMELIB_ZONETYPE_OFFSET:
2598                new_offset = tzobj->tzi.utc_offset;
2599                break;
2600            case TIMELIB_ZONETYPE_ABBR:
2601                new_offset = tzobj->tzi.z.utc_offset;
2602                new_dst    = tzobj->tzi.z.dst;
2603                new_abbr   = strdup(tzobj->tzi.z.abbr);
2604                break;
2605        }
2606        type = tzobj->type;
2607    } else if (dateobj->time->tz_info) {
2608        tzi = dateobj->time->tz_info;
2609    } else {
2610        tzi = get_timezone_info(TSRMLS_C);
2611    }
2612
2613    now = timelib_time_ctor();
2614    now->zone_type = type;
2615    switch (type) {
2616        case TIMELIB_ZONETYPE_ID:
2617            now->tz_info = tzi;
2618            break;
2619        case TIMELIB_ZONETYPE_OFFSET:
2620            now->z = new_offset;
2621            break;
2622        case TIMELIB_ZONETYPE_ABBR:
2623            now->z = new_offset;
2624            now->dst = new_dst;
2625            now->tz_abbr = new_abbr;
2626            break;
2627    }
2628    timelib_unixtime2local(now, (timelib_sll) time(NULL));
2629
2630    timelib_fill_holes(dateobj->time, now, TIMELIB_NO_CLONE);
2631    timelib_update_ts(dateobj->time, tzi);
2632    timelib_update_from_sse(dateobj->time);
2633
2634    dateobj->time->have_relative = 0;
2635
2636    timelib_time_dtor(now);
2637
2638    return 1;
2639}
2640
2641/* {{{ proto DateTime date_create([string time[, DateTimeZone object]])
2642   Returns new DateTime object
2643*/
2644PHP_FUNCTION(date_create)
2645{
2646    zval           *timezone_object = NULL;
2647    char           *time_str = NULL;
2648    int             time_str_len = 0;
2649
2650    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
2651        RETURN_FALSE;
2652    }
2653
2654    php_date_instantiate(date_ce_date, return_value TSRMLS_CC);
2655    if (!php_date_initialize(zend_object_store_get_object(return_value TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 0 TSRMLS_CC)) {
2656        RETURN_FALSE;
2657    }
2658}
2659/* }}} */
2660
2661/* {{{ proto DateTime date_create_immutable([string time[, DateTimeZone object]])
2662   Returns new DateTime object
2663*/
2664PHP_FUNCTION(date_create_immutable)
2665{
2666    zval           *timezone_object = NULL;
2667    char           *time_str = NULL;
2668    int             time_str_len = 0;
2669
2670    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
2671        RETURN_FALSE;
2672    }
2673
2674    php_date_instantiate(date_ce_immutable, return_value TSRMLS_CC);
2675    if (!php_date_initialize(zend_object_store_get_object(return_value TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 0 TSRMLS_CC)) {
2676        RETURN_FALSE;
2677    }
2678}
2679/* }}} */
2680
2681/* {{{ proto DateTime date_create_from_format(string format, string time[, DateTimeZone object])
2682   Returns new DateTime object formatted according to the specified format
2683*/
2684PHP_FUNCTION(date_create_from_format)
2685{
2686    zval           *timezone_object = NULL;
2687    char           *time_str = NULL, *format_str = NULL;
2688    int             time_str_len = 0, format_str_len = 0;
2689
2690    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|O", &format_str, &format_str_len, &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
2691        RETURN_FALSE;
2692    }
2693
2694    php_date_instantiate(date_ce_date, return_value TSRMLS_CC);
2695    if (!php_date_initialize(zend_object_store_get_object(return_value TSRMLS_CC), time_str, time_str_len, format_str, timezone_object, 0 TSRMLS_CC)) {
2696        RETURN_FALSE;
2697    }
2698}
2699/* }}} */
2700
2701/* {{{ proto DateTime date_create_immutable_from_format(string format, string time[, DateTimeZone object])
2702   Returns new DateTime object formatted according to the specified format
2703*/
2704PHP_FUNCTION(date_create_immutable_from_format)
2705{
2706    zval           *timezone_object = NULL;
2707    char           *time_str = NULL, *format_str = NULL;
2708    int             time_str_len = 0, format_str_len = 0;
2709
2710    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|O", &format_str, &format_str_len, &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
2711        RETURN_FALSE;
2712    }
2713
2714    php_date_instantiate(date_ce_immutable, return_value TSRMLS_CC);
2715    if (!php_date_initialize(zend_object_store_get_object(return_value TSRMLS_CC), time_str, time_str_len, format_str, timezone_object, 0 TSRMLS_CC)) {
2716        RETURN_FALSE;
2717    }
2718}
2719/* }}} */
2720
2721/* {{{ proto DateTime::__construct([string time[, DateTimeZone object]])
2722   Creates new DateTime object
2723*/
2724PHP_METHOD(DateTime, __construct)
2725{
2726    zval *timezone_object = NULL;
2727    char *time_str = NULL;
2728    int time_str_len = 0;
2729    zend_error_handling error_handling;
2730
2731    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
2732    if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone)) {
2733        php_date_initialize(zend_object_store_get_object(getThis() TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 1 TSRMLS_CC);
2734    }
2735    zend_restore_error_handling(&error_handling TSRMLS_CC);
2736}
2737/* }}} */
2738
2739/* {{{ proto DateTimeImmutable::__construct([string time[, DateTimeZone object]])
2740   Creates new DateTimeImmutable object
2741*/
2742PHP_METHOD(DateTimeImmutable, __construct)
2743{
2744    zval *timezone_object = NULL;
2745    char *time_str = NULL;
2746    int time_str_len = 0;
2747    zend_error_handling error_handling;
2748
2749    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
2750    if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone)) {
2751        php_date_initialize(zend_object_store_get_object(getThis() TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 1 TSRMLS_CC);
2752    }
2753    zend_restore_error_handling(&error_handling TSRMLS_CC);
2754}
2755/* }}} */
2756
2757static int php_date_initialize_from_hash(zval **return_value, php_date_obj **dateobj, HashTable *myht TSRMLS_DC)
2758{
2759    zval            **z_date = NULL;
2760    zval            **z_timezone = NULL;
2761    zval            **z_timezone_type = NULL;
2762    zval             *tmp_obj = NULL;
2763    timelib_tzinfo   *tzi;
2764    php_timezone_obj *tzobj;
2765
2766    if (zend_hash_find(myht, "date", 5, (void**) &z_date) == SUCCESS) {
2767        convert_to_string(*z_date);
2768        if (zend_hash_find(myht, "timezone_type", 14, (void**) &z_timezone_type) == SUCCESS) {
2769            convert_to_long(*z_timezone_type);
2770            if (zend_hash_find(myht, "timezone", 9, (void**) &z_timezone) == SUCCESS) {
2771                convert_to_string(*z_timezone);
2772
2773                switch (Z_LVAL_PP(z_timezone_type)) {
2774                    case TIMELIB_ZONETYPE_OFFSET:
2775                    case TIMELIB_ZONETYPE_ABBR: {
2776                        char *tmp = emalloc(Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 2);
2777                        int ret;
2778                        snprintf(tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 2, "%s %s", Z_STRVAL_PP(z_date), Z_STRVAL_PP(z_timezone));
2779                        ret = php_date_initialize(*dateobj, tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 1, NULL, NULL, 0 TSRMLS_CC);
2780                        efree(tmp);
2781                        return 1 == ret;
2782                    }
2783
2784                    case TIMELIB_ZONETYPE_ID: {
2785                        int ret;
2786                        convert_to_string(*z_timezone);
2787
2788                        tzi = php_date_parse_tzfile(Z_STRVAL_PP(z_timezone), DATE_TIMEZONEDB TSRMLS_CC);
2789
2790                        if (tzi == NULL) {
2791                            return 0;
2792                        }
2793
2794                        ALLOC_INIT_ZVAL(tmp_obj);
2795                        tzobj = zend_object_store_get_object(php_date_instantiate(date_ce_timezone, tmp_obj TSRMLS_CC) TSRMLS_CC);
2796                        tzobj->type = TIMELIB_ZONETYPE_ID;
2797                        tzobj->tzi.tz = tzi;
2798                        tzobj->initialized = 1;
2799
2800                        ret = php_date_initialize(*dateobj, Z_STRVAL_PP(z_date), Z_STRLEN_PP(z_date), NULL, tmp_obj, 0 TSRMLS_CC);
2801                        zval_ptr_dtor(&tmp_obj);
2802                        return 1 == ret;
2803                    }
2804                }
2805            }
2806        }
2807    }
2808    return 0;
2809}
2810
2811/* {{{ proto DateTime::__set_state()
2812*/
2813PHP_METHOD(DateTime, __set_state)
2814{
2815    php_date_obj     *dateobj;
2816    zval             *array;
2817    HashTable        *myht;
2818
2819    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
2820        RETURN_FALSE;
2821    }
2822
2823    myht = HASH_OF(array);
2824
2825    php_date_instantiate(date_ce_date, return_value TSRMLS_CC);
2826    dateobj = (php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
2827    if (!php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC)) {
2828        php_error(E_ERROR, "Invalid serialization data for DateTime object");
2829    }
2830}
2831/* }}} */
2832
2833/* {{{ proto DateTimeImmutable::__set_state()
2834*/
2835PHP_METHOD(DateTimeImmutable, __set_state)
2836{
2837    php_date_obj     *dateobj;
2838    zval             *array;
2839    HashTable        *myht;
2840
2841    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
2842        RETURN_FALSE;
2843    }
2844
2845    myht = HASH_OF(array);
2846
2847    php_date_instantiate(date_ce_immutable, return_value TSRMLS_CC);
2848    dateobj = (php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
2849    if (!php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC)) {
2850        php_error(E_ERROR, "Invalid serialization data for DateTimeImmutable object");
2851    }
2852}
2853/* }}} */
2854
2855/* {{{ proto DateTime::__wakeup()
2856*/
2857PHP_METHOD(DateTime, __wakeup)
2858{
2859    zval             *object = getThis();
2860    php_date_obj     *dateobj;
2861    HashTable        *myht;
2862
2863    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
2864
2865    myht = Z_OBJPROP_P(object);
2866
2867    if (!php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC)) {
2868        php_error(E_ERROR, "Invalid serialization data for DateTime object");
2869    }
2870}
2871/* }}} */
2872
2873/* Helper function used to add an associative array of warnings and errors to a zval */
2874static void zval_from_error_container(zval *z, timelib_error_container *error)
2875{
2876    int   i;
2877    zval *element;
2878
2879    add_assoc_long(z, "warning_count", error->warning_count);
2880    MAKE_STD_ZVAL(element);
2881    array_init(element);
2882    for (i = 0; i < error->warning_count; i++) {
2883        add_index_string(element, error->warning_messages[i].position, error->warning_messages[i].message, 1);
2884    }
2885    add_assoc_zval(z, "warnings", element);
2886
2887    add_assoc_long(z, "error_count", error->error_count);
2888    MAKE_STD_ZVAL(element);
2889    array_init(element);
2890    for (i = 0; i < error->error_count; i++) {
2891        add_index_string(element, error->error_messages[i].position, error->error_messages[i].message, 1);
2892    }
2893    add_assoc_zval(z, "errors", element);
2894}
2895
2896/* {{{ proto array date_get_last_errors()
2897   Returns the warnings and errors found while parsing a date/time string.
2898*/
2899PHP_FUNCTION(date_get_last_errors)
2900{
2901    if (DATEG(last_errors)) {
2902        array_init(return_value);
2903        zval_from_error_container(return_value, DATEG(last_errors));
2904    } else {
2905        RETURN_FALSE;
2906    }
2907}
2908/* }}} */
2909
2910void php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAMETERS, timelib_time *parsed_time, struct timelib_error_container *error)
2911{
2912    zval *element;
2913
2914    array_init(return_value);
2915#define PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(name, elem) \
2916    if (parsed_time->elem == -99999) {               \
2917        add_assoc_bool(return_value, #name, 0); \
2918    } else {                                       \
2919        add_assoc_long(return_value, #name, parsed_time->elem); \
2920    }
2921    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(year,      y);
2922    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(month,     m);
2923    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(day,       d);
2924    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(hour,      h);
2925    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(minute,    i);
2926    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(second,    s);
2927
2928    if (parsed_time->f == -99999) {
2929        add_assoc_bool(return_value, "fraction", 0);
2930    } else {
2931        add_assoc_double(return_value, "fraction", parsed_time->f);
2932    }
2933
2934    zval_from_error_container(return_value, error);
2935
2936    timelib_error_container_dtor(error);
2937
2938    add_assoc_bool(return_value, "is_localtime", parsed_time->is_localtime);
2939
2940    if (parsed_time->is_localtime) {
2941        PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone_type, zone_type);
2942        switch (parsed_time->zone_type) {
2943            case TIMELIB_ZONETYPE_OFFSET:
2944                PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone, z);
2945                add_assoc_bool(return_value, "is_dst", parsed_time->dst);
2946                break;
2947            case TIMELIB_ZONETYPE_ID:
2948                if (parsed_time->tz_abbr) {
2949                    add_assoc_string(return_value, "tz_abbr", parsed_time->tz_abbr, 1);
2950                }
2951                if (parsed_time->tz_info) {
2952                    add_assoc_string(return_value, "tz_id", parsed_time->tz_info->name, 1);
2953                }
2954                break;
2955            case TIMELIB_ZONETYPE_ABBR:
2956                PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone, z);
2957                add_assoc_bool(return_value, "is_dst", parsed_time->dst);
2958                add_assoc_string(return_value, "tz_abbr", parsed_time->tz_abbr, 1);
2959                break;
2960        }
2961    }
2962    if (parsed_time->have_relative) {
2963        MAKE_STD_ZVAL(element);
2964        array_init(element);
2965        add_assoc_long(element, "year",   parsed_time->relative.y);
2966        add_assoc_long(element, "month",  parsed_time->relative.m);
2967        add_assoc_long(element, "day",    parsed_time->relative.d);
2968        add_assoc_long(element, "hour",   parsed_time->relative.h);
2969        add_assoc_long(element, "minute", parsed_time->relative.i);
2970        add_assoc_long(element, "second", parsed_time->relative.s);
2971        if (parsed_time->relative.have_weekday_relative) {
2972            add_assoc_long(element, "weekday", parsed_time->relative.weekday);
2973        }
2974        if (parsed_time->relative.have_special_relative && (parsed_time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY)) {
2975            add_assoc_long(element, "weekdays", parsed_time->relative.special.amount);
2976        }
2977        if (parsed_time->relative.first_last_day_of) {
2978            add_assoc_bool(element, parsed_time->relative.first_last_day_of == 1 ? "first_day_of_month" : "last_day_of_month", 1);
2979        }
2980        add_assoc_zval(return_value, "relative", element);
2981    }
2982    timelib_time_dtor(parsed_time);
2983}
2984
2985/* {{{ proto array date_parse(string date)
2986   Returns associative array with detailed info about given date
2987*/
2988PHP_FUNCTION(date_parse)
2989{
2990    char                           *date;
2991    int                             date_len;
2992    struct timelib_error_container *error;
2993    timelib_time                   *parsed_time;
2994
2995    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &date, &date_len) == FAILURE) {
2996        RETURN_FALSE;
2997    }
2998
2999    parsed_time = timelib_strtotime(date, date_len, &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3000    php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAM_PASSTHRU, parsed_time, error);
3001}
3002/* }}} */
3003
3004/* {{{ proto array date_parse_from_format(string format, string date)
3005   Returns associative array with detailed info about given date
3006*/
3007PHP_FUNCTION(date_parse_from_format)
3008{
3009    char                           *date, *format;
3010    int                             date_len, format_len;
3011    struct timelib_error_container *error;
3012    timelib_time                   *parsed_time;
3013
3014    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &format, &format_len, &date, &date_len) == FAILURE) {
3015        RETURN_FALSE;
3016    }
3017
3018    parsed_time = timelib_parse_from_format(format, date, date_len, &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3019    php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAM_PASSTHRU, parsed_time, error);
3020}
3021/* }}} */
3022
3023/* {{{ proto string date_format(DateTimeInterface object, string format)
3024   Returns date formatted according to given format
3025*/
3026PHP_FUNCTION(date_format)
3027{
3028    zval         *object;
3029    php_date_obj *dateobj;
3030    char         *format;
3031    int           format_len;
3032
3033    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_interface, &format, &format_len) == FAILURE) {
3034        RETURN_FALSE;
3035    }
3036    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3037    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3038    RETURN_STRING(date_format(format, format_len, dateobj->time, dateobj->time->is_localtime), 0);
3039}
3040/* }}} */
3041
3042static int php_date_modify(zval *object, char *modify, int modify_len TSRMLS_DC)
3043{
3044    php_date_obj *dateobj;
3045    timelib_time *tmp_time;
3046    timelib_error_container *err = NULL;
3047
3048    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3049
3050    if (!(dateobj->time)) {
3051        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The DateTime object has not been correctly initialized by its constructor");
3052        return 0;
3053    }
3054
3055    tmp_time = timelib_strtotime(modify, modify_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3056
3057    /* update last errors and warnings */
3058    update_errors_warnings(err TSRMLS_CC);
3059    if (err && err->error_count) {
3060        /* spit out the first library error message, at least */
3061        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", modify,
3062            err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message);
3063        timelib_time_dtor(tmp_time);
3064        return 0;
3065    }
3066
3067    memcpy(&dateobj->time->relative, &tmp_time->relative, sizeof(struct timelib_rel_time));
3068    dateobj->time->have_relative = tmp_time->have_relative;
3069    dateobj->time->sse_uptodate = 0;
3070
3071    if (tmp_time->y != -99999) {
3072        dateobj->time->y = tmp_time->y;
3073    }
3074    if (tmp_time->m != -99999) {
3075        dateobj->time->m = tmp_time->m;
3076    }
3077    if (tmp_time->d != -99999) {
3078        dateobj->time->d = tmp_time->d;
3079    }
3080
3081    if (tmp_time->h != -99999) {
3082        dateobj->time->h = tmp_time->h;
3083        if (tmp_time->i != -99999) {
3084            dateobj->time->i = tmp_time->i;
3085            if (tmp_time->s != -99999) {
3086                dateobj->time->s = tmp_time->s;
3087            } else {
3088                dateobj->time->s = 0;
3089            }
3090        } else {
3091            dateobj->time->i = 0;
3092            dateobj->time->s = 0;
3093        }
3094    }
3095    timelib_time_dtor(tmp_time);
3096
3097    timelib_update_ts(dateobj->time, NULL);
3098    timelib_update_from_sse(dateobj->time);
3099    dateobj->time->have_relative = 0;
3100
3101    return 1;
3102}
3103
3104/* {{{ proto DateTime date_modify(DateTime object, string modify)
3105   Alters the timestamp.
3106*/
3107PHP_FUNCTION(date_modify)
3108{
3109    zval         *object;
3110    char         *modify;
3111    int           modify_len;
3112
3113    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_date, &modify, &modify_len) == FAILURE) {
3114        RETURN_FALSE;
3115    }
3116
3117    if (php_date_modify(object, modify, modify_len TSRMLS_CC)) {
3118        RETURN_ZVAL(object, 1, 0);
3119    }
3120
3121    RETURN_FALSE;
3122}
3123/* }}} */
3124
3125/* {{{ proto DateTimeImmutable::modify()
3126*/
3127PHP_METHOD(DateTimeImmutable, modify)
3128{
3129    zval *object, *new_object;
3130    char *modify;
3131    int   modify_len;
3132
3133    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_immutable, &modify, &modify_len) == FAILURE) {
3134        RETURN_FALSE;
3135    }
3136
3137    new_object = date_clone_immutable(object TSRMLS_CC);
3138    if (php_date_modify(new_object, modify, modify_len TSRMLS_CC)) {
3139        RETURN_ZVAL(new_object, 0, 1);
3140    }
3141
3142    RETURN_FALSE;
3143}
3144/* }}} */
3145
3146static void php_date_add(zval *object, zval *interval, zval *return_value TSRMLS_DC)
3147{
3148    php_date_obj     *dateobj;
3149    php_interval_obj *intobj;
3150    timelib_time     *new_time;
3151
3152    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3153    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3154    intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC);
3155    DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
3156
3157    new_time = timelib_add(dateobj->time, intobj->diff);
3158    timelib_time_dtor(dateobj->time);
3159    dateobj->time = new_time;
3160}
3161
3162/* {{{ proto DateTime date_add(DateTime object, DateInterval interval)
3163   Adds an interval to the current date in object.
3164*/
3165PHP_FUNCTION(date_add)
3166{
3167    zval *object, *interval;
3168
3169    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
3170        RETURN_FALSE;
3171    }
3172
3173    php_date_add(object, interval, return_value TSRMLS_CC);
3174
3175    RETURN_ZVAL(object, 1, 0);
3176}
3177/* }}} */
3178
3179/* {{{ proto DateTimeImmutable::add()
3180*/
3181PHP_METHOD(DateTimeImmutable, add)
3182{
3183    zval *object, *interval, *new_object;
3184
3185    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_immutable, &interval, date_ce_interval) == FAILURE) {
3186        RETURN_FALSE;
3187    }
3188
3189    new_object = date_clone_immutable(object TSRMLS_CC);
3190    php_date_add(new_object, interval, return_value TSRMLS_CC);
3191
3192    RETURN_ZVAL(new_object, 0, 1);
3193}
3194/* }}} */
3195
3196static void php_date_sub(zval *object, zval *interval, zval *return_value TSRMLS_DC)
3197{
3198    php_date_obj     *dateobj;
3199    php_interval_obj *intobj;
3200    timelib_time     *new_time;
3201
3202    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3203    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3204    intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC);
3205    DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
3206
3207    if (intobj->diff->have_special_relative) {
3208        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only non-special relative time specifications are supported for subtraction");
3209        return;
3210    }
3211
3212    new_time = timelib_sub(dateobj->time, intobj->diff);
3213    timelib_time_dtor(dateobj->time);
3214    dateobj->time = new_time;
3215}
3216
3217/* {{{ proto DateTime date_sub(DateTime object, DateInterval interval)
3218   Subtracts an interval to the current date in object.
3219*/
3220PHP_FUNCTION(date_sub)
3221{
3222    zval *object, *interval;
3223
3224    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
3225        RETURN_FALSE;
3226    }
3227
3228    php_date_sub(object, interval, return_value TSRMLS_CC);
3229
3230    RETURN_ZVAL(object, 1, 0);
3231}
3232/* }}} */
3233
3234/* {{{ proto DateTimeImmutable::sub()
3235*/
3236PHP_METHOD(DateTimeImmutable, sub)
3237{
3238    zval *object, *interval, *new_object;
3239
3240    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_immutable, &interval, date_ce_interval) == FAILURE) {
3241        RETURN_FALSE;
3242    }
3243
3244    new_object = date_clone_immutable(object TSRMLS_CC);
3245    php_date_sub(new_object, interval, return_value TSRMLS_CC);
3246
3247    RETURN_ZVAL(new_object, 0, 1);
3248}
3249/* }}} */
3250
3251static void set_timezone_from_timelib_time(php_timezone_obj *tzobj, timelib_time *t)
3252{
3253       tzobj->initialized = 1;
3254       tzobj->type = t->zone_type;
3255       switch (t->zone_type) {
3256               case TIMELIB_ZONETYPE_ID:
3257                       tzobj->tzi.tz = t->tz_info;
3258                       break;
3259               case TIMELIB_ZONETYPE_OFFSET:
3260                       tzobj->tzi.utc_offset = t->z;
3261                       break;
3262               case TIMELIB_ZONETYPE_ABBR:
3263                       tzobj->tzi.z.utc_offset = t->z;
3264                       tzobj->tzi.z.dst = t->dst;
3265                       tzobj->tzi.z.abbr = strdup(t->tz_abbr);
3266                       break;
3267       }
3268}
3269
3270
3271/* {{{ proto DateTimeZone date_timezone_get(DateTimeInterface object)
3272   Return new DateTimeZone object relative to give DateTime
3273*/
3274PHP_FUNCTION(date_timezone_get)
3275{
3276    zval             *object;
3277    php_date_obj     *dateobj;
3278    php_timezone_obj *tzobj;
3279
3280    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, date_ce_interface) == FAILURE) {
3281        RETURN_FALSE;
3282    }
3283    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3284    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3285    if (dateobj->time->is_localtime/* && dateobj->time->tz_info*/) {
3286        php_date_instantiate(date_ce_timezone, return_value TSRMLS_CC);
3287        tzobj = (php_timezone_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
3288        set_timezone_from_timelib_time(tzobj, dateobj->time);
3289    } else {
3290        RETURN_FALSE;
3291    }
3292}
3293/* }}} */
3294
3295static void php_date_timezone_set(zval *object, zval *timezone_object, zval *return_value TSRMLS_DC)
3296{
3297    php_date_obj     *dateobj;
3298    php_timezone_obj *tzobj;
3299
3300    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3301    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3302    tzobj = (php_timezone_obj *) zend_object_store_get_object(timezone_object TSRMLS_CC);
3303
3304    switch (tzobj->type) {
3305        case TIMELIB_ZONETYPE_OFFSET:
3306            timelib_set_timezone_from_offset(dateobj->time, tzobj->tzi.utc_offset);
3307            break;
3308        case TIMELIB_ZONETYPE_ABBR:
3309            timelib_set_timezone_from_abbr(dateobj->time, tzobj->tzi.z);
3310            break;
3311        case TIMELIB_ZONETYPE_ID:
3312            timelib_set_timezone(dateobj->time, tzobj->tzi.tz);
3313            break;
3314    }
3315    timelib_unixtime2local(dateobj->time, dateobj->time->sse);
3316}
3317
3318/* {{{ proto DateTime date_timezone_set(DateTime object, DateTimeZone object)
3319   Sets the timezone for the DateTime object.
3320*/
3321PHP_FUNCTION(date_timezone_set)
3322{
3323    zval *object;
3324    zval *timezone_object;
3325
3326    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &timezone_object, date_ce_timezone) == FAILURE) {
3327        RETURN_FALSE;
3328    }
3329
3330    php_date_timezone_set(object, timezone_object, return_value TSRMLS_CC);
3331
3332    RETURN_ZVAL(object, 1, 0);
3333}
3334/* }}} */
3335
3336/* {{{ proto DateTimeImmutable::setTimezone()
3337*/
3338PHP_METHOD(DateTimeImmutable, setTimezone)
3339{
3340    zval *object, *new_object;
3341    zval *timezone_object;
3342
3343    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_immutable, &timezone_object, date_ce_timezone) == FAILURE) {
3344        RETURN_FALSE;
3345    }
3346
3347    new_object = date_clone_immutable(object TSRMLS_CC);
3348    php_date_timezone_set(new_object, timezone_object, return_value TSRMLS_CC);
3349
3350    RETURN_ZVAL(new_object, 0, 1);
3351}
3352/* }}} */
3353
3354/* {{{ proto long date_offset_get(DateTimeInterface object)
3355   Returns the DST offset.
3356*/
3357PHP_FUNCTION(date_offset_get)
3358{
3359    zval                *object;
3360    php_date_obj        *dateobj;
3361    timelib_time_offset *offset;
3362
3363    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, date_ce_interface) == FAILURE) {
3364        RETURN_FALSE;
3365    }
3366    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3367    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3368    if (dateobj->time->is_localtime/* && dateobj->time->tz_info*/) {
3369        switch (dateobj->time->zone_type) {
3370            case TIMELIB_ZONETYPE_ID:
3371                offset = timelib_get_time_zone_info(dateobj->time->sse, dateobj->time->tz_info);
3372                RETVAL_LONG(offset->offset);
3373                timelib_time_offset_dtor(offset);
3374                break;
3375            case TIMELIB_ZONETYPE_OFFSET:
3376                RETVAL_LONG(dateobj->time->z * -60);
3377                break;
3378            case TIMELIB_ZONETYPE_ABBR:
3379                RETVAL_LONG((dateobj->time->z - (60 * dateobj->time->dst)) * -60);
3380                break;
3381        }
3382        return;
3383    } else {
3384        RETURN_LONG(0);
3385    }
3386}
3387/* }}} */
3388
3389static void php_date_time_set(zval *object, long h, long i, long s, zval *return_value TSRMLS_DC)
3390{
3391    php_date_obj *dateobj;
3392
3393    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3394    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3395    dateobj->time->h = h;
3396    dateobj->time->i = i;
3397    dateobj->time->s = s;
3398    timelib_update_ts(dateobj->time, NULL);
3399}
3400
3401/* {{{ proto DateTime date_time_set(DateTime object, long hour, long minute[, long second])
3402   Sets the time.
3403*/
3404PHP_FUNCTION(date_time_set)
3405{
3406    zval *object;
3407    long  h, i, s = 0;
3408
3409    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_date, &h, &i, &s) == FAILURE) {
3410        RETURN_FALSE;
3411    }
3412
3413    php_date_time_set(object, h, i, s, return_value TSRMLS_CC);
3414
3415    RETURN_ZVAL(object, 1, 0);
3416}
3417/* }}} */
3418
3419/* {{{ proto DateTimeImmutable::setTime()
3420*/
3421PHP_METHOD(DateTimeImmutable, setTime)
3422{
3423    zval *object, *new_object;
3424    long  h, i, s = 0;
3425
3426    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_immutable, &h, &i, &s) == FAILURE) {
3427        RETURN_FALSE;
3428    }
3429
3430    new_object = date_clone_immutable(object TSRMLS_CC);
3431    php_date_time_set(new_object, h, i, s, return_value TSRMLS_CC);
3432
3433    RETURN_ZVAL(new_object, 0, 1);
3434}
3435/* }}} */
3436
3437static void php_date_date_set(zval *object, long y, long m, long d, zval *return_value TSRMLS_DC)
3438{
3439    php_date_obj *dateobj;
3440
3441    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3442    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3443    dateobj->time->y = y;
3444    dateobj->time->m = m;
3445    dateobj->time->d = d;
3446    timelib_update_ts(dateobj->time, NULL);
3447}
3448
3449/* {{{ proto DateTime date_date_set(DateTime object, long year, long month, long day)
3450   Sets the date.
3451*/
3452PHP_FUNCTION(date_date_set)
3453{
3454    zval *object;
3455    long  y, m, d;
3456
3457    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Olll", &object, date_ce_date, &y, &m, &d) == FAILURE) {
3458        RETURN_FALSE;
3459    }
3460
3461    php_date_date_set(object, y, m, d, return_value TSRMLS_CC);
3462
3463    RETURN_ZVAL(object, 1, 0);
3464}
3465/* }}} */
3466
3467/* {{{ proto DateTimeImmutable::setDate()
3468*/
3469PHP_METHOD(DateTimeImmutable, setDate)
3470{
3471    zval *object, *new_object;
3472    long  y, m, d;
3473
3474    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Olll", &object, date_ce_immutable, &y, &m, &d) == FAILURE) {
3475        RETURN_FALSE;
3476    }
3477
3478    new_object = date_clone_immutable(object TSRMLS_CC);
3479    php_date_date_set(new_object, y, m, d, return_value TSRMLS_CC);
3480
3481    RETURN_ZVAL(new_object, 0, 1);
3482}
3483/* }}} */
3484
3485static void php_date_isodate_set(zval *object, long y, long w, long d, zval *return_value TSRMLS_DC)
3486{
3487    php_date_obj *dateobj;
3488
3489    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3490    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3491    dateobj->time->y = y;
3492    dateobj->time->m = 1;
3493    dateobj->time->d = 1;
3494    memset(&dateobj->time->relative, 0, sizeof(dateobj->time->relative));
3495    dateobj->time->relative.d = timelib_daynr_from_weeknr(y, w, d);
3496    dateobj->time->have_relative = 1;
3497
3498    timelib_update_ts(dateobj->time, NULL);
3499}
3500
3501/* {{{ proto DateTime date_isodate_set(DateTime object, long year, long week[, long day])
3502   Sets the ISO date.
3503*/
3504PHP_FUNCTION(date_isodate_set)
3505{
3506    zval *object;
3507    long  y, w, d = 1;
3508
3509    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_date, &y, &w, &d) == FAILURE) {
3510        RETURN_FALSE;
3511    }
3512
3513    php_date_isodate_set(object, y, w, d, return_value TSRMLS_CC);
3514
3515    RETURN_ZVAL(object, 1, 0);
3516}
3517/* }}} */
3518
3519/* {{{ proto DateTimeImmutable::setISODate()
3520*/
3521PHP_METHOD(DateTimeImmutable, setISODate)
3522{
3523    zval *object, *new_object;
3524    long  y, w, d = 1;
3525
3526    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_immutable, &y, &w, &d) == FAILURE) {
3527        RETURN_FALSE;
3528    }
3529
3530    new_object = date_clone_immutable(object TSRMLS_CC);
3531    php_date_isodate_set(new_object, y, w, d, return_value TSRMLS_CC);
3532
3533    RETURN_ZVAL(new_object, 0, 1);
3534}
3535/* }}} */
3536
3537static void php_date_timestamp_set(zval *object, long timestamp, zval *return_value TSRMLS_DC)
3538{
3539    php_date_obj *dateobj;
3540
3541    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3542    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3543    timelib_unixtime2local(dateobj->time, (timelib_sll)timestamp);
3544    timelib_update_ts(dateobj->time, NULL);
3545}
3546
3547/* {{{ proto DateTime date_timestamp_set(DateTime object, long unixTimestamp)
3548   Sets the date and time based on an Unix timestamp.
3549*/
3550PHP_FUNCTION(date_timestamp_set)
3551{
3552    zval *object;
3553    long  timestamp;
3554
3555    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &object, date_ce_date, &timestamp) == FAILURE) {
3556        RETURN_FALSE;
3557    }
3558
3559    php_date_timestamp_set(object, timestamp, return_value TSRMLS_CC);
3560
3561    RETURN_ZVAL(object, 1, 0);
3562}
3563/* }}} */
3564
3565/* {{{ proto DateTimeImmutable::setTimestamp()
3566*/
3567PHP_METHOD(DateTimeImmutable, setTimestamp)
3568{
3569    zval *object, *new_object;
3570    long  timestamp;
3571
3572    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &object, date_ce_immutable, &timestamp) == FAILURE) {
3573        RETURN_FALSE;
3574    }
3575
3576    new_object = date_clone_immutable(object TSRMLS_CC);
3577    php_date_timestamp_set(new_object, timestamp, return_value TSRMLS_CC);
3578
3579    RETURN_ZVAL(new_object, 0, 1);
3580}
3581/* }}} */
3582
3583/* {{{ proto long date_timestamp_get(DateTimeInterface object)
3584   Gets the Unix timestamp.
3585*/
3586PHP_FUNCTION(date_timestamp_get)
3587{
3588    zval         *object;
3589    php_date_obj *dateobj;
3590    long          timestamp;
3591    int           error;
3592
3593    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, date_ce_interface) == FAILURE) {
3594        RETURN_FALSE;
3595    }
3596    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3597    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3598    timelib_update_ts(dateobj->time, NULL);
3599
3600    timestamp = timelib_date_to_int(dateobj->time, &error);
3601    if (error) {
3602        RETURN_FALSE;
3603    } else {
3604        RETVAL_LONG(timestamp);
3605    }
3606}
3607/* }}} */
3608
3609/* {{{ proto DateInterval date_diff(DateTime object [, bool absolute])
3610   Returns the difference between two DateTime objects.
3611*/
3612PHP_FUNCTION(date_diff)
3613{
3614    zval         *object1, *object2;
3615    php_date_obj *dateobj1, *dateobj2;
3616    php_interval_obj *interval;
3617    long          absolute = 0;
3618
3619    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO|l", &object1, date_ce_interface, &object2, date_ce_interface, &absolute) == FAILURE) {
3620        RETURN_FALSE;
3621    }
3622    dateobj1 = (php_date_obj *) zend_object_store_get_object(object1 TSRMLS_CC);
3623    dateobj2 = (php_date_obj *) zend_object_store_get_object(object2 TSRMLS_CC);
3624    DATE_CHECK_INITIALIZED(dateobj1->time, DateTimeInterface);
3625    DATE_CHECK_INITIALIZED(dateobj2->time, DateTimeInterface);
3626    timelib_update_ts(dateobj1->time, NULL);
3627    timelib_update_ts(dateobj2->time, NULL);
3628
3629    php_date_instantiate(date_ce_interval, return_value TSRMLS_CC);
3630    interval = zend_object_store_get_object(return_value TSRMLS_CC);
3631    interval->diff = timelib_diff(dateobj1->time, dateobj2->time);
3632    if (absolute) {
3633        interval->diff->invert = 0;
3634    }
3635    interval->initialized = 1;
3636}
3637/* }}} */
3638
3639static int timezone_initialize(php_timezone_obj *tzobj, /*const*/ char *tz TSRMLS_DC)
3640{
3641    timelib_time *dummy_t = ecalloc(1, sizeof(timelib_time));
3642    int           dst, not_found;
3643    char         *orig_tz = tz;
3644
3645    dummy_t->z = timelib_parse_zone(&tz, &dst, dummy_t, &not_found, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3646    if (not_found) {
3647        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown or bad timezone (%s)", orig_tz);
3648        efree(dummy_t);
3649        return FAILURE;
3650    } else {
3651        set_timezone_from_timelib_time(tzobj, dummy_t);
3652        efree(dummy_t);
3653        return SUCCESS;
3654    }
3655}
3656
3657/* {{{ proto DateTimeZone timezone_open(string timezone)
3658   Returns new DateTimeZone object
3659*/
3660PHP_FUNCTION(timezone_open)
3661{
3662    char *tz;
3663    int   tz_len;
3664    php_timezone_obj *tzobj;
3665
3666    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &tz, &tz_len) == FAILURE) {
3667        RETURN_FALSE;
3668    }
3669    tzobj = zend_object_store_get_object(php_date_instantiate(date_ce_timezone, return_value TSRMLS_CC) TSRMLS_CC);
3670    if (SUCCESS != timezone_initialize(tzobj, tz TSRMLS_CC)) {
3671        RETURN_FALSE;
3672    }
3673}
3674/* }}} */
3675
3676/* {{{ proto DateTimeZone::__construct(string timezone)
3677   Creates new DateTimeZone object.
3678*/
3679PHP_METHOD(DateTimeZone, __construct)
3680{
3681    char *tz;
3682    int tz_len;
3683    php_timezone_obj *tzobj;
3684    zend_error_handling error_handling;
3685
3686    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
3687    if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &tz, &tz_len)) {
3688        tzobj = zend_object_store_get_object(getThis() TSRMLS_CC);
3689        if (FAILURE == timezone_initialize(tzobj, tz TSRMLS_CC)) {
3690            ZVAL_NULL(getThis());
3691        }
3692    }
3693    zend_restore_error_handling(&error_handling TSRMLS_CC);
3694}
3695/* }}} */
3696
3697static int php_date_timezone_initialize_from_hash(zval **return_value, php_timezone_obj **tzobj, HashTable *myht TSRMLS_DC)
3698{
3699    zval            **z_timezone = NULL;
3700    zval            **z_timezone_type = NULL;
3701
3702    if (zend_hash_find(myht, "timezone_type", 14, (void**) &z_timezone_type) == SUCCESS) {
3703        if (zend_hash_find(myht, "timezone", 9, (void**) &z_timezone) == SUCCESS) {
3704            convert_to_long(*z_timezone_type);
3705            if (SUCCESS == timezone_initialize(*tzobj, Z_STRVAL_PP(z_timezone) TSRMLS_CC)) {
3706                return SUCCESS;
3707            }
3708        }
3709    }
3710    return FAILURE;
3711}
3712
3713/* {{{ proto DateTimeZone::__set_state()
3714 *  */
3715PHP_METHOD(DateTimeZone, __set_state)
3716{
3717    php_timezone_obj *tzobj;
3718    zval             *array;
3719    HashTable        *myht;
3720
3721    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
3722        RETURN_FALSE;
3723    }
3724
3725    myht = HASH_OF(array);
3726
3727    php_date_instantiate(date_ce_timezone, return_value TSRMLS_CC);
3728    tzobj = (php_timezone_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
3729    php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht TSRMLS_CC);
3730}
3731/* }}} */
3732
3733/* {{{ proto DateTimeZone::__wakeup()
3734 *  */
3735PHP_METHOD(DateTimeZone, __wakeup)
3736{
3737    zval             *object = getThis();
3738    php_timezone_obj *tzobj;
3739    HashTable        *myht;
3740
3741    tzobj = (php_timezone_obj *) zend_object_store_get_object(object TSRMLS_CC);
3742
3743    myht = Z_OBJPROP_P(object);
3744
3745    php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht TSRMLS_CC);
3746}
3747/* }}} */
3748
3749/* {{{ proto string timezone_name_get(DateTimeZone object)
3750   Returns the name of the timezone.
3751*/
3752PHP_FUNCTION(timezone_name_get)
3753{
3754    zval             *object;
3755    php_timezone_obj *tzobj;
3756
3757    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, date_ce_timezone) == FAILURE) {
3758        RETURN_FALSE;
3759    }
3760    tzobj = (php_timezone_obj *) zend_object_store_get_object(object TSRMLS_CC);
3761    DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3762
3763    switch (tzobj->type) {
3764        case TIMELIB_ZONETYPE_ID:
3765            RETURN_STRING(tzobj->tzi.tz->name, 1);
3766            break;
3767        case TIMELIB_ZONETYPE_OFFSET: {
3768            char *tmpstr = emalloc(sizeof("UTC+05:00"));
3769            timelib_sll utc_offset = tzobj->tzi.utc_offset;
3770
3771            snprintf(tmpstr, sizeof("+05:00"), "%c%02d:%02d",
3772                utc_offset > 0 ? '-' : '+',
3773                abs(utc_offset / 60),
3774                abs((utc_offset % 60)));
3775
3776            RETURN_STRING(tmpstr, 0);
3777            }
3778            break;
3779        case TIMELIB_ZONETYPE_ABBR:
3780            RETURN_STRING(tzobj->tzi.z.abbr, 1);
3781            break;
3782    }
3783}
3784/* }}} */
3785
3786/* {{{ proto string timezone_name_from_abbr(string abbr[, long gmtOffset[, long isdst]])
3787   Returns the timezone name from abbrevation
3788*/
3789PHP_FUNCTION(timezone_name_from_abbr)
3790{
3791    char    *abbr;
3792    char    *tzid;
3793    int      abbr_len;
3794    long     gmtoffset = -1;
3795    long     isdst = -1;
3796
3797    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &abbr, &abbr_len, &gmtoffset, &isdst) == FAILURE) {
3798        RETURN_FALSE;
3799    }
3800    tzid = timelib_timezone_id_from_abbr(abbr, gmtoffset, isdst);
3801
3802    if (tzid) {
3803        RETURN_STRING(tzid, 1);
3804    } else {
3805        RETURN_FALSE;
3806    }
3807}
3808/* }}} */
3809
3810/* {{{ proto long timezone_offset_get(DateTimeZone object, DateTime object)
3811   Returns the timezone offset.
3812*/
3813PHP_FUNCTION(timezone_offset_get)
3814{
3815    zval                *object, *dateobject;
3816    php_timezone_obj    *tzobj;
3817    php_date_obj        *dateobj;
3818    timelib_time_offset *offset;
3819
3820    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_timezone, &dateobject, date_ce_date) == FAILURE) {
3821        RETURN_FALSE;
3822    }
3823    tzobj = (php_timezone_obj *) zend_object_store_get_object(object TSRMLS_CC);
3824    DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3825    dateobj = (php_date_obj *) zend_object_store_get_object(dateobject TSRMLS_CC);
3826    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3827
3828    switch (tzobj->type) {
3829        case TIMELIB_ZONETYPE_ID:
3830            offset = timelib_get_time_zone_info(dateobj->time->sse, tzobj->tzi.tz);
3831            RETVAL_LONG(offset->offset);
3832            timelib_time_offset_dtor(offset);
3833            break;
3834        case TIMELIB_ZONETYPE_OFFSET:
3835            RETURN_LONG(tzobj->tzi.utc_offset * -60);
3836            break;
3837        case TIMELIB_ZONETYPE_ABBR:
3838            RETURN_LONG((tzobj->tzi.z.utc_offset - (tzobj->tzi.z.dst*60)) * -60);
3839            break;
3840    }
3841}
3842/* }}} */
3843
3844/* {{{ proto array timezone_transitions_get(DateTimeZone object [, long timestamp_begin [, long timestamp_end ]])
3845   Returns numerically indexed array containing associative array for all transitions in the specified range for the timezone.
3846*/
3847PHP_FUNCTION(timezone_transitions_get)
3848{
3849    zval                *object, *element;
3850    php_timezone_obj    *tzobj;
3851    unsigned int         i, begin = 0, found;
3852    long                 timestamp_begin = LONG_MIN, timestamp_end = LONG_MAX;
3853
3854    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|ll", &object, date_ce_timezone, &timestamp_begin, &timestamp_end) == FAILURE) {
3855        RETURN_FALSE;
3856    }
3857    tzobj = (php_timezone_obj *) zend_object_store_get_object(object TSRMLS_CC);
3858    DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3859    if (tzobj->type != TIMELIB_ZONETYPE_ID) {
3860        RETURN_FALSE;
3861    }
3862
3863#define add_nominal() \
3864        MAKE_STD_ZVAL(element); \
3865        array_init(element); \
3866        add_assoc_long(element, "ts",     timestamp_begin); \
3867        add_assoc_string(element, "time", php_format_date(DATE_FORMAT_ISO8601, 13, timestamp_begin, 0 TSRMLS_CC), 0); \
3868        add_assoc_long(element, "offset", tzobj->tzi.tz->type[0].offset); \
3869        add_assoc_bool(element, "isdst",  tzobj->tzi.tz->type[0].isdst); \
3870        add_assoc_string(element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[0].abbr_idx], 1); \
3871        add_next_index_zval(return_value, element);
3872
3873#define add(i,ts) \
3874        MAKE_STD_ZVAL(element); \
3875        array_init(element); \
3876        add_assoc_long(element, "ts",     ts); \
3877        add_assoc_string(element, "time", php_format_date(DATE_FORMAT_ISO8601, 13, ts, 0 TSRMLS_CC), 0); \
3878        add_assoc_long(element, "offset", tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].offset); \
3879        add_assoc_bool(element, "isdst",  tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].isdst); \
3880        add_assoc_string(element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].abbr_idx], 1); \
3881        add_next_index_zval(return_value, element);
3882
3883#define add_last() add(tzobj->tzi.tz->timecnt - 1, timestamp_begin)
3884
3885    array_init(return_value);
3886
3887    if (timestamp_begin == LONG_MIN) {
3888        add_nominal();
3889        begin = 0;
3890        found = 1;
3891    } else {
3892        begin = 0;
3893        found = 0;
3894        if (tzobj->tzi.tz->timecnt > 0) {
3895            do {
3896                if (tzobj->tzi.tz->trans[begin] > timestamp_begin) {
3897                    if (begin > 0) {
3898                        add(begin - 1, timestamp_begin);
3899                    } else {
3900                        add_nominal();
3901                    }
3902                    found = 1;
3903                    break;
3904                }
3905                begin++;
3906            } while (begin < tzobj->tzi.tz->timecnt);
3907        }
3908    }
3909
3910    if (!found) {
3911        if (tzobj->tzi.tz->timecnt > 0) {
3912            add_last();
3913        } else {
3914            add_nominal();
3915        }
3916    } else {
3917        for (i = begin; i < tzobj->tzi.tz->timecnt; ++i) {
3918            if (tzobj->tzi.tz->trans[i] < timestamp_end) {
3919                add(i, tzobj->tzi.tz->trans[i]);
3920            }
3921        }
3922    }
3923}
3924/* }}} */
3925
3926/* {{{ proto array timezone_location_get()
3927   Returns location information for a timezone, including country code, latitude/longitude and comments
3928*/
3929PHP_FUNCTION(timezone_location_get)
3930{
3931    zval                *object;
3932    php_timezone_obj    *tzobj;
3933
3934    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, date_ce_timezone) == FAILURE) {
3935        RETURN_FALSE;
3936    }
3937    tzobj = (php_timezone_obj *) zend_object_store_get_object(object TSRMLS_CC);
3938    DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3939    if (tzobj->type != TIMELIB_ZONETYPE_ID) {
3940        RETURN_FALSE;
3941    }
3942
3943    array_init(return_value);
3944    add_assoc_string(return_value, "country_code", tzobj->tzi.tz->location.country_code, 1);
3945    add_assoc_double(return_value, "latitude", tzobj->tzi.tz->location.latitude);
3946    add_assoc_double(return_value, "longitude", tzobj->tzi.tz->location.longitude);
3947    add_assoc_string(return_value, "comments", tzobj->tzi.tz->location.comments, 1);
3948}
3949/* }}} */
3950
3951static int date_interval_initialize(timelib_rel_time **rt, /*const*/ char *format, int format_length TSRMLS_DC)
3952{
3953    timelib_time     *b = NULL, *e = NULL;
3954    timelib_rel_time *p = NULL;
3955    int               r = 0;
3956    int               retval = 0;
3957    struct timelib_error_container *errors;
3958
3959    timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);
3960
3961    if (errors->error_count > 0) {
3962        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown or bad format (%s)", format);
3963        retval = FAILURE;
3964    } else {
3965        if(p) {
3966            *rt = p;
3967            retval = SUCCESS;
3968        } else {
3969            if(b && e) {
3970                timelib_update_ts(b, NULL);
3971                timelib_update_ts(e, NULL);
3972                *rt = timelib_diff(b, e);
3973                retval = SUCCESS;
3974            } else {
3975                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse interval (%s)", format);
3976                retval = FAILURE;
3977            }
3978        }
3979    }
3980    timelib_error_container_dtor(errors);
3981    return retval;
3982}
3983
3984/* {{{ date_interval_read_property */
3985zval *date_interval_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
3986{
3987    php_interval_obj *obj;
3988    zval *retval;
3989    zval tmp_member;
3990    timelib_sll value = -1;
3991
3992    if (member->type != IS_STRING) {
3993        tmp_member = *member;
3994        zval_copy_ctor(&tmp_member);
3995        convert_to_string(&tmp_member);
3996        member = &tmp_member;
3997        key = NULL;
3998    }
3999
4000    obj = (php_interval_obj *)zend_objects_get_address(object TSRMLS_CC);
4001
4002    if (!obj->initialized) {
4003        retval = (zend_get_std_object_handlers())->read_property(object, member, type, key TSRMLS_CC);
4004        if (member == &tmp_member) {
4005            zval_dtor(member);
4006        }
4007        return retval;
4008    }
4009
4010#define GET_VALUE_FROM_STRUCT(n,m)            \
4011    if (strcmp(Z_STRVAL_P(member), m) == 0) { \
4012        value = obj->diff->n;                 \
4013        break;                                \
4014    }
4015    do {
4016        GET_VALUE_FROM_STRUCT(y, "y");
4017        GET_VALUE_FROM_STRUCT(m, "m");
4018        GET_VALUE_FROM_STRUCT(d, "d");
4019        GET_VALUE_FROM_STRUCT(h, "h");
4020        GET_VALUE_FROM_STRUCT(i, "i");
4021        GET_VALUE_FROM_STRUCT(s, "s");
4022        GET_VALUE_FROM_STRUCT(invert, "invert");
4023        GET_VALUE_FROM_STRUCT(days, "days");
4024        /* didn't find any */
4025        retval = (zend_get_std_object_handlers())->read_property(object, member, type, key TSRMLS_CC);
4026
4027        if (member == &tmp_member) {
4028            zval_dtor(member);
4029        }
4030
4031        return retval;
4032    } while(0);
4033
4034    ALLOC_INIT_ZVAL(retval);
4035    Z_SET_REFCOUNT_P(retval, 0);
4036
4037    if (value != -99999) {
4038        ZVAL_LONG(retval, value);
4039    } else {
4040        ZVAL_FALSE(retval);
4041    }
4042
4043    if (member == &tmp_member) {
4044        zval_dtor(member);
4045    }
4046
4047    return retval;
4048}
4049/* }}} */
4050
4051/* {{{ date_interval_write_property */
4052void date_interval_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
4053{
4054    php_interval_obj *obj;
4055    zval tmp_member, tmp_value;
4056
4057    if (member->type != IS_STRING) {
4058        tmp_member = *member;
4059        zval_copy_ctor(&tmp_member);
4060        convert_to_string(&tmp_member);
4061        member = &tmp_member;
4062        key = NULL;
4063    }
4064
4065    obj = (php_interval_obj *)zend_objects_get_address(object TSRMLS_CC);
4066
4067    if (!obj->initialized) {
4068        (zend_get_std_object_handlers())->write_property(object, member, value, key TSRMLS_CC);
4069        if (member == &tmp_member) {
4070            zval_dtor(member);
4071        }
4072        return;
4073    }
4074
4075#define SET_VALUE_FROM_STRUCT(n,m)            \
4076    if (strcmp(Z_STRVAL_P(member), m) == 0) { \
4077        if (value->type != IS_LONG) {         \
4078            tmp_value = *value;               \
4079            zval_copy_ctor(&tmp_value);       \
4080            convert_to_long(&tmp_value);      \
4081            value = &tmp_value;               \
4082        }                                     \
4083        obj->diff->n = Z_LVAL_P(value);       \
4084        if (value == &tmp_value) {            \
4085            zval_dtor(value);                 \
4086        }                                     \
4087        break;                                \
4088    }
4089
4090    do {
4091        SET_VALUE_FROM_STRUCT(y, "y");
4092        SET_VALUE_FROM_STRUCT(m, "m");
4093        SET_VALUE_FROM_STRUCT(d, "d");
4094        SET_VALUE_FROM_STRUCT(h, "h");
4095        SET_VALUE_FROM_STRUCT(i, "i");
4096        SET_VALUE_FROM_STRUCT(s, "s");
4097        SET_VALUE_FROM_STRUCT(invert, "invert");
4098        /* didn't find any */
4099        (zend_get_std_object_handlers())->write_property(object, member, value, key TSRMLS_CC);
4100    } while(0);
4101
4102    if (member == &tmp_member) {
4103        zval_dtor(member);
4104    }
4105}
4106/* }}} */
4107
4108
4109/* {{{ proto DateInterval::__construct([string interval_spec])
4110   Creates new DateInterval object.
4111*/
4112PHP_METHOD(DateInterval, __construct)
4113{
4114    char *interval_string = NULL;
4115    int   interval_string_length;
4116    php_interval_obj *diobj;
4117    timelib_rel_time *reltime;
4118    zend_error_handling error_handling;
4119
4120    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
4121    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &interval_string, &interval_string_length) == SUCCESS) {
4122        if (date_interval_initialize(&reltime, interval_string, interval_string_length TSRMLS_CC) == SUCCESS) {
4123            diobj = zend_object_store_get_object(getThis() TSRMLS_CC);
4124            diobj->diff = reltime;
4125            diobj->initialized = 1;
4126        } else {
4127            ZVAL_NULL(getThis());
4128        }
4129    }
4130    zend_restore_error_handling(&error_handling TSRMLS_CC);
4131}
4132/* }}} */
4133
4134
4135static int php_date_interval_initialize_from_hash(zval **return_value, php_interval_obj **intobj, HashTable *myht TSRMLS_DC)
4136{
4137    (*intobj)->diff = timelib_rel_time_ctor();
4138
4139#define PHP_DATE_INTERVAL_READ_PROPERTY(element, member, itype, def) \
4140    do { \
4141        zval **z_arg = NULL; \
4142        if (zend_hash_find(myht, element, strlen(element) + 1, (void**) &z_arg) == SUCCESS) { \
4143            convert_to_long(*z_arg); \
4144            (*intobj)->diff->member = (itype)Z_LVAL_PP(z_arg); \
4145        } else { \
4146            (*intobj)->diff->member = (itype)def; \
4147        } \
4148    } while (0);
4149
4150#define PHP_DATE_INTERVAL_READ_PROPERTY_I64(element, member) \
4151    do { \
4152        zval **z_arg = NULL; \
4153        if (zend_hash_find(myht, element, strlen(element) + 1, (void**) &z_arg) == SUCCESS) { \
4154            convert_to_string(*z_arg); \
4155            DATE_A64I((*intobj)->diff->member, Z_STRVAL_PP(z_arg)); \
4156        } else { \
4157            (*intobj)->diff->member = -1LL; \
4158        } \
4159    } while (0);
4160
4161    PHP_DATE_INTERVAL_READ_PROPERTY("y", y, timelib_sll, -1)
4162    PHP_DATE_INTERVAL_READ_PROPERTY("m", m, timelib_sll, -1)
4163    PHP_DATE_INTERVAL_READ_PROPERTY("d", d, timelib_sll, -1)
4164    PHP_DATE_INTERVAL_READ_PROPERTY("h", h, timelib_sll, -1)
4165    PHP_DATE_INTERVAL_READ_PROPERTY("i", i, timelib_sll, -1)
4166    PHP_DATE_INTERVAL_READ_PROPERTY("s", s, timelib_sll, -1)
4167    PHP_DATE_INTERVAL_READ_PROPERTY("weekday", weekday, int, -1)
4168    PHP_DATE_INTERVAL_READ_PROPERTY("weekday_behavior", weekday_behavior, int, -1)
4169    PHP_DATE_INTERVAL_READ_PROPERTY("first_last_day_of", first_last_day_of, int, -1)
4170    PHP_DATE_INTERVAL_READ_PROPERTY("invert", invert, int, 0);
4171    PHP_DATE_INTERVAL_READ_PROPERTY_I64("days", days);
4172    PHP_DATE_INTERVAL_READ_PROPERTY("special_type", special.type, unsigned int, 0);
4173    PHP_DATE_INTERVAL_READ_PROPERTY_I64("special_amount", special.amount);
4174    PHP_DATE_INTERVAL_READ_PROPERTY("have_weekday_relative", have_weekday_relative, unsigned int, 0);
4175    PHP_DATE_INTERVAL_READ_PROPERTY("have_special_relative", have_special_relative, unsigned int, 0);
4176    (*intobj)->initialized = 1;
4177
4178    return 0;
4179}
4180
4181/* {{{ proto DateInterval::__set_state()
4182*/
4183PHP_METHOD(DateInterval, __set_state)
4184{
4185    php_interval_obj *intobj;
4186    zval             *array;
4187    HashTable        *myht;
4188
4189    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
4190        RETURN_FALSE;
4191    }
4192
4193    myht = HASH_OF(array);
4194
4195    php_date_instantiate(date_ce_interval, return_value TSRMLS_CC);
4196    intobj = (php_interval_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
4197    php_date_interval_initialize_from_hash(&return_value, &intobj, myht TSRMLS_CC);
4198}
4199/* }}} */
4200
4201/* {{{ proto DateInterval::__wakeup()
4202*/
4203PHP_METHOD(DateInterval, __wakeup)
4204{
4205    zval             *object = getThis();
4206    php_interval_obj *intobj;
4207    HashTable        *myht;
4208
4209    intobj = (php_interval_obj *) zend_object_store_get_object(object TSRMLS_CC);
4210
4211    myht = Z_OBJPROP_P(object);
4212
4213    php_date_interval_initialize_from_hash(&return_value, &intobj, myht TSRMLS_CC);
4214}
4215/* }}} */
4216/* {{{ proto DateInterval date_interval_create_from_date_string(string time)
4217   Uses the normal date parsers and sets up a DateInterval from the relative parts of the parsed string
4218*/
4219PHP_FUNCTION(date_interval_create_from_date_string)
4220{
4221    char           *time_str = NULL;
4222    int             time_str_len = 0;
4223    timelib_time   *time;
4224    timelib_error_container *err = NULL;
4225    php_interval_obj *diobj;
4226
4227    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &time_str, &time_str_len) == FAILURE) {
4228        RETURN_FALSE;
4229    }
4230
4231    php_date_instantiate(date_ce_interval, return_value TSRMLS_CC);
4232
4233    time = timelib_strtotime(time_str, time_str_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
4234    diobj = (php_interval_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
4235    diobj->diff = timelib_rel_time_clone(&time->relative);
4236    diobj->initialized = 1;
4237    timelib_time_dtor(time);
4238    timelib_error_container_dtor(err);
4239}
4240/* }}} */
4241
4242/* {{{ date_interval_format -  */
4243static char *date_interval_format(char *format, int format_len, timelib_rel_time *t)
4244{
4245    smart_str            string = {0};
4246    int                  i, length, have_format_spec = 0;
4247    char                 buffer[33];
4248
4249    if (!format_len) {
4250        return estrdup("");
4251    }
4252
4253    for (i = 0; i < format_len; i++) {
4254        if (have_format_spec) {
4255            switch (format[i]) {
4256                case 'Y': length = slprintf(buffer, 32, "%02d", (int) t->y); break;
4257                case 'y': length = slprintf(buffer, 32, "%d", (int) t->y); break;
4258
4259                case 'M': length = slprintf(buffer, 32, "%02d", (int) t->m); break;
4260                case 'm': length = slprintf(buffer, 32, "%d", (int) t->m); break;
4261
4262                case 'D': length = slprintf(buffer, 32, "%02d", (int) t->d); break;
4263                case 'd': length = slprintf(buffer, 32, "%d", (int) t->d); break;
4264
4265                case 'H': length = slprintf(buffer, 32, "%02d", (int) t->h); break;
4266                case 'h': length = slprintf(buffer, 32, "%d", (int) t->h); break;
4267
4268                case 'I': length = slprintf(buffer, 32, "%02d", (int) t->i); break;
4269                case 'i': length = slprintf(buffer, 32, "%d", (int) t->i); break;
4270
4271                case 'S': length = slprintf(buffer, 32, "%02ld", (long) t->s); break;
4272                case 's': length = slprintf(buffer, 32, "%ld", (long) t->s); break;
4273
4274                case 'a': {
4275                    if ((int) t->days != -99999) {
4276                        length = slprintf(buffer, 32, "%d", (int) t->days);
4277                    } else {
4278                        length = slprintf(buffer, 32, "(unknown)");
4279                    }
4280                } break;
4281                case 'r': length = slprintf(buffer, 32, "%s", t->invert ? "-" : ""); break;
4282                case 'R': length = slprintf(buffer, 32, "%c", t->invert ? '-' : '+'); break;
4283
4284                case '%': length = slprintf(buffer, 32, "%%"); break;
4285                default: buffer[0] = '%'; buffer[1] = format[i]; buffer[2] = '\0'; length = 2; break;
4286            }
4287            smart_str_appendl(&string, buffer, length);
4288            have_format_spec = 0;
4289        } else {
4290            if (format[i] == '%') {
4291                have_format_spec = 1;
4292            } else {
4293                smart_str_appendc(&string, format[i]);
4294            }
4295        }
4296    }
4297
4298    smart_str_0(&string);
4299
4300    return string.c;
4301}
4302/* }}} */
4303
4304/* {{{ proto string date_interval_format(DateInterval object, string format)
4305   Formats the interval.
4306*/
4307PHP_FUNCTION(date_interval_format)
4308{
4309    zval             *object;
4310    php_interval_obj *diobj;
4311    char             *format;
4312    int               format_len;
4313
4314    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_interval, &format, &format_len) == FAILURE) {
4315        RETURN_FALSE;
4316    }
4317    diobj = (php_interval_obj *) zend_object_store_get_object(object TSRMLS_CC);
4318    DATE_CHECK_INITIALIZED(diobj->initialized, DateInterval);
4319
4320    RETURN_STRING(date_interval_format(format, format_len, diobj->diff), 0);
4321}
4322/* }}} */
4323
4324static int date_period_initialize(timelib_time **st, timelib_time **et, timelib_rel_time **d, long *recurrences, /*const*/ char *format, int format_length TSRMLS_DC)
4325{
4326    timelib_time     *b = NULL, *e = NULL;
4327    timelib_rel_time *p = NULL;
4328    int               r = 0;
4329    int               retval = 0;
4330    struct timelib_error_container *errors;
4331
4332    timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);
4333
4334    if (errors->error_count > 0) {
4335        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown or bad format (%s)", format);
4336        retval = FAILURE;
4337    } else {
4338        *st = b;
4339        *et = e;
4340        *d  = p;
4341        *recurrences = r;
4342        retval = SUCCESS;
4343    }
4344    timelib_error_container_dtor(errors);
4345    return retval;
4346}
4347
4348/* {{{ proto DatePeriod::__construct(DateTime $start, DateInterval $interval, int recurrences|DateTime $end)
4349   Creates new DatePeriod object.
4350*/
4351PHP_METHOD(DatePeriod, __construct)
4352{
4353    php_period_obj   *dpobj;
4354    php_date_obj     *dateobj;
4355    php_interval_obj *intobj;
4356    zval *start, *end = NULL, *interval;
4357    long  recurrences = 0, options = 0;
4358    char *isostr = NULL;
4359    int   isostr_len = 0;
4360    timelib_time *clone;
4361    zend_error_handling error_handling;
4362
4363    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
4364    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "OOl|l", &start, date_ce_interface, &interval, date_ce_interval, &recurrences, &options) == FAILURE) {
4365        if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "OOO|l", &start, date_ce_interface, &interval, date_ce_interval, &end, date_ce_interface, &options) == FAILURE) {
4366            if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &isostr, &isostr_len, &options) == FAILURE) {
4367                php_error_docref(NULL TSRMLS_CC, E_WARNING, "This constructor accepts either (DateTimeInterface, DateInterval, int) OR (DateTimeInterface, DateInterval, DateTime) OR (string) as arguments.");
4368                zend_restore_error_handling(&error_handling TSRMLS_CC);
4369                return;
4370            }
4371        }
4372    }
4373
4374    dpobj = zend_object_store_get_object(getThis() TSRMLS_CC);
4375    dpobj->current = NULL;
4376
4377    if (isostr) {
4378        date_period_initialize(&(dpobj->start), &(dpobj->end), &(dpobj->interval), &recurrences, isostr, isostr_len TSRMLS_CC);
4379        if (dpobj->start == NULL) {
4380            php_error_docref(NULL TSRMLS_CC, E_WARNING, "The ISO interval '%s' did not contain a start date.", isostr);
4381        }
4382        if (dpobj->interval == NULL) {
4383            php_error_docref(NULL TSRMLS_CC, E_WARNING, "The ISO interval '%s' did not contain an interval.", isostr);
4384        }
4385        if (dpobj->end == NULL && recurrences == 0) {
4386            php_error_docref(NULL TSRMLS_CC, E_WARNING, "The ISO interval '%s' did not contain an end date or a recurrence count.", isostr);
4387        }
4388
4389        if (dpobj->start) {
4390            timelib_update_ts(dpobj->start, NULL);
4391        }
4392        if (dpobj->end) {
4393            timelib_update_ts(dpobj->end, NULL);
4394        }
4395        dpobj->start_ce = date_ce_date;
4396    } else {
4397        /* init */
4398        intobj  = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC);
4399
4400        /* start date */
4401        dateobj = (php_date_obj *) zend_object_store_get_object(start TSRMLS_CC);
4402        clone = timelib_time_ctor();
4403        memcpy(clone, dateobj->time, sizeof(timelib_time));
4404        if (dateobj->time->tz_abbr) {
4405            clone->tz_abbr = strdup(dateobj->time->tz_abbr);
4406        }
4407        if (dateobj->time->tz_info) {
4408            clone->tz_info = dateobj->time->tz_info;
4409        }
4410        dpobj->start = clone;
4411        dpobj->start_ce = Z_OBJCE_P(start);
4412
4413        /* interval */
4414        dpobj->interval = timelib_rel_time_clone(intobj->diff);
4415
4416        /* end date */
4417        if (end) {
4418            dateobj = (php_date_obj *) zend_object_store_get_object(end TSRMLS_CC);
4419            clone = timelib_time_clone(dateobj->time);
4420            dpobj->end = clone;
4421        }
4422    }
4423
4424    /* options */
4425    dpobj->include_start_date = !(options & PHP_DATE_PERIOD_EXCLUDE_START_DATE);
4426
4427    /* recurrrences */
4428    dpobj->recurrences = recurrences + dpobj->include_start_date;
4429
4430    dpobj->initialized = 1;
4431
4432    zend_restore_error_handling(&error_handling TSRMLS_CC);
4433}
4434/* }}} */
4435
4436static int check_id_allowed(char *id, long what)
4437{
4438    if (what & PHP_DATE_TIMEZONE_GROUP_AFRICA     && strncasecmp(id, "Africa/",      7) == 0) return 1;
4439    if (what & PHP_DATE_TIMEZONE_GROUP_AMERICA    && strncasecmp(id, "America/",     8) == 0) return 1;
4440    if (what & PHP_DATE_TIMEZONE_GROUP_ANTARCTICA && strncasecmp(id, "Antarctica/", 11) == 0) return 1;
4441    if (what & PHP_DATE_TIMEZONE_GROUP_ARCTIC     && strncasecmp(id, "Arctic/",      7) == 0) return 1;
4442    if (what & PHP_DATE_TIMEZONE_GROUP_ASIA       && strncasecmp(id, "Asia/",        5) == 0) return 1;
4443    if (what & PHP_DATE_TIMEZONE_GROUP_ATLANTIC   && strncasecmp(id, "Atlantic/",    9) == 0) return 1;
4444    if (what & PHP_DATE_TIMEZONE_GROUP_AUSTRALIA  && strncasecmp(id, "Australia/",  10) == 0) return 1;
4445    if (what & PHP_DATE_TIMEZONE_GROUP_EUROPE     && strncasecmp(id, "Europe/",      7) == 0) return 1;
4446    if (what & PHP_DATE_TIMEZONE_GROUP_INDIAN     && strncasecmp(id, "Indian/",      7) == 0) return 1;
4447    if (what & PHP_DATE_TIMEZONE_GROUP_PACIFIC    && strncasecmp(id, "Pacific/",     8) == 0) return 1;
4448    if (what & PHP_DATE_TIMEZONE_GROUP_UTC        && strncasecmp(id, "UTC",          3) == 0) return 1;
4449    return 0;
4450}
4451
4452/* {{{ proto array timezone_identifiers_list([long what[, string country]])
4453   Returns numerically index array with all timezone identifiers.
4454*/
4455PHP_FUNCTION(timezone_identifiers_list)
4456{
4457    const timelib_tzdb             *tzdb;
4458    const timelib_tzdb_index_entry *table;
4459    int                             i, item_count;
4460    long                            what = PHP_DATE_TIMEZONE_GROUP_ALL;
4461    char                           *option = NULL;
4462    int                             option_len = 0;
4463
4464    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ls", &what, &option, &option_len) == FAILURE) {
4465        RETURN_FALSE;
4466    }
4467
4468    /* Extra validation */
4469    if (what == PHP_DATE_TIMEZONE_PER_COUNTRY && option_len != 2) {
4470        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "A two-letter ISO 3166-1 compatible country code is expected");
4471        RETURN_FALSE;
4472    }
4473
4474    tzdb = DATE_TIMEZONEDB;
4475    item_count = tzdb->index_size;
4476    table = tzdb->index;
4477
4478    array_init(return_value);
4479
4480    for (i = 0; i < item_count; ++i) {
4481        if (what == PHP_DATE_TIMEZONE_PER_COUNTRY) {
4482            if (tzdb->data[table[i].pos + 5] == option[0] && tzdb->data[table[i].pos + 6] == option[1]) {
4483                add_next_index_string(return_value, table[i].id, 1);
4484            }
4485        } else if (what == PHP_DATE_TIMEZONE_GROUP_ALL_W_BC || (check_id_allowed(table[i].id, what) && (tzdb->data[table[i].pos + 4] == '\1'))) {
4486            add_next_index_string(return_value, table[i].id, 1);
4487        }
4488    };
4489}
4490/* }}} */
4491
4492/* {{{ proto array timezone_version_get()
4493   Returns the Olson database version number.
4494*/
4495PHP_FUNCTION(timezone_version_get)
4496{
4497    const timelib_tzdb *tzdb;
4498
4499    tzdb = DATE_TIMEZONEDB;
4500    RETURN_STRING(tzdb->version, 1);
4501}
4502/* }}} */
4503
4504/* {{{ proto array timezone_abbreviations_list()
4505   Returns associative array containing dst, offset and the timezone name
4506*/
4507PHP_FUNCTION(timezone_abbreviations_list)
4508{
4509    const timelib_tz_lookup_table *table, *entry;
4510    zval                          *element, **abbr_array_pp, *abbr_array;
4511
4512    table = timelib_timezone_abbreviations_list();
4513    array_init(return_value);
4514    entry = table;
4515
4516    do {
4517        MAKE_STD_ZVAL(element);
4518        array_init(element);
4519        add_assoc_bool(element, "dst", entry->type);
4520        add_assoc_long(element, "offset", entry->gmtoffset);
4521        if (entry->full_tz_name) {
4522            add_assoc_string(element, "timezone_id", entry->full_tz_name, 1);
4523        } else {
4524            add_assoc_null(element, "timezone_id");
4525        }
4526
4527        if (zend_hash_find(HASH_OF(return_value), entry->name, strlen(entry->name) + 1, (void **) &abbr_array_pp) == FAILURE) {
4528            MAKE_STD_ZVAL(abbr_array);
4529            array_init(abbr_array);
4530            add_assoc_zval(return_value, entry->name, abbr_array);
4531        } else {
4532            abbr_array = *abbr_array_pp;
4533        }
4534        add_next_index_zval(abbr_array, element);
4535        entry++;
4536    } while (entry->name);
4537}
4538/* }}} */
4539
4540/* {{{ proto bool date_default_timezone_set(string timezone_identifier)
4541   Sets the default timezone used by all date/time functions in a script */
4542PHP_FUNCTION(date_default_timezone_set)
4543{
4544    char *zone;
4545    int   zone_len;
4546
4547    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &zone, &zone_len) == FAILURE) {
4548        RETURN_FALSE;
4549    }
4550    if (!timelib_timezone_id_is_valid(zone, DATE_TIMEZONEDB)) {
4551        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Timezone ID '%s' is invalid", zone);
4552        RETURN_FALSE;
4553    }
4554    if (DATEG(timezone)) {
4555        efree(DATEG(timezone));
4556        DATEG(timezone) = NULL;
4557    }
4558    DATEG(timezone) = estrndup(zone, zone_len);
4559    RETURN_TRUE;
4560}
4561/* }}} */
4562
4563/* {{{ proto string date_default_timezone_get()
4564   Gets the default timezone used by all date/time functions in a script */
4565PHP_FUNCTION(date_default_timezone_get)
4566{
4567    timelib_tzinfo *default_tz;
4568
4569    default_tz = get_timezone_info(TSRMLS_C);
4570    RETVAL_STRING(default_tz->name, 1);
4571}
4572/* }}} */
4573
4574/* {{{ php_do_date_sunrise_sunset
4575 *  Common for date_sunrise() and date_sunset() functions
4576 */
4577static void php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAMETERS, int calc_sunset)
4578{
4579    double latitude = 0.0, longitude = 0.0, zenith = 0.0, gmt_offset = 0, altitude;
4580    double h_rise, h_set, N;
4581    timelib_sll rise, set, transit;
4582    long time, retformat = 0;
4583    int             rs;
4584    timelib_time   *t;
4585    timelib_tzinfo *tzi;
4586    char           *retstr;
4587
4588    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|ldddd", &time, &retformat, &latitude, &longitude, &zenith, &gmt_offset) == FAILURE) {
4589        RETURN_FALSE;
4590    }
4591
4592    switch (ZEND_NUM_ARGS()) {
4593        case 1:
4594            retformat = SUNFUNCS_RET_STRING;
4595        case 2:
4596            latitude = INI_FLT("date.default_latitude");
4597        case 3:
4598            longitude = INI_FLT("date.default_longitude");
4599        case 4:
4600            if (calc_sunset) {
4601                zenith = INI_FLT("date.sunset_zenith");
4602            } else {
4603                zenith = INI_FLT("date.sunrise_zenith");
4604            }
4605        case 5:
4606        case 6:
4607            break;
4608        default:
4609            php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid format");
4610            RETURN_FALSE;
4611            break;
4612    }
4613    if (retformat != SUNFUNCS_RET_TIMESTAMP &&
4614        retformat != SUNFUNCS_RET_STRING &&
4615        retformat != SUNFUNCS_RET_DOUBLE)
4616    {
4617        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Wrong return format given, pick one of SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING or SUNFUNCS_RET_DOUBLE");
4618        RETURN_FALSE;
4619    }
4620    altitude = 90 - zenith;
4621
4622    /* Initialize time struct */
4623    t = timelib_time_ctor();
4624    tzi = get_timezone_info(TSRMLS_C);
4625    t->tz_info = tzi;
4626    t->zone_type = TIMELIB_ZONETYPE_ID;
4627
4628    if (ZEND_NUM_ARGS() <= 5) {
4629        gmt_offset = timelib_get_current_offset(t) / 3600;
4630    }
4631
4632    timelib_unixtime2local(t, time);
4633    rs = timelib_astro_rise_set_altitude(t, longitude, latitude, altitude, 1, &h_rise, &h_set, &rise, &set, &transit);
4634    timelib_time_dtor(t);
4635
4636    if (rs != 0) {
4637        RETURN_FALSE;
4638    }
4639
4640    if (retformat == SUNFUNCS_RET_TIMESTAMP) {
4641        RETURN_LONG(calc_sunset ? set : rise);
4642    }
4643    N = (calc_sunset ? h_set : h_rise) + gmt_offset;
4644
4645    if (N > 24 || N < 0) {
4646        N -= floor(N / 24) * 24;
4647    }
4648
4649    switch (retformat) {
4650        case SUNFUNCS_RET_STRING:
4651            spprintf(&retstr, 0, "%02d:%02d", (int) N, (int) (60 * (N - (int) N)));
4652            RETURN_STRINGL(retstr, 5, 0);
4653            break;
4654        case SUNFUNCS_RET_DOUBLE:
4655            RETURN_DOUBLE(N);
4656            break;
4657    }
4658}
4659/* }}} */
4660
4661/* {{{ proto mixed date_sunrise(mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]])
4662   Returns time of sunrise for a given day and location */
4663PHP_FUNCTION(date_sunrise)
4664{
4665    php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4666}
4667/* }}} */
4668
4669/* {{{ proto mixed date_sunset(mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]])
4670   Returns time of sunset for a given day and location */
4671PHP_FUNCTION(date_sunset)
4672{
4673    php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4674}
4675/* }}} */
4676
4677/* {{{ proto array date_sun_info(long time, float latitude, float longitude)
4678   Returns an array with information about sun set/rise and twilight begin/end */
4679PHP_FUNCTION(date_sun_info)
4680{
4681    long            time;
4682    double          latitude, longitude;
4683    timelib_time   *t, *t2;
4684    timelib_tzinfo *tzi;
4685    int             rs;
4686    timelib_sll     rise, set, transit;
4687    int             dummy;
4688    double          ddummy;
4689
4690    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ldd", &time, &latitude, &longitude) == FAILURE) {
4691        RETURN_FALSE;
4692    }
4693    /* Initialize time struct */
4694    t = timelib_time_ctor();
4695    tzi = get_timezone_info(TSRMLS_C);
4696    t->tz_info = tzi;
4697    t->zone_type = TIMELIB_ZONETYPE_ID;
4698    timelib_unixtime2local(t, time);
4699
4700    /* Setup */
4701    t2 = timelib_time_ctor();
4702    array_init(return_value);
4703
4704    /* Get sun up/down and transit */
4705    rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -35.0/60, 1, &ddummy, &ddummy, &rise, &set, &transit);
4706    switch (rs) {
4707        case -1: /* always below */
4708            add_assoc_bool(return_value, "sunrise", 0);
4709            add_assoc_bool(return_value, "sunset", 0);
4710            break;
4711        case 1: /* always above */
4712            add_assoc_bool(return_value, "sunrise", 1);
4713            add_assoc_bool(return_value, "sunset", 1);
4714            break;
4715        default:
4716            t2->sse = rise;
4717            add_assoc_long(return_value, "sunrise", timelib_date_to_int(t2, &dummy));
4718            t2->sse = set;
4719            add_assoc_long(return_value, "sunset", timelib_date_to_int(t2, &dummy));
4720    }
4721    t2->sse = transit;
4722    add_assoc_long(return_value, "transit", timelib_date_to_int(t2, &dummy));
4723
4724    /* Get civil twilight */
4725    rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -6.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4726    switch (rs) {
4727        case -1: /* always below */
4728            add_assoc_bool(return_value, "civil_twilight_begin", 0);
4729            add_assoc_bool(return_value, "civil_twilight_end", 0);
4730            break;
4731        case 1: /* always above */
4732            add_assoc_bool(return_value, "civil_twilight_begin", 1);
4733            add_assoc_bool(return_value, "civil_twilight_end", 1);
4734            break;
4735        default:
4736            t2->sse = rise;
4737            add_assoc_long(return_value, "civil_twilight_begin", timelib_date_to_int(t2, &dummy));
4738            t2->sse = set;
4739            add_assoc_long(return_value, "civil_twilight_end", timelib_date_to_int(t2, &dummy));
4740    }
4741
4742    /* Get nautical twilight */
4743    rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -12.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4744    switch (rs) {
4745        case -1: /* always below */
4746            add_assoc_bool(return_value, "nautical_twilight_begin", 0);
4747            add_assoc_bool(return_value, "nautical_twilight_end", 0);
4748            break;
4749        case 1: /* always above */
4750            add_assoc_bool(return_value, "nautical_twilight_begin", 1);
4751            add_assoc_bool(return_value, "nautical_twilight_end", 1);
4752            break;
4753        default:
4754            t2->sse = rise;
4755            add_assoc_long(return_value, "nautical_twilight_begin", timelib_date_to_int(t2, &dummy));
4756            t2->sse = set;
4757            add_assoc_long(return_value, "nautical_twilight_end", timelib_date_to_int(t2, &dummy));
4758    }
4759
4760    /* Get astronomical twilight */
4761    rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -18.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4762    switch (rs) {
4763        case -1: /* always below */
4764            add_assoc_bool(return_value, "astronomical_twilight_begin", 0);
4765            add_assoc_bool(return_value, "astronomical_twilight_end", 0);
4766            break;
4767        case 1: /* always above */
4768            add_assoc_bool(return_value, "astronomical_twilight_begin", 1);
4769            add_assoc_bool(return_value, "astronomical_twilight_end", 1);
4770            break;
4771        default:
4772            t2->sse = rise;
4773            add_assoc_long(return_value, "astronomical_twilight_begin", timelib_date_to_int(t2, &dummy));
4774            t2->sse = set;
4775            add_assoc_long(return_value, "astronomical_twilight_end", timelib_date_to_int(t2, &dummy));
4776    }
4777    timelib_time_dtor(t);
4778    timelib_time_dtor(t2);
4779}
4780/* }}} */
4781
4782static HashTable *date_object_get_gc_period(zval *object, zval ***table, int *n TSRMLS_DC)
4783{
4784    *table = NULL;
4785    *n = 0;
4786    return zend_std_get_properties(object TSRMLS_CC);
4787}
4788
4789static HashTable *date_object_get_properties_period(zval *object TSRMLS_DC)
4790{
4791    HashTable       *props;
4792    zval            *zv;
4793    php_period_obj  *period_obj;
4794
4795    period_obj = zend_object_store_get_object(object TSRMLS_CC);
4796
4797    props = zend_std_get_properties(object TSRMLS_CC);
4798
4799    if (!period_obj->start || GC_G(gc_active)) {
4800        return props;
4801    }
4802
4803    MAKE_STD_ZVAL(zv);
4804    if (period_obj->start) {
4805        php_date_obj *date_obj;
4806        object_init_ex(zv, date_ce_date);
4807        date_obj = zend_object_store_get_object(zv TSRMLS_CC);
4808        date_obj->time = timelib_time_clone(period_obj->start);
4809    } else {
4810        ZVAL_NULL(zv);
4811    }
4812    zend_hash_update(props, "start", sizeof("start"), &zv, sizeof(zv), NULL);
4813
4814    MAKE_STD_ZVAL(zv);
4815    if (period_obj->current) {
4816        php_date_obj *date_obj;
4817        object_init_ex(zv, date_ce_date);
4818        date_obj = zend_object_store_get_object(zv TSRMLS_CC);
4819        date_obj->time = timelib_time_clone(period_obj->current);
4820    } else {
4821        ZVAL_NULL(zv);
4822    }
4823    zend_hash_update(props, "current", sizeof("current"), &zv, sizeof(zv), NULL);
4824
4825    MAKE_STD_ZVAL(zv);
4826    if (period_obj->end) {
4827        php_date_obj *date_obj;
4828        object_init_ex(zv, date_ce_date);
4829        date_obj = zend_object_store_get_object(zv TSRMLS_CC);
4830        date_obj->time = timelib_time_clone(period_obj->end);
4831    } else {
4832        ZVAL_NULL(zv);
4833    }
4834    zend_hash_update(props, "end", sizeof("end"), &zv, sizeof(zv), NULL);
4835
4836    MAKE_STD_ZVAL(zv);
4837    if (period_obj->interval) {
4838        php_interval_obj *interval_obj;
4839        object_init_ex(zv, date_ce_interval);
4840        interval_obj = zend_object_store_get_object(zv TSRMLS_CC);
4841        interval_obj->diff = timelib_rel_time_clone(period_obj->interval);
4842        interval_obj->initialized = 1;
4843    } else {
4844        ZVAL_NULL(zv);
4845    }
4846    zend_hash_update(props, "interval", sizeof("interval"), &zv, sizeof(zv), NULL);
4847
4848    /* converted to larger type (int->long); must check when unserializing */
4849    MAKE_STD_ZVAL(zv);
4850    ZVAL_LONG(zv, (long) period_obj->recurrences);
4851    zend_hash_update(props, "recurrences", sizeof("recurrences"), &zv, sizeof(zv), NULL);
4852
4853    MAKE_STD_ZVAL(zv);
4854    ZVAL_BOOL(zv, period_obj->include_start_date);
4855    zend_hash_update(props, "include_start_date", sizeof("include_start_date"), &zv, sizeof(zv), NULL);
4856
4857    return props;
4858}
4859
4860static int php_date_period_initialize_from_hash(php_period_obj *period_obj, HashTable *myht TSRMLS_DC)
4861{
4862    zval **ht_entry;
4863
4864    /* this function does no rollback on error */
4865
4866    if (zend_hash_find(myht, "start", sizeof("start"), (void**) &ht_entry) == SUCCESS) {
4867        if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) {
4868            php_date_obj *date_obj;
4869            date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
4870            period_obj->start = timelib_time_clone(date_obj->time);
4871            period_obj->start_ce = Z_OBJCE_PP(ht_entry);
4872        } else if (Z_TYPE_PP(ht_entry) != IS_NULL) {
4873            return 0;
4874        }
4875    } else {
4876        return 0;
4877    }
4878
4879    if (zend_hash_find(myht, "end", sizeof("end"), (void**) &ht_entry) == SUCCESS) {
4880        if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) {
4881            php_date_obj *date_obj;
4882            date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
4883            period_obj->end = timelib_time_clone(date_obj->time);
4884        } else if (Z_TYPE_PP(ht_entry) != IS_NULL) {
4885            return 0;
4886        }
4887    } else {
4888        return 0;
4889    }
4890
4891    if (zend_hash_find(myht, "current", sizeof("current"), (void**) &ht_entry) == SUCCESS) {
4892        if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) {
4893            php_date_obj *date_obj;
4894            date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
4895            period_obj->current = timelib_time_clone(date_obj->time);
4896        } else if (Z_TYPE_PP(ht_entry) != IS_NULL)  {
4897            return 0;
4898        }
4899    } else {
4900        return 0;
4901    }
4902
4903    if (zend_hash_find(myht, "interval", sizeof("interval"), (void**) &ht_entry) == SUCCESS) {
4904        if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_interval) {
4905            php_interval_obj *interval_obj;
4906            interval_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
4907            period_obj->interval = timelib_rel_time_clone(interval_obj->diff);
4908        } else { /* interval is required */
4909            return 0;
4910        }
4911    } else {
4912        return 0;
4913    }
4914
4915    if (zend_hash_find(myht, "recurrences", sizeof("recurrences"), (void**) &ht_entry) == SUCCESS &&
4916            Z_TYPE_PP(ht_entry) == IS_LONG && Z_LVAL_PP(ht_entry) >= 0 && Z_LVAL_PP(ht_entry) <= INT_MAX) {
4917        period_obj->recurrences = Z_LVAL_PP(ht_entry);
4918    } else {
4919        return 0;
4920    }
4921
4922    if (zend_hash_find(myht, "include_start_date", sizeof("include_start_date"), (void**) &ht_entry) == SUCCESS &&
4923            Z_TYPE_PP(ht_entry) == IS_BOOL) {
4924        period_obj->include_start_date = Z_BVAL_PP(ht_entry);
4925    } else {
4926        return 0;
4927    }
4928
4929    period_obj->initialized = 1;
4930
4931    return 1;
4932}
4933
4934/* {{{ proto DatePeriod::__set_state()
4935*/
4936PHP_METHOD(DatePeriod, __set_state)
4937{
4938    php_period_obj   *period_obj;
4939    zval             *array;
4940    HashTable        *myht;
4941
4942    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
4943        RETURN_FALSE;
4944    }
4945
4946    myht = Z_ARRVAL_P(array);
4947
4948    object_init_ex(return_value, date_ce_period);
4949    period_obj = zend_object_store_get_object(return_value TSRMLS_CC);
4950    if (!php_date_period_initialize_from_hash(period_obj, myht TSRMLS_CC)) {
4951        php_error(E_ERROR, "Invalid serialization data for DatePeriod object");
4952    }
4953}
4954/* }}} */
4955
4956/* {{{ proto DatePeriod::__wakeup()
4957*/
4958PHP_METHOD(DatePeriod, __wakeup)
4959{
4960    zval             *object = getThis();
4961    php_period_obj   *period_obj;
4962    HashTable        *myht;
4963
4964    period_obj = zend_object_store_get_object(object TSRMLS_CC);
4965
4966    myht = Z_OBJPROP_P(object);
4967
4968    if (!php_date_period_initialize_from_hash(period_obj, myht TSRMLS_CC)) {
4969        php_error(E_ERROR, "Invalid serialization data for DatePeriod object");
4970    }
4971}
4972/* }}} */
4973
4974/* {{{ date_period_read_property */
4975static zval *date_period_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
4976{
4977    zval *zv;
4978    if (type != BP_VAR_IS && type != BP_VAR_R) {
4979        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Retrieval of DatePeriod properties for modification is unsupported");
4980    }
4981
4982    Z_OBJPROP_P(object); /* build properties hash table */
4983
4984    zv = std_object_handlers.read_property(object, member, type, key TSRMLS_CC);
4985    if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJ_HANDLER_P(zv, clone_obj)) {
4986        /* defensive copy */
4987        zend_object_value zov = Z_OBJ_HANDLER_P(zv, clone_obj)(zv TSRMLS_CC);
4988        MAKE_STD_ZVAL(zv);
4989        Z_TYPE_P(zv) = IS_OBJECT;
4990        Z_OBJVAL_P(zv) = zov;
4991    }
4992
4993    return zv;
4994}
4995/* }}} */
4996
4997/* {{{ date_period_write_property */
4998static void date_period_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
4999{
5000    php_error_docref(NULL TSRMLS_CC, E_ERROR, "Writing to DatePeriod properties is unsupported");
5001}
5002/* }}} */
5003
5004
5005/*
5006 * Local variables:
5007 * tab-width: 4
5008 * c-basic-offset: 4
5009 * End:
5010 * vim600: fdm=marker
5011 * vim: noet sw=4 ts=4
5012 */
5013