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    object_init_ex(object, pce);
2538    return object;
2539}
2540
2541/* Helper function used to store the latest found warnings and errors while
2542 * parsing, from either strtotime or parse_from_format. */
2543static void update_errors_warnings(timelib_error_container *last_errors TSRMLS_DC)
2544{
2545    if (DATEG(last_errors)) {
2546        timelib_error_container_dtor(DATEG(last_errors));
2547        DATEG(last_errors) = NULL;
2548    }
2549    DATEG(last_errors) = last_errors;
2550}
2551
2552PHPAPI 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)
2553{
2554    timelib_time   *now;
2555    timelib_tzinfo *tzi = NULL;
2556    timelib_error_container *err = NULL;
2557    int type = TIMELIB_ZONETYPE_ID, new_dst = 0;
2558    char *new_abbr = NULL;
2559    timelib_sll     new_offset;
2560
2561    if (dateobj->time) {
2562        timelib_time_dtor(dateobj->time);
2563    }
2564    if (format) {
2565        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);
2566    } else {
2567        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);
2568    }
2569
2570    /* update last errors and warnings */
2571    update_errors_warnings(err TSRMLS_CC);
2572
2573
2574    if (ctor && err && err->error_count) {
2575        /* spit out the first library error message, at least */
2576        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", time_str,
2577            err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message);
2578    }
2579    if (err && err->error_count) {
2580        timelib_time_dtor(dateobj->time);
2581        dateobj->time = 0;
2582        return 0;
2583    }
2584
2585    if (timezone_object) {
2586        php_timezone_obj *tzobj;
2587
2588        tzobj = (php_timezone_obj *) zend_object_store_get_object(timezone_object TSRMLS_CC);
2589        switch (tzobj->type) {
2590            case TIMELIB_ZONETYPE_ID:
2591                tzi = tzobj->tzi.tz;
2592                break;
2593            case TIMELIB_ZONETYPE_OFFSET:
2594                new_offset = tzobj->tzi.utc_offset;
2595                break;
2596            case TIMELIB_ZONETYPE_ABBR:
2597                new_offset = tzobj->tzi.z.utc_offset;
2598                new_dst    = tzobj->tzi.z.dst;
2599                new_abbr   = strdup(tzobj->tzi.z.abbr);
2600                break;
2601        }
2602        type = tzobj->type;
2603    } else if (dateobj->time->tz_info) {
2604        tzi = dateobj->time->tz_info;
2605    } else {
2606        tzi = get_timezone_info(TSRMLS_C);
2607    }
2608
2609    now = timelib_time_ctor();
2610    now->zone_type = type;
2611    switch (type) {
2612        case TIMELIB_ZONETYPE_ID:
2613            now->tz_info = tzi;
2614            break;
2615        case TIMELIB_ZONETYPE_OFFSET:
2616            now->z = new_offset;
2617            break;
2618        case TIMELIB_ZONETYPE_ABBR:
2619            now->z = new_offset;
2620            now->dst = new_dst;
2621            now->tz_abbr = new_abbr;
2622            break;
2623    }
2624    timelib_unixtime2local(now, (timelib_sll) time(NULL));
2625
2626    timelib_fill_holes(dateobj->time, now, TIMELIB_NO_CLONE);
2627    timelib_update_ts(dateobj->time, tzi);
2628    timelib_update_from_sse(dateobj->time);
2629
2630    dateobj->time->have_relative = 0;
2631
2632    timelib_time_dtor(now);
2633
2634    return 1;
2635}
2636
2637/* {{{ proto DateTime date_create([string time[, DateTimeZone object]])
2638   Returns new DateTime object
2639*/
2640PHP_FUNCTION(date_create)
2641{
2642    zval           *timezone_object = NULL;
2643    char           *time_str = NULL;
2644    int             time_str_len = 0;
2645    zval            datetime_object;
2646
2647    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
2648        RETURN_FALSE;
2649    }
2650
2651    php_date_instantiate(date_ce_date, &datetime_object TSRMLS_CC);
2652    if (!php_date_initialize(zend_object_store_get_object(&datetime_object TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 0 TSRMLS_CC)) {
2653        zval_dtor(&datetime_object);
2654        RETURN_FALSE;
2655    } else {
2656        zval *datetime_object_ptr = &datetime_object;
2657        RETVAL_ZVAL(datetime_object_ptr, 0, 0);
2658    }
2659}
2660/* }}} */
2661
2662/* {{{ proto DateTime date_create_immutable([string time[, DateTimeZone object]])
2663   Returns new DateTime object
2664*/
2665PHP_FUNCTION(date_create_immutable)
2666{
2667    zval           *timezone_object = NULL;
2668    char           *time_str = NULL;
2669    int             time_str_len = 0;
2670    zval            datetime_object;
2671
2672    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
2673        RETURN_FALSE;
2674    }
2675
2676    php_date_instantiate(date_ce_immutable, &datetime_object TSRMLS_CC);
2677    if (!php_date_initialize(zend_object_store_get_object(&datetime_object TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 0 TSRMLS_CC)) {
2678        zval_dtor(&datetime_object);
2679        RETURN_FALSE;
2680    } else {
2681        zval *datetime_object_ptr = &datetime_object;
2682        RETVAL_ZVAL(datetime_object_ptr, 0, 0);
2683    }
2684}
2685/* }}} */
2686
2687/* {{{ proto DateTime date_create_from_format(string format, string time[, DateTimeZone object])
2688   Returns new DateTime object formatted according to the specified format
2689*/
2690PHP_FUNCTION(date_create_from_format)
2691{
2692    zval           *timezone_object = NULL;
2693    char           *time_str = NULL, *format_str = NULL;
2694    int             time_str_len = 0, format_str_len = 0;
2695    zval            datetime_object;
2696
2697    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) {
2698        RETURN_FALSE;
2699    }
2700
2701    php_date_instantiate(date_ce_date, &datetime_object TSRMLS_CC);
2702    if (!php_date_initialize(zend_object_store_get_object(&datetime_object TSRMLS_CC), time_str, time_str_len, format_str, timezone_object, 0 TSRMLS_CC)) {
2703        zval_dtor(&datetime_object);
2704        RETURN_FALSE;
2705    } else {
2706        zval *datetime_object_ptr = &datetime_object;
2707        RETVAL_ZVAL(datetime_object_ptr, 0, 0);
2708    }
2709}
2710/* }}} */
2711
2712/* {{{ proto DateTime date_create_immutable_from_format(string format, string time[, DateTimeZone object])
2713   Returns new DateTime object formatted according to the specified format
2714*/
2715PHP_FUNCTION(date_create_immutable_from_format)
2716{
2717    zval           *timezone_object = NULL;
2718    char           *time_str = NULL, *format_str = NULL;
2719    int             time_str_len = 0, format_str_len = 0;
2720    zval            datetime_object;
2721
2722    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) {
2723        RETURN_FALSE;
2724    }
2725
2726    php_date_instantiate(date_ce_immutable, &datetime_object TSRMLS_CC);
2727    if (!php_date_initialize(zend_object_store_get_object(&datetime_object TSRMLS_CC), time_str, time_str_len, format_str, timezone_object, 0 TSRMLS_CC)) {
2728        zval_dtor(&datetime_object);
2729        RETURN_FALSE;
2730    } else {
2731        zval *datetime_object_ptr = &datetime_object;
2732        RETVAL_ZVAL(datetime_object_ptr, 0, 0);
2733    }
2734}
2735/* }}} */
2736
2737/* {{{ proto DateTime::__construct([string time[, DateTimeZone object]])
2738   Creates new DateTime object
2739*/
2740PHP_METHOD(DateTime, __construct)
2741{
2742    zval *timezone_object = NULL;
2743    char *time_str = NULL;
2744    int time_str_len = 0;
2745    zend_error_handling error_handling;
2746
2747    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
2748    if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone)) {
2749        php_date_initialize(zend_object_store_get_object(getThis() TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 1 TSRMLS_CC);
2750    }
2751    zend_restore_error_handling(&error_handling TSRMLS_CC);
2752}
2753/* }}} */
2754
2755/* {{{ proto DateTimeImmutable::__construct([string time[, DateTimeZone object]])
2756   Creates new DateTimeImmutable object
2757*/
2758PHP_METHOD(DateTimeImmutable, __construct)
2759{
2760    zval *timezone_object = NULL;
2761    char *time_str = NULL;
2762    int time_str_len = 0;
2763    zend_error_handling error_handling;
2764
2765    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
2766    if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone)) {
2767        php_date_initialize(zend_object_store_get_object(getThis() TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 1 TSRMLS_CC);
2768    }
2769    zend_restore_error_handling(&error_handling TSRMLS_CC);
2770}
2771/* }}} */
2772
2773static int php_date_initialize_from_hash(php_date_obj **dateobj, HashTable *myht TSRMLS_DC)
2774{
2775    zval            **z_date = NULL;
2776    zval            **z_timezone = NULL;
2777    zval            **z_timezone_type = NULL;
2778    zval             *tmp_obj = NULL;
2779    timelib_tzinfo   *tzi;
2780    php_timezone_obj *tzobj;
2781
2782    if (zend_hash_find(myht, "date", 5, (void**) &z_date) == SUCCESS) {
2783        convert_to_string(*z_date);
2784        if (zend_hash_find(myht, "timezone_type", 14, (void**) &z_timezone_type) == SUCCESS) {
2785            convert_to_long(*z_timezone_type);
2786            if (zend_hash_find(myht, "timezone", 9, (void**) &z_timezone) == SUCCESS) {
2787                convert_to_string(*z_timezone);
2788
2789                switch (Z_LVAL_PP(z_timezone_type)) {
2790                    case TIMELIB_ZONETYPE_OFFSET:
2791                    case TIMELIB_ZONETYPE_ABBR: {
2792                        char *tmp = emalloc(Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 2);
2793                        int ret;
2794                        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));
2795                        ret = php_date_initialize(*dateobj, tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 1, NULL, NULL, 0 TSRMLS_CC);
2796                        efree(tmp);
2797                        return 1 == ret;
2798                    }
2799
2800                    case TIMELIB_ZONETYPE_ID: {
2801                        int ret;
2802                        convert_to_string(*z_timezone);
2803
2804                        tzi = php_date_parse_tzfile(Z_STRVAL_PP(z_timezone), DATE_TIMEZONEDB TSRMLS_CC);
2805
2806                        if (tzi == NULL) {
2807                            return 0;
2808                        }
2809
2810                        ALLOC_INIT_ZVAL(tmp_obj);
2811                        tzobj = zend_object_store_get_object(php_date_instantiate(date_ce_timezone, tmp_obj TSRMLS_CC) TSRMLS_CC);
2812                        tzobj->type = TIMELIB_ZONETYPE_ID;
2813                        tzobj->tzi.tz = tzi;
2814                        tzobj->initialized = 1;
2815
2816                        ret = php_date_initialize(*dateobj, Z_STRVAL_PP(z_date), Z_STRLEN_PP(z_date), NULL, tmp_obj, 0 TSRMLS_CC);
2817                        zval_ptr_dtor(&tmp_obj);
2818                        return 1 == ret;
2819                    }
2820                }
2821            }
2822        }
2823    }
2824    return 0;
2825}
2826
2827/* {{{ proto DateTime::__set_state()
2828*/
2829PHP_METHOD(DateTime, __set_state)
2830{
2831    php_date_obj     *dateobj;
2832    zval             *array;
2833    HashTable        *myht;
2834
2835    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
2836        RETURN_FALSE;
2837    }
2838
2839    myht = HASH_OF(array);
2840
2841    php_date_instantiate(date_ce_date, return_value TSRMLS_CC);
2842    dateobj = (php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
2843    if (!php_date_initialize_from_hash(&dateobj, myht TSRMLS_CC)) {
2844        php_error(E_ERROR, "Invalid serialization data for DateTime object");
2845    }
2846}
2847/* }}} */
2848
2849/* {{{ proto DateTimeImmutable::__set_state()
2850*/
2851PHP_METHOD(DateTimeImmutable, __set_state)
2852{
2853    php_date_obj     *dateobj;
2854    zval             *array;
2855    HashTable        *myht;
2856
2857    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
2858        RETURN_FALSE;
2859    }
2860
2861    myht = HASH_OF(array);
2862
2863    php_date_instantiate(date_ce_immutable, return_value TSRMLS_CC);
2864    dateobj = (php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
2865    if (!php_date_initialize_from_hash(&dateobj, myht TSRMLS_CC)) {
2866        php_error(E_ERROR, "Invalid serialization data for DateTimeImmutable object");
2867    }
2868}
2869/* }}} */
2870
2871/* {{{ proto DateTime::__wakeup()
2872*/
2873PHP_METHOD(DateTime, __wakeup)
2874{
2875    zval             *object = getThis();
2876    php_date_obj     *dateobj;
2877    HashTable        *myht;
2878
2879    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
2880
2881    myht = Z_OBJPROP_P(object);
2882
2883    if (!php_date_initialize_from_hash(&dateobj, myht TSRMLS_CC)) {
2884        php_error(E_ERROR, "Invalid serialization data for DateTime object");
2885    }
2886}
2887/* }}} */
2888
2889/* Helper function used to add an associative array of warnings and errors to a zval */
2890static void zval_from_error_container(zval *z, timelib_error_container *error)
2891{
2892    int   i;
2893    zval *element;
2894
2895    add_assoc_long(z, "warning_count", error->warning_count);
2896    MAKE_STD_ZVAL(element);
2897    array_init(element);
2898    for (i = 0; i < error->warning_count; i++) {
2899        add_index_string(element, error->warning_messages[i].position, error->warning_messages[i].message, 1);
2900    }
2901    add_assoc_zval(z, "warnings", element);
2902
2903    add_assoc_long(z, "error_count", error->error_count);
2904    MAKE_STD_ZVAL(element);
2905    array_init(element);
2906    for (i = 0; i < error->error_count; i++) {
2907        add_index_string(element, error->error_messages[i].position, error->error_messages[i].message, 1);
2908    }
2909    add_assoc_zval(z, "errors", element);
2910}
2911
2912/* {{{ proto array date_get_last_errors()
2913   Returns the warnings and errors found while parsing a date/time string.
2914*/
2915PHP_FUNCTION(date_get_last_errors)
2916{
2917    if (DATEG(last_errors)) {
2918        array_init(return_value);
2919        zval_from_error_container(return_value, DATEG(last_errors));
2920    } else {
2921        RETURN_FALSE;
2922    }
2923}
2924/* }}} */
2925
2926void php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAMETERS, timelib_time *parsed_time, struct timelib_error_container *error)
2927{
2928    zval *element;
2929
2930    array_init(return_value);
2931#define PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(name, elem) \
2932    if (parsed_time->elem == -99999) {               \
2933        add_assoc_bool(return_value, #name, 0); \
2934    } else {                                       \
2935        add_assoc_long(return_value, #name, parsed_time->elem); \
2936    }
2937    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(year,      y);
2938    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(month,     m);
2939    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(day,       d);
2940    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(hour,      h);
2941    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(minute,    i);
2942    PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(second,    s);
2943
2944    if (parsed_time->f == -99999) {
2945        add_assoc_bool(return_value, "fraction", 0);
2946    } else {
2947        add_assoc_double(return_value, "fraction", parsed_time->f);
2948    }
2949
2950    zval_from_error_container(return_value, error);
2951
2952    timelib_error_container_dtor(error);
2953
2954    add_assoc_bool(return_value, "is_localtime", parsed_time->is_localtime);
2955
2956    if (parsed_time->is_localtime) {
2957        PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone_type, zone_type);
2958        switch (parsed_time->zone_type) {
2959            case TIMELIB_ZONETYPE_OFFSET:
2960                PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone, z);
2961                add_assoc_bool(return_value, "is_dst", parsed_time->dst);
2962                break;
2963            case TIMELIB_ZONETYPE_ID:
2964                if (parsed_time->tz_abbr) {
2965                    add_assoc_string(return_value, "tz_abbr", parsed_time->tz_abbr, 1);
2966                }
2967                if (parsed_time->tz_info) {
2968                    add_assoc_string(return_value, "tz_id", parsed_time->tz_info->name, 1);
2969                }
2970                break;
2971            case TIMELIB_ZONETYPE_ABBR:
2972                PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone, z);
2973                add_assoc_bool(return_value, "is_dst", parsed_time->dst);
2974                add_assoc_string(return_value, "tz_abbr", parsed_time->tz_abbr, 1);
2975                break;
2976        }
2977    }
2978    if (parsed_time->have_relative) {
2979        MAKE_STD_ZVAL(element);
2980        array_init(element);
2981        add_assoc_long(element, "year",   parsed_time->relative.y);
2982        add_assoc_long(element, "month",  parsed_time->relative.m);
2983        add_assoc_long(element, "day",    parsed_time->relative.d);
2984        add_assoc_long(element, "hour",   parsed_time->relative.h);
2985        add_assoc_long(element, "minute", parsed_time->relative.i);
2986        add_assoc_long(element, "second", parsed_time->relative.s);
2987        if (parsed_time->relative.have_weekday_relative) {
2988            add_assoc_long(element, "weekday", parsed_time->relative.weekday);
2989        }
2990        if (parsed_time->relative.have_special_relative && (parsed_time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY)) {
2991            add_assoc_long(element, "weekdays", parsed_time->relative.special.amount);
2992        }
2993        if (parsed_time->relative.first_last_day_of) {
2994            add_assoc_bool(element, parsed_time->relative.first_last_day_of == 1 ? "first_day_of_month" : "last_day_of_month", 1);
2995        }
2996        add_assoc_zval(return_value, "relative", element);
2997    }
2998    timelib_time_dtor(parsed_time);
2999}
3000
3001/* {{{ proto array date_parse(string date)
3002   Returns associative array with detailed info about given date
3003*/
3004PHP_FUNCTION(date_parse)
3005{
3006    char                           *date;
3007    int                             date_len;
3008    struct timelib_error_container *error;
3009    timelib_time                   *parsed_time;
3010
3011    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &date, &date_len) == FAILURE) {
3012        RETURN_FALSE;
3013    }
3014
3015    parsed_time = timelib_strtotime(date, date_len, &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3016    php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAM_PASSTHRU, parsed_time, error);
3017}
3018/* }}} */
3019
3020/* {{{ proto array date_parse_from_format(string format, string date)
3021   Returns associative array with detailed info about given date
3022*/
3023PHP_FUNCTION(date_parse_from_format)
3024{
3025    char                           *date, *format;
3026    int                             date_len, format_len;
3027    struct timelib_error_container *error;
3028    timelib_time                   *parsed_time;
3029
3030    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &format, &format_len, &date, &date_len) == FAILURE) {
3031        RETURN_FALSE;
3032    }
3033
3034    parsed_time = timelib_parse_from_format(format, date, date_len, &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3035    php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAM_PASSTHRU, parsed_time, error);
3036}
3037/* }}} */
3038
3039/* {{{ proto string date_format(DateTimeInterface object, string format)
3040   Returns date formatted according to given format
3041*/
3042PHP_FUNCTION(date_format)
3043{
3044    zval         *object;
3045    php_date_obj *dateobj;
3046    char         *format;
3047    int           format_len;
3048
3049    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_interface, &format, &format_len) == FAILURE) {
3050        RETURN_FALSE;
3051    }
3052    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3053    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3054    RETURN_STRING(date_format(format, format_len, dateobj->time, dateobj->time->is_localtime), 0);
3055}
3056/* }}} */
3057
3058static int php_date_modify(zval *object, char *modify, int modify_len TSRMLS_DC)
3059{
3060    php_date_obj *dateobj;
3061    timelib_time *tmp_time;
3062    timelib_error_container *err = NULL;
3063
3064    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3065
3066    if (!(dateobj->time)) {
3067        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The DateTime object has not been correctly initialized by its constructor");
3068        return 0;
3069    }
3070
3071    tmp_time = timelib_strtotime(modify, modify_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3072
3073    /* update last errors and warnings */
3074    update_errors_warnings(err TSRMLS_CC);
3075    if (err && err->error_count) {
3076        /* spit out the first library error message, at least */
3077        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", modify,
3078            err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message);
3079        timelib_time_dtor(tmp_time);
3080        return 0;
3081    }
3082
3083    memcpy(&dateobj->time->relative, &tmp_time->relative, sizeof(struct timelib_rel_time));
3084    dateobj->time->have_relative = tmp_time->have_relative;
3085    dateobj->time->sse_uptodate = 0;
3086
3087    if (tmp_time->y != -99999) {
3088        dateobj->time->y = tmp_time->y;
3089    }
3090    if (tmp_time->m != -99999) {
3091        dateobj->time->m = tmp_time->m;
3092    }
3093    if (tmp_time->d != -99999) {
3094        dateobj->time->d = tmp_time->d;
3095    }
3096
3097    if (tmp_time->h != -99999) {
3098        dateobj->time->h = tmp_time->h;
3099        if (tmp_time->i != -99999) {
3100            dateobj->time->i = tmp_time->i;
3101            if (tmp_time->s != -99999) {
3102                dateobj->time->s = tmp_time->s;
3103            } else {
3104                dateobj->time->s = 0;
3105            }
3106        } else {
3107            dateobj->time->i = 0;
3108            dateobj->time->s = 0;
3109        }
3110    }
3111    timelib_time_dtor(tmp_time);
3112
3113    timelib_update_ts(dateobj->time, NULL);
3114    timelib_update_from_sse(dateobj->time);
3115    dateobj->time->have_relative = 0;
3116
3117    return 1;
3118}
3119
3120/* {{{ proto DateTime date_modify(DateTime object, string modify)
3121   Alters the timestamp.
3122*/
3123PHP_FUNCTION(date_modify)
3124{
3125    zval         *object;
3126    char         *modify;
3127    int           modify_len;
3128
3129    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_date, &modify, &modify_len) == FAILURE) {
3130        RETURN_FALSE;
3131    }
3132
3133    if (php_date_modify(object, modify, modify_len TSRMLS_CC)) {
3134        RETURN_ZVAL(object, 1, 0);
3135    }
3136
3137    RETURN_FALSE;
3138}
3139/* }}} */
3140
3141/* {{{ proto DateTimeImmutable::modify()
3142*/
3143PHP_METHOD(DateTimeImmutable, modify)
3144{
3145    zval *object, *new_object;
3146    char *modify;
3147    int   modify_len;
3148
3149    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_immutable, &modify, &modify_len) == FAILURE) {
3150        RETURN_FALSE;
3151    }
3152
3153    new_object = date_clone_immutable(object TSRMLS_CC);
3154    if (php_date_modify(new_object, modify, modify_len TSRMLS_CC)) {
3155        RETURN_ZVAL(new_object, 0, 1);
3156    }
3157
3158    RETURN_FALSE;
3159}
3160/* }}} */
3161
3162static void php_date_add(zval *object, zval *interval, zval *return_value TSRMLS_DC)
3163{
3164    php_date_obj     *dateobj;
3165    php_interval_obj *intobj;
3166    timelib_time     *new_time;
3167
3168    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3169    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3170    intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC);
3171    DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
3172
3173    new_time = timelib_add(dateobj->time, intobj->diff);
3174    timelib_time_dtor(dateobj->time);
3175    dateobj->time = new_time;
3176}
3177
3178/* {{{ proto DateTime date_add(DateTime object, DateInterval interval)
3179   Adds an interval to the current date in object.
3180*/
3181PHP_FUNCTION(date_add)
3182{
3183    zval *object, *interval;
3184
3185    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
3186        RETURN_FALSE;
3187    }
3188
3189    php_date_add(object, interval, return_value TSRMLS_CC);
3190
3191    RETURN_ZVAL(object, 1, 0);
3192}
3193/* }}} */
3194
3195/* {{{ proto DateTimeImmutable::add()
3196*/
3197PHP_METHOD(DateTimeImmutable, add)
3198{
3199    zval *object, *interval, *new_object;
3200
3201    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_immutable, &interval, date_ce_interval) == FAILURE) {
3202        RETURN_FALSE;
3203    }
3204
3205    new_object = date_clone_immutable(object TSRMLS_CC);
3206    php_date_add(new_object, interval, return_value TSRMLS_CC);
3207
3208    RETURN_ZVAL(new_object, 0, 1);
3209}
3210/* }}} */
3211
3212static void php_date_sub(zval *object, zval *interval, zval *return_value TSRMLS_DC)
3213{
3214    php_date_obj     *dateobj;
3215    php_interval_obj *intobj;
3216    timelib_time     *new_time;
3217
3218    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3219    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3220    intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC);
3221    DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
3222
3223    if (intobj->diff->have_special_relative) {
3224        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only non-special relative time specifications are supported for subtraction");
3225        return;
3226    }
3227
3228    new_time = timelib_sub(dateobj->time, intobj->diff);
3229    timelib_time_dtor(dateobj->time);
3230    dateobj->time = new_time;
3231}
3232
3233/* {{{ proto DateTime date_sub(DateTime object, DateInterval interval)
3234   Subtracts an interval to the current date in object.
3235*/
3236PHP_FUNCTION(date_sub)
3237{
3238    zval *object, *interval;
3239
3240    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
3241        RETURN_FALSE;
3242    }
3243
3244    php_date_sub(object, interval, return_value TSRMLS_CC);
3245
3246    RETURN_ZVAL(object, 1, 0);
3247}
3248/* }}} */
3249
3250/* {{{ proto DateTimeImmutable::sub()
3251*/
3252PHP_METHOD(DateTimeImmutable, sub)
3253{
3254    zval *object, *interval, *new_object;
3255
3256    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_immutable, &interval, date_ce_interval) == FAILURE) {
3257        RETURN_FALSE;
3258    }
3259
3260    new_object = date_clone_immutable(object TSRMLS_CC);
3261    php_date_sub(new_object, interval, return_value TSRMLS_CC);
3262
3263    RETURN_ZVAL(new_object, 0, 1);
3264}
3265/* }}} */
3266
3267static void set_timezone_from_timelib_time(php_timezone_obj *tzobj, timelib_time *t)
3268{
3269       tzobj->initialized = 1;
3270       tzobj->type = t->zone_type;
3271       switch (t->zone_type) {
3272               case TIMELIB_ZONETYPE_ID:
3273                       tzobj->tzi.tz = t->tz_info;
3274                       break;
3275               case TIMELIB_ZONETYPE_OFFSET:
3276                       tzobj->tzi.utc_offset = t->z;
3277                       break;
3278               case TIMELIB_ZONETYPE_ABBR:
3279                       tzobj->tzi.z.utc_offset = t->z;
3280                       tzobj->tzi.z.dst = t->dst;
3281                       tzobj->tzi.z.abbr = strdup(t->tz_abbr);
3282                       break;
3283       }
3284}
3285
3286
3287/* {{{ proto DateTimeZone date_timezone_get(DateTimeInterface object)
3288   Return new DateTimeZone object relative to give DateTime
3289*/
3290PHP_FUNCTION(date_timezone_get)
3291{
3292    zval             *object;
3293    php_date_obj     *dateobj;
3294    php_timezone_obj *tzobj;
3295
3296    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, date_ce_interface) == FAILURE) {
3297        RETURN_FALSE;
3298    }
3299    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3300    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3301    if (dateobj->time->is_localtime/* && dateobj->time->tz_info*/) {
3302        php_date_instantiate(date_ce_timezone, return_value TSRMLS_CC);
3303        tzobj = (php_timezone_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
3304        set_timezone_from_timelib_time(tzobj, dateobj->time);
3305    } else {
3306        RETURN_FALSE;
3307    }
3308}
3309/* }}} */
3310
3311static void php_date_timezone_set(zval *object, zval *timezone_object, zval *return_value TSRMLS_DC)
3312{
3313    php_date_obj     *dateobj;
3314    php_timezone_obj *tzobj;
3315
3316    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3317    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3318    tzobj = (php_timezone_obj *) zend_object_store_get_object(timezone_object TSRMLS_CC);
3319
3320    switch (tzobj->type) {
3321        case TIMELIB_ZONETYPE_OFFSET:
3322            timelib_set_timezone_from_offset(dateobj->time, tzobj->tzi.utc_offset);
3323            break;
3324        case TIMELIB_ZONETYPE_ABBR:
3325            timelib_set_timezone_from_abbr(dateobj->time, tzobj->tzi.z);
3326            break;
3327        case TIMELIB_ZONETYPE_ID:
3328            timelib_set_timezone(dateobj->time, tzobj->tzi.tz);
3329            break;
3330    }
3331    timelib_unixtime2local(dateobj->time, dateobj->time->sse);
3332}
3333
3334/* {{{ proto DateTime date_timezone_set(DateTime object, DateTimeZone object)
3335   Sets the timezone for the DateTime object.
3336*/
3337PHP_FUNCTION(date_timezone_set)
3338{
3339    zval *object;
3340    zval *timezone_object;
3341
3342    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &timezone_object, date_ce_timezone) == FAILURE) {
3343        RETURN_FALSE;
3344    }
3345
3346    php_date_timezone_set(object, timezone_object, return_value TSRMLS_CC);
3347
3348    RETURN_ZVAL(object, 1, 0);
3349}
3350/* }}} */
3351
3352/* {{{ proto DateTimeImmutable::setTimezone()
3353*/
3354PHP_METHOD(DateTimeImmutable, setTimezone)
3355{
3356    zval *object, *new_object;
3357    zval *timezone_object;
3358
3359    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_immutable, &timezone_object, date_ce_timezone) == FAILURE) {
3360        RETURN_FALSE;
3361    }
3362
3363    new_object = date_clone_immutable(object TSRMLS_CC);
3364    php_date_timezone_set(new_object, timezone_object, return_value TSRMLS_CC);
3365
3366    RETURN_ZVAL(new_object, 0, 1);
3367}
3368/* }}} */
3369
3370/* {{{ proto long date_offset_get(DateTimeInterface object)
3371   Returns the DST offset.
3372*/
3373PHP_FUNCTION(date_offset_get)
3374{
3375    zval                *object;
3376    php_date_obj        *dateobj;
3377    timelib_time_offset *offset;
3378
3379    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, date_ce_interface) == FAILURE) {
3380        RETURN_FALSE;
3381    }
3382    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3383    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3384    if (dateobj->time->is_localtime/* && dateobj->time->tz_info*/) {
3385        switch (dateobj->time->zone_type) {
3386            case TIMELIB_ZONETYPE_ID:
3387                offset = timelib_get_time_zone_info(dateobj->time->sse, dateobj->time->tz_info);
3388                RETVAL_LONG(offset->offset);
3389                timelib_time_offset_dtor(offset);
3390                break;
3391            case TIMELIB_ZONETYPE_OFFSET:
3392                RETVAL_LONG(dateobj->time->z * -60);
3393                break;
3394            case TIMELIB_ZONETYPE_ABBR:
3395                RETVAL_LONG((dateobj->time->z - (60 * dateobj->time->dst)) * -60);
3396                break;
3397        }
3398        return;
3399    } else {
3400        RETURN_LONG(0);
3401    }
3402}
3403/* }}} */
3404
3405static void php_date_time_set(zval *object, long h, long i, long s, zval *return_value TSRMLS_DC)
3406{
3407    php_date_obj *dateobj;
3408
3409    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3410    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3411    dateobj->time->h = h;
3412    dateobj->time->i = i;
3413    dateobj->time->s = s;
3414    timelib_update_ts(dateobj->time, NULL);
3415}
3416
3417/* {{{ proto DateTime date_time_set(DateTime object, long hour, long minute[, long second])
3418   Sets the time.
3419*/
3420PHP_FUNCTION(date_time_set)
3421{
3422    zval *object;
3423    long  h, i, s = 0;
3424
3425    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_date, &h, &i, &s) == FAILURE) {
3426        RETURN_FALSE;
3427    }
3428
3429    php_date_time_set(object, h, i, s, return_value TSRMLS_CC);
3430
3431    RETURN_ZVAL(object, 1, 0);
3432}
3433/* }}} */
3434
3435/* {{{ proto DateTimeImmutable::setTime()
3436*/
3437PHP_METHOD(DateTimeImmutable, setTime)
3438{
3439    zval *object, *new_object;
3440    long  h, i, s = 0;
3441
3442    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_immutable, &h, &i, &s) == FAILURE) {
3443        RETURN_FALSE;
3444    }
3445
3446    new_object = date_clone_immutable(object TSRMLS_CC);
3447    php_date_time_set(new_object, h, i, s, return_value TSRMLS_CC);
3448
3449    RETURN_ZVAL(new_object, 0, 1);
3450}
3451/* }}} */
3452
3453static void php_date_date_set(zval *object, long y, long m, long d, zval *return_value TSRMLS_DC)
3454{
3455    php_date_obj *dateobj;
3456
3457    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3458    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3459    dateobj->time->y = y;
3460    dateobj->time->m = m;
3461    dateobj->time->d = d;
3462    timelib_update_ts(dateobj->time, NULL);
3463}
3464
3465/* {{{ proto DateTime date_date_set(DateTime object, long year, long month, long day)
3466   Sets the date.
3467*/
3468PHP_FUNCTION(date_date_set)
3469{
3470    zval *object;
3471    long  y, m, d;
3472
3473    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Olll", &object, date_ce_date, &y, &m, &d) == FAILURE) {
3474        RETURN_FALSE;
3475    }
3476
3477    php_date_date_set(object, y, m, d, return_value TSRMLS_CC);
3478
3479    RETURN_ZVAL(object, 1, 0);
3480}
3481/* }}} */
3482
3483/* {{{ proto DateTimeImmutable::setDate()
3484*/
3485PHP_METHOD(DateTimeImmutable, setDate)
3486{
3487    zval *object, *new_object;
3488    long  y, m, d;
3489
3490    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Olll", &object, date_ce_immutable, &y, &m, &d) == FAILURE) {
3491        RETURN_FALSE;
3492    }
3493
3494    new_object = date_clone_immutable(object TSRMLS_CC);
3495    php_date_date_set(new_object, y, m, d, return_value TSRMLS_CC);
3496
3497    RETURN_ZVAL(new_object, 0, 1);
3498}
3499/* }}} */
3500
3501static void php_date_isodate_set(zval *object, long y, long w, long d, zval *return_value TSRMLS_DC)
3502{
3503    php_date_obj *dateobj;
3504
3505    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3506    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3507    dateobj->time->y = y;
3508    dateobj->time->m = 1;
3509    dateobj->time->d = 1;
3510    memset(&dateobj->time->relative, 0, sizeof(dateobj->time->relative));
3511    dateobj->time->relative.d = timelib_daynr_from_weeknr(y, w, d);
3512    dateobj->time->have_relative = 1;
3513
3514    timelib_update_ts(dateobj->time, NULL);
3515}
3516
3517/* {{{ proto DateTime date_isodate_set(DateTime object, long year, long week[, long day])
3518   Sets the ISO date.
3519*/
3520PHP_FUNCTION(date_isodate_set)
3521{
3522    zval *object;
3523    long  y, w, d = 1;
3524
3525    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_date, &y, &w, &d) == FAILURE) {
3526        RETURN_FALSE;
3527    }
3528
3529    php_date_isodate_set(object, y, w, d, return_value TSRMLS_CC);
3530
3531    RETURN_ZVAL(object, 1, 0);
3532}
3533/* }}} */
3534
3535/* {{{ proto DateTimeImmutable::setISODate()
3536*/
3537PHP_METHOD(DateTimeImmutable, setISODate)
3538{
3539    zval *object, *new_object;
3540    long  y, w, d = 1;
3541
3542    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_immutable, &y, &w, &d) == FAILURE) {
3543        RETURN_FALSE;
3544    }
3545
3546    new_object = date_clone_immutable(object TSRMLS_CC);
3547    php_date_isodate_set(new_object, y, w, d, return_value TSRMLS_CC);
3548
3549    RETURN_ZVAL(new_object, 0, 1);
3550}
3551/* }}} */
3552
3553static void php_date_timestamp_set(zval *object, long timestamp, zval *return_value TSRMLS_DC)
3554{
3555    php_date_obj *dateobj;
3556
3557    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3558    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3559    timelib_unixtime2local(dateobj->time, (timelib_sll)timestamp);
3560    timelib_update_ts(dateobj->time, NULL);
3561}
3562
3563/* {{{ proto DateTime date_timestamp_set(DateTime object, long unixTimestamp)
3564   Sets the date and time based on an Unix timestamp.
3565*/
3566PHP_FUNCTION(date_timestamp_set)
3567{
3568    zval *object;
3569    long  timestamp;
3570
3571    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &object, date_ce_date, &timestamp) == FAILURE) {
3572        RETURN_FALSE;
3573    }
3574
3575    php_date_timestamp_set(object, timestamp, return_value TSRMLS_CC);
3576
3577    RETURN_ZVAL(object, 1, 0);
3578}
3579/* }}} */
3580
3581/* {{{ proto DateTimeImmutable::setTimestamp()
3582*/
3583PHP_METHOD(DateTimeImmutable, setTimestamp)
3584{
3585    zval *object, *new_object;
3586    long  timestamp;
3587
3588    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &object, date_ce_immutable, &timestamp) == FAILURE) {
3589        RETURN_FALSE;
3590    }
3591
3592    new_object = date_clone_immutable(object TSRMLS_CC);
3593    php_date_timestamp_set(new_object, timestamp, return_value TSRMLS_CC);
3594
3595    RETURN_ZVAL(new_object, 0, 1);
3596}
3597/* }}} */
3598
3599/* {{{ proto long date_timestamp_get(DateTimeInterface object)
3600   Gets the Unix timestamp.
3601*/
3602PHP_FUNCTION(date_timestamp_get)
3603{
3604    zval         *object;
3605    php_date_obj *dateobj;
3606    long          timestamp;
3607    int           error;
3608
3609    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, date_ce_interface) == FAILURE) {
3610        RETURN_FALSE;
3611    }
3612    dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
3613    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3614    timelib_update_ts(dateobj->time, NULL);
3615
3616    timestamp = timelib_date_to_int(dateobj->time, &error);
3617    if (error) {
3618        RETURN_FALSE;
3619    } else {
3620        RETVAL_LONG(timestamp);
3621    }
3622}
3623/* }}} */
3624
3625/* {{{ proto DateInterval date_diff(DateTime object [, bool absolute])
3626   Returns the difference between two DateTime objects.
3627*/
3628PHP_FUNCTION(date_diff)
3629{
3630    zval         *object1, *object2;
3631    php_date_obj *dateobj1, *dateobj2;
3632    php_interval_obj *interval;
3633    long          absolute = 0;
3634
3635    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO|l", &object1, date_ce_interface, &object2, date_ce_interface, &absolute) == FAILURE) {
3636        RETURN_FALSE;
3637    }
3638    dateobj1 = (php_date_obj *) zend_object_store_get_object(object1 TSRMLS_CC);
3639    dateobj2 = (php_date_obj *) zend_object_store_get_object(object2 TSRMLS_CC);
3640    DATE_CHECK_INITIALIZED(dateobj1->time, DateTimeInterface);
3641    DATE_CHECK_INITIALIZED(dateobj2->time, DateTimeInterface);
3642    timelib_update_ts(dateobj1->time, NULL);
3643    timelib_update_ts(dateobj2->time, NULL);
3644
3645    php_date_instantiate(date_ce_interval, return_value TSRMLS_CC);
3646    interval = zend_object_store_get_object(return_value TSRMLS_CC);
3647    interval->diff = timelib_diff(dateobj1->time, dateobj2->time);
3648    if (absolute) {
3649        interval->diff->invert = 0;
3650    }
3651    interval->initialized = 1;
3652}
3653/* }}} */
3654
3655static int timezone_initialize(php_timezone_obj *tzobj, /*const*/ char *tz TSRMLS_DC)
3656{
3657    timelib_time *dummy_t = ecalloc(1, sizeof(timelib_time));
3658    int           dst, not_found;
3659    char         *orig_tz = tz;
3660
3661    dummy_t->z = timelib_parse_zone(&tz, &dst, dummy_t, &not_found, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3662    if (not_found) {
3663        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown or bad timezone (%s)", orig_tz);
3664        efree(dummy_t);
3665        return FAILURE;
3666    } else {
3667        set_timezone_from_timelib_time(tzobj, dummy_t);
3668        efree(dummy_t);
3669        return SUCCESS;
3670    }
3671}
3672
3673/* {{{ proto DateTimeZone timezone_open(string timezone)
3674   Returns new DateTimeZone object
3675*/
3676PHP_FUNCTION(timezone_open)
3677{
3678    char *tz;
3679    int   tz_len;
3680    php_timezone_obj *tzobj;
3681
3682    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &tz, &tz_len) == FAILURE) {
3683        RETURN_FALSE;
3684    }
3685    tzobj = zend_object_store_get_object(php_date_instantiate(date_ce_timezone, return_value TSRMLS_CC) TSRMLS_CC);
3686    if (SUCCESS != timezone_initialize(tzobj, tz TSRMLS_CC)) {
3687        RETURN_FALSE;
3688    }
3689}
3690/* }}} */
3691
3692/* {{{ proto DateTimeZone::__construct(string timezone)
3693   Creates new DateTimeZone object.
3694*/
3695PHP_METHOD(DateTimeZone, __construct)
3696{
3697    char *tz;
3698    int tz_len;
3699    php_timezone_obj *tzobj;
3700    zend_error_handling error_handling;
3701
3702    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
3703    if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &tz, &tz_len)) {
3704        tzobj = zend_object_store_get_object(getThis() TSRMLS_CC);
3705        if (FAILURE == timezone_initialize(tzobj, tz TSRMLS_CC)) {
3706            ZVAL_NULL(getThis());
3707        }
3708    }
3709    zend_restore_error_handling(&error_handling TSRMLS_CC);
3710}
3711/* }}} */
3712
3713static int php_date_timezone_initialize_from_hash(zval **return_value, php_timezone_obj **tzobj, HashTable *myht TSRMLS_DC)
3714{
3715    zval            **z_timezone = NULL;
3716    zval            **z_timezone_type = NULL;
3717
3718    if (zend_hash_find(myht, "timezone_type", 14, (void**) &z_timezone_type) == SUCCESS) {
3719        if (zend_hash_find(myht, "timezone", 9, (void**) &z_timezone) == SUCCESS) {
3720            convert_to_long(*z_timezone_type);
3721            if (SUCCESS == timezone_initialize(*tzobj, Z_STRVAL_PP(z_timezone) TSRMLS_CC)) {
3722                return SUCCESS;
3723            }
3724        }
3725    }
3726    return FAILURE;
3727}
3728
3729/* {{{ proto DateTimeZone::__set_state()
3730 *  */
3731PHP_METHOD(DateTimeZone, __set_state)
3732{
3733    php_timezone_obj *tzobj;
3734    zval             *array;
3735    HashTable        *myht;
3736
3737    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
3738        RETURN_FALSE;
3739    }
3740
3741    myht = HASH_OF(array);
3742
3743    php_date_instantiate(date_ce_timezone, return_value TSRMLS_CC);
3744    tzobj = (php_timezone_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
3745    php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht TSRMLS_CC);
3746}
3747/* }}} */
3748
3749/* {{{ proto DateTimeZone::__wakeup()
3750 *  */
3751PHP_METHOD(DateTimeZone, __wakeup)
3752{
3753    zval             *object = getThis();
3754    php_timezone_obj *tzobj;
3755    HashTable        *myht;
3756
3757    tzobj = (php_timezone_obj *) zend_object_store_get_object(object TSRMLS_CC);
3758
3759    myht = Z_OBJPROP_P(object);
3760
3761    php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht TSRMLS_CC);
3762}
3763/* }}} */
3764
3765/* {{{ proto string timezone_name_get(DateTimeZone object)
3766   Returns the name of the timezone.
3767*/
3768PHP_FUNCTION(timezone_name_get)
3769{
3770    zval             *object;
3771    php_timezone_obj *tzobj;
3772
3773    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, date_ce_timezone) == FAILURE) {
3774        RETURN_FALSE;
3775    }
3776    tzobj = (php_timezone_obj *) zend_object_store_get_object(object TSRMLS_CC);
3777    DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3778
3779    switch (tzobj->type) {
3780        case TIMELIB_ZONETYPE_ID:
3781            RETURN_STRING(tzobj->tzi.tz->name, 1);
3782            break;
3783        case TIMELIB_ZONETYPE_OFFSET: {
3784            char *tmpstr = emalloc(sizeof("UTC+05:00"));
3785            timelib_sll utc_offset = tzobj->tzi.utc_offset;
3786
3787            snprintf(tmpstr, sizeof("+05:00"), "%c%02d:%02d",
3788                utc_offset > 0 ? '-' : '+',
3789                abs(utc_offset / 60),
3790                abs((utc_offset % 60)));
3791
3792            RETURN_STRING(tmpstr, 0);
3793            }
3794            break;
3795        case TIMELIB_ZONETYPE_ABBR:
3796            RETURN_STRING(tzobj->tzi.z.abbr, 1);
3797            break;
3798    }
3799}
3800/* }}} */
3801
3802/* {{{ proto string timezone_name_from_abbr(string abbr[, long gmtOffset[, long isdst]])
3803   Returns the timezone name from abbrevation
3804*/
3805PHP_FUNCTION(timezone_name_from_abbr)
3806{
3807    char    *abbr;
3808    char    *tzid;
3809    int      abbr_len;
3810    long     gmtoffset = -1;
3811    long     isdst = -1;
3812
3813    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &abbr, &abbr_len, &gmtoffset, &isdst) == FAILURE) {
3814        RETURN_FALSE;
3815    }
3816    tzid = timelib_timezone_id_from_abbr(abbr, gmtoffset, isdst);
3817
3818    if (tzid) {
3819        RETURN_STRING(tzid, 1);
3820    } else {
3821        RETURN_FALSE;
3822    }
3823}
3824/* }}} */
3825
3826/* {{{ proto long timezone_offset_get(DateTimeZone object, DateTime object)
3827   Returns the timezone offset.
3828*/
3829PHP_FUNCTION(timezone_offset_get)
3830{
3831    zval                *object, *dateobject;
3832    php_timezone_obj    *tzobj;
3833    php_date_obj        *dateobj;
3834    timelib_time_offset *offset;
3835
3836    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_timezone, &dateobject, date_ce_date) == FAILURE) {
3837        RETURN_FALSE;
3838    }
3839    tzobj = (php_timezone_obj *) zend_object_store_get_object(object TSRMLS_CC);
3840    DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3841    dateobj = (php_date_obj *) zend_object_store_get_object(dateobject TSRMLS_CC);
3842    DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3843
3844    switch (tzobj->type) {
3845        case TIMELIB_ZONETYPE_ID:
3846            offset = timelib_get_time_zone_info(dateobj->time->sse, tzobj->tzi.tz);
3847            RETVAL_LONG(offset->offset);
3848            timelib_time_offset_dtor(offset);
3849            break;
3850        case TIMELIB_ZONETYPE_OFFSET:
3851            RETURN_LONG(tzobj->tzi.utc_offset * -60);
3852            break;
3853        case TIMELIB_ZONETYPE_ABBR:
3854            RETURN_LONG((tzobj->tzi.z.utc_offset - (tzobj->tzi.z.dst*60)) * -60);
3855            break;
3856    }
3857}
3858/* }}} */
3859
3860/* {{{ proto array timezone_transitions_get(DateTimeZone object [, long timestamp_begin [, long timestamp_end ]])
3861   Returns numerically indexed array containing associative array for all transitions in the specified range for the timezone.
3862*/
3863PHP_FUNCTION(timezone_transitions_get)
3864{
3865    zval                *object, *element;
3866    php_timezone_obj    *tzobj;
3867    unsigned int         i, begin = 0, found;
3868    long                 timestamp_begin = LONG_MIN, timestamp_end = LONG_MAX;
3869
3870    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|ll", &object, date_ce_timezone, &timestamp_begin, &timestamp_end) == FAILURE) {
3871        RETURN_FALSE;
3872    }
3873    tzobj = (php_timezone_obj *) zend_object_store_get_object(object TSRMLS_CC);
3874    DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3875    if (tzobj->type != TIMELIB_ZONETYPE_ID) {
3876        RETURN_FALSE;
3877    }
3878
3879#define add_nominal() \
3880        MAKE_STD_ZVAL(element); \
3881        array_init(element); \
3882        add_assoc_long(element, "ts",     timestamp_begin); \
3883        add_assoc_string(element, "time", php_format_date(DATE_FORMAT_ISO8601, 13, timestamp_begin, 0 TSRMLS_CC), 0); \
3884        add_assoc_long(element, "offset", tzobj->tzi.tz->type[0].offset); \
3885        add_assoc_bool(element, "isdst",  tzobj->tzi.tz->type[0].isdst); \
3886        add_assoc_string(element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[0].abbr_idx], 1); \
3887        add_next_index_zval(return_value, element);
3888
3889#define add(i,ts) \
3890        MAKE_STD_ZVAL(element); \
3891        array_init(element); \
3892        add_assoc_long(element, "ts",     ts); \
3893        add_assoc_string(element, "time", php_format_date(DATE_FORMAT_ISO8601, 13, ts, 0 TSRMLS_CC), 0); \
3894        add_assoc_long(element, "offset", tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].offset); \
3895        add_assoc_bool(element, "isdst",  tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].isdst); \
3896        add_assoc_string(element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].abbr_idx], 1); \
3897        add_next_index_zval(return_value, element);
3898
3899#define add_last() add(tzobj->tzi.tz->timecnt - 1, timestamp_begin)
3900
3901    array_init(return_value);
3902
3903    if (timestamp_begin == LONG_MIN) {
3904        add_nominal();
3905        begin = 0;
3906        found = 1;
3907    } else {
3908        begin = 0;
3909        found = 0;
3910        if (tzobj->tzi.tz->timecnt > 0) {
3911            do {
3912                if (tzobj->tzi.tz->trans[begin] > timestamp_begin) {
3913                    if (begin > 0) {
3914                        add(begin - 1, timestamp_begin);
3915                    } else {
3916                        add_nominal();
3917                    }
3918                    found = 1;
3919                    break;
3920                }
3921                begin++;
3922            } while (begin < tzobj->tzi.tz->timecnt);
3923        }
3924    }
3925
3926    if (!found) {
3927        if (tzobj->tzi.tz->timecnt > 0) {
3928            add_last();
3929        } else {
3930            add_nominal();
3931        }
3932    } else {
3933        for (i = begin; i < tzobj->tzi.tz->timecnt; ++i) {
3934            if (tzobj->tzi.tz->trans[i] < timestamp_end) {
3935                add(i, tzobj->tzi.tz->trans[i]);
3936            }
3937        }
3938    }
3939}
3940/* }}} */
3941
3942/* {{{ proto array timezone_location_get()
3943   Returns location information for a timezone, including country code, latitude/longitude and comments
3944*/
3945PHP_FUNCTION(timezone_location_get)
3946{
3947    zval                *object;
3948    php_timezone_obj    *tzobj;
3949
3950    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, date_ce_timezone) == FAILURE) {
3951        RETURN_FALSE;
3952    }
3953    tzobj = (php_timezone_obj *) zend_object_store_get_object(object TSRMLS_CC);
3954    DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3955    if (tzobj->type != TIMELIB_ZONETYPE_ID) {
3956        RETURN_FALSE;
3957    }
3958
3959    array_init(return_value);
3960    add_assoc_string(return_value, "country_code", tzobj->tzi.tz->location.country_code, 1);
3961    add_assoc_double(return_value, "latitude", tzobj->tzi.tz->location.latitude);
3962    add_assoc_double(return_value, "longitude", tzobj->tzi.tz->location.longitude);
3963    add_assoc_string(return_value, "comments", tzobj->tzi.tz->location.comments, 1);
3964}
3965/* }}} */
3966
3967static int date_interval_initialize(timelib_rel_time **rt, /*const*/ char *format, int format_length TSRMLS_DC)
3968{
3969    timelib_time     *b = NULL, *e = NULL;
3970    timelib_rel_time *p = NULL;
3971    int               r = 0;
3972    int               retval = 0;
3973    struct timelib_error_container *errors;
3974
3975    timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);
3976
3977    if (errors->error_count > 0) {
3978        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown or bad format (%s)", format);
3979        retval = FAILURE;
3980    } else {
3981        if(p) {
3982            *rt = p;
3983            retval = SUCCESS;
3984        } else {
3985            if(b && e) {
3986                timelib_update_ts(b, NULL);
3987                timelib_update_ts(e, NULL);
3988                *rt = timelib_diff(b, e);
3989                retval = SUCCESS;
3990            } else {
3991                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse interval (%s)", format);
3992                retval = FAILURE;
3993            }
3994        }
3995    }
3996    timelib_error_container_dtor(errors);
3997    return retval;
3998}
3999
4000/* {{{ date_interval_read_property */
4001zval *date_interval_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
4002{
4003    php_interval_obj *obj;
4004    zval *retval;
4005    zval tmp_member;
4006    timelib_sll value = -1;
4007
4008    if (member->type != IS_STRING) {
4009        tmp_member = *member;
4010        zval_copy_ctor(&tmp_member);
4011        convert_to_string(&tmp_member);
4012        member = &tmp_member;
4013        key = NULL;
4014    }
4015
4016    obj = (php_interval_obj *)zend_objects_get_address(object TSRMLS_CC);
4017
4018    if (!obj->initialized) {
4019        retval = (zend_get_std_object_handlers())->read_property(object, member, type, key TSRMLS_CC);
4020        if (member == &tmp_member) {
4021            zval_dtor(member);
4022        }
4023        return retval;
4024    }
4025
4026#define GET_VALUE_FROM_STRUCT(n,m)            \
4027    if (strcmp(Z_STRVAL_P(member), m) == 0) { \
4028        value = obj->diff->n;                 \
4029        break;                                \
4030    }
4031    do {
4032        GET_VALUE_FROM_STRUCT(y, "y");
4033        GET_VALUE_FROM_STRUCT(m, "m");
4034        GET_VALUE_FROM_STRUCT(d, "d");
4035        GET_VALUE_FROM_STRUCT(h, "h");
4036        GET_VALUE_FROM_STRUCT(i, "i");
4037        GET_VALUE_FROM_STRUCT(s, "s");
4038        GET_VALUE_FROM_STRUCT(invert, "invert");
4039        GET_VALUE_FROM_STRUCT(days, "days");
4040        /* didn't find any */
4041        retval = (zend_get_std_object_handlers())->read_property(object, member, type, key TSRMLS_CC);
4042
4043        if (member == &tmp_member) {
4044            zval_dtor(member);
4045        }
4046
4047        return retval;
4048    } while(0);
4049
4050    ALLOC_INIT_ZVAL(retval);
4051    Z_SET_REFCOUNT_P(retval, 0);
4052
4053    if (value != -99999) {
4054        ZVAL_LONG(retval, value);
4055    } else {
4056        ZVAL_FALSE(retval);
4057    }
4058
4059    if (member == &tmp_member) {
4060        zval_dtor(member);
4061    }
4062
4063    return retval;
4064}
4065/* }}} */
4066
4067/* {{{ date_interval_write_property */
4068void date_interval_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
4069{
4070    php_interval_obj *obj;
4071    zval tmp_member, tmp_value;
4072
4073    if (member->type != IS_STRING) {
4074        tmp_member = *member;
4075        zval_copy_ctor(&tmp_member);
4076        convert_to_string(&tmp_member);
4077        member = &tmp_member;
4078        key = NULL;
4079    }
4080
4081    obj = (php_interval_obj *)zend_objects_get_address(object TSRMLS_CC);
4082
4083    if (!obj->initialized) {
4084        (zend_get_std_object_handlers())->write_property(object, member, value, key TSRMLS_CC);
4085        if (member == &tmp_member) {
4086            zval_dtor(member);
4087        }
4088        return;
4089    }
4090
4091#define SET_VALUE_FROM_STRUCT(n,m)            \
4092    if (strcmp(Z_STRVAL_P(member), m) == 0) { \
4093        if (value->type != IS_LONG) {         \
4094            tmp_value = *value;               \
4095            zval_copy_ctor(&tmp_value);       \
4096            convert_to_long(&tmp_value);      \
4097            value = &tmp_value;               \
4098        }                                     \
4099        obj->diff->n = Z_LVAL_P(value);       \
4100        if (value == &tmp_value) {            \
4101            zval_dtor(value);                 \
4102        }                                     \
4103        break;                                \
4104    }
4105
4106    do {
4107        SET_VALUE_FROM_STRUCT(y, "y");
4108        SET_VALUE_FROM_STRUCT(m, "m");
4109        SET_VALUE_FROM_STRUCT(d, "d");
4110        SET_VALUE_FROM_STRUCT(h, "h");
4111        SET_VALUE_FROM_STRUCT(i, "i");
4112        SET_VALUE_FROM_STRUCT(s, "s");
4113        SET_VALUE_FROM_STRUCT(invert, "invert");
4114        /* didn't find any */
4115        (zend_get_std_object_handlers())->write_property(object, member, value, key TSRMLS_CC);
4116    } while(0);
4117
4118    if (member == &tmp_member) {
4119        zval_dtor(member);
4120    }
4121}
4122/* }}} */
4123
4124
4125/* {{{ proto DateInterval::__construct([string interval_spec])
4126   Creates new DateInterval object.
4127*/
4128PHP_METHOD(DateInterval, __construct)
4129{
4130    char *interval_string = NULL;
4131    int   interval_string_length;
4132    php_interval_obj *diobj;
4133    timelib_rel_time *reltime;
4134    zend_error_handling error_handling;
4135
4136    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
4137    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &interval_string, &interval_string_length) == SUCCESS) {
4138        if (date_interval_initialize(&reltime, interval_string, interval_string_length TSRMLS_CC) == SUCCESS) {
4139            diobj = zend_object_store_get_object(getThis() TSRMLS_CC);
4140            diobj->diff = reltime;
4141            diobj->initialized = 1;
4142        } else {
4143            ZVAL_NULL(getThis());
4144        }
4145    }
4146    zend_restore_error_handling(&error_handling TSRMLS_CC);
4147}
4148/* }}} */
4149
4150
4151static int php_date_interval_initialize_from_hash(zval **return_value, php_interval_obj **intobj, HashTable *myht TSRMLS_DC)
4152{
4153    (*intobj)->diff = timelib_rel_time_ctor();
4154
4155#define PHP_DATE_INTERVAL_READ_PROPERTY(element, member, itype, def) \
4156    do { \
4157        zval **z_arg = NULL; \
4158        if (zend_hash_find(myht, element, strlen(element) + 1, (void**) &z_arg) == SUCCESS) { \
4159            convert_to_long(*z_arg); \
4160            (*intobj)->diff->member = (itype)Z_LVAL_PP(z_arg); \
4161        } else { \
4162            (*intobj)->diff->member = (itype)def; \
4163        } \
4164    } while (0);
4165
4166#define PHP_DATE_INTERVAL_READ_PROPERTY_I64(element, member) \
4167    do { \
4168        zval **z_arg = NULL; \
4169        if (zend_hash_find(myht, element, strlen(element) + 1, (void**) &z_arg) == SUCCESS) { \
4170            convert_to_string(*z_arg); \
4171            DATE_A64I((*intobj)->diff->member, Z_STRVAL_PP(z_arg)); \
4172        } else { \
4173            (*intobj)->diff->member = -1LL; \
4174        } \
4175    } while (0);
4176
4177    PHP_DATE_INTERVAL_READ_PROPERTY("y", y, timelib_sll, -1)
4178    PHP_DATE_INTERVAL_READ_PROPERTY("m", m, timelib_sll, -1)
4179    PHP_DATE_INTERVAL_READ_PROPERTY("d", d, timelib_sll, -1)
4180    PHP_DATE_INTERVAL_READ_PROPERTY("h", h, timelib_sll, -1)
4181    PHP_DATE_INTERVAL_READ_PROPERTY("i", i, timelib_sll, -1)
4182    PHP_DATE_INTERVAL_READ_PROPERTY("s", s, timelib_sll, -1)
4183    PHP_DATE_INTERVAL_READ_PROPERTY("weekday", weekday, int, -1)
4184    PHP_DATE_INTERVAL_READ_PROPERTY("weekday_behavior", weekday_behavior, int, -1)
4185    PHP_DATE_INTERVAL_READ_PROPERTY("first_last_day_of", first_last_day_of, int, -1)
4186    PHP_DATE_INTERVAL_READ_PROPERTY("invert", invert, int, 0);
4187    PHP_DATE_INTERVAL_READ_PROPERTY_I64("days", days);
4188    PHP_DATE_INTERVAL_READ_PROPERTY("special_type", special.type, unsigned int, 0);
4189    PHP_DATE_INTERVAL_READ_PROPERTY_I64("special_amount", special.amount);
4190    PHP_DATE_INTERVAL_READ_PROPERTY("have_weekday_relative", have_weekday_relative, unsigned int, 0);
4191    PHP_DATE_INTERVAL_READ_PROPERTY("have_special_relative", have_special_relative, unsigned int, 0);
4192    (*intobj)->initialized = 1;
4193
4194    return 0;
4195}
4196
4197/* {{{ proto DateInterval::__set_state()
4198*/
4199PHP_METHOD(DateInterval, __set_state)
4200{
4201    php_interval_obj *intobj;
4202    zval             *array;
4203    HashTable        *myht;
4204
4205    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
4206        RETURN_FALSE;
4207    }
4208
4209    myht = HASH_OF(array);
4210
4211    php_date_instantiate(date_ce_interval, return_value TSRMLS_CC);
4212    intobj = (php_interval_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
4213    php_date_interval_initialize_from_hash(&return_value, &intobj, myht TSRMLS_CC);
4214}
4215/* }}} */
4216
4217/* {{{ proto DateInterval::__wakeup()
4218*/
4219PHP_METHOD(DateInterval, __wakeup)
4220{
4221    zval             *object = getThis();
4222    php_interval_obj *intobj;
4223    HashTable        *myht;
4224
4225    intobj = (php_interval_obj *) zend_object_store_get_object(object TSRMLS_CC);
4226
4227    myht = Z_OBJPROP_P(object);
4228
4229    php_date_interval_initialize_from_hash(&return_value, &intobj, myht TSRMLS_CC);
4230}
4231/* }}} */
4232/* {{{ proto DateInterval date_interval_create_from_date_string(string time)
4233   Uses the normal date parsers and sets up a DateInterval from the relative parts of the parsed string
4234*/
4235PHP_FUNCTION(date_interval_create_from_date_string)
4236{
4237    char           *time_str = NULL;
4238    int             time_str_len = 0;
4239    timelib_time   *time;
4240    timelib_error_container *err = NULL;
4241    php_interval_obj *diobj;
4242
4243    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &time_str, &time_str_len) == FAILURE) {
4244        RETURN_FALSE;
4245    }
4246
4247    php_date_instantiate(date_ce_interval, return_value TSRMLS_CC);
4248
4249    time = timelib_strtotime(time_str, time_str_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
4250    diobj = (php_interval_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
4251    diobj->diff = timelib_rel_time_clone(&time->relative);
4252    diobj->initialized = 1;
4253    timelib_time_dtor(time);
4254    timelib_error_container_dtor(err);
4255}
4256/* }}} */
4257
4258/* {{{ date_interval_format -  */
4259static char *date_interval_format(char *format, int format_len, timelib_rel_time *t)
4260{
4261    smart_str            string = {0};
4262    int                  i, length, have_format_spec = 0;
4263    char                 buffer[33];
4264
4265    if (!format_len) {
4266        return estrdup("");
4267    }
4268
4269    for (i = 0; i < format_len; i++) {
4270        if (have_format_spec) {
4271            switch (format[i]) {
4272                case 'Y': length = slprintf(buffer, 32, "%02d", (int) t->y); break;
4273                case 'y': length = slprintf(buffer, 32, "%d", (int) t->y); break;
4274
4275                case 'M': length = slprintf(buffer, 32, "%02d", (int) t->m); break;
4276                case 'm': length = slprintf(buffer, 32, "%d", (int) t->m); break;
4277
4278                case 'D': length = slprintf(buffer, 32, "%02d", (int) t->d); break;
4279                case 'd': length = slprintf(buffer, 32, "%d", (int) t->d); break;
4280
4281                case 'H': length = slprintf(buffer, 32, "%02d", (int) t->h); break;
4282                case 'h': length = slprintf(buffer, 32, "%d", (int) t->h); break;
4283
4284                case 'I': length = slprintf(buffer, 32, "%02d", (int) t->i); break;
4285                case 'i': length = slprintf(buffer, 32, "%d", (int) t->i); break;
4286
4287                case 'S': length = slprintf(buffer, 32, "%02ld", (long) t->s); break;
4288                case 's': length = slprintf(buffer, 32, "%ld", (long) t->s); break;
4289
4290                case 'a': {
4291                    if ((int) t->days != -99999) {
4292                        length = slprintf(buffer, 32, "%d", (int) t->days);
4293                    } else {
4294                        length = slprintf(buffer, 32, "(unknown)");
4295                    }
4296                } break;
4297                case 'r': length = slprintf(buffer, 32, "%s", t->invert ? "-" : ""); break;
4298                case 'R': length = slprintf(buffer, 32, "%c", t->invert ? '-' : '+'); break;
4299
4300                case '%': length = slprintf(buffer, 32, "%%"); break;
4301                default: buffer[0] = '%'; buffer[1] = format[i]; buffer[2] = '\0'; length = 2; break;
4302            }
4303            smart_str_appendl(&string, buffer, length);
4304            have_format_spec = 0;
4305        } else {
4306            if (format[i] == '%') {
4307                have_format_spec = 1;
4308            } else {
4309                smart_str_appendc(&string, format[i]);
4310            }
4311        }
4312    }
4313
4314    smart_str_0(&string);
4315
4316    return string.c;
4317}
4318/* }}} */
4319
4320/* {{{ proto string date_interval_format(DateInterval object, string format)
4321   Formats the interval.
4322*/
4323PHP_FUNCTION(date_interval_format)
4324{
4325    zval             *object;
4326    php_interval_obj *diobj;
4327    char             *format;
4328    int               format_len;
4329
4330    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_interval, &format, &format_len) == FAILURE) {
4331        RETURN_FALSE;
4332    }
4333    diobj = (php_interval_obj *) zend_object_store_get_object(object TSRMLS_CC);
4334    DATE_CHECK_INITIALIZED(diobj->initialized, DateInterval);
4335
4336    RETURN_STRING(date_interval_format(format, format_len, diobj->diff), 0);
4337}
4338/* }}} */
4339
4340static int date_period_initialize(timelib_time **st, timelib_time **et, timelib_rel_time **d, long *recurrences, /*const*/ char *format, int format_length TSRMLS_DC)
4341{
4342    timelib_time     *b = NULL, *e = NULL;
4343    timelib_rel_time *p = NULL;
4344    int               r = 0;
4345    int               retval = 0;
4346    struct timelib_error_container *errors;
4347
4348    timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);
4349
4350    if (errors->error_count > 0) {
4351        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown or bad format (%s)", format);
4352        retval = FAILURE;
4353    } else {
4354        *st = b;
4355        *et = e;
4356        *d  = p;
4357        *recurrences = r;
4358        retval = SUCCESS;
4359    }
4360    timelib_error_container_dtor(errors);
4361    return retval;
4362}
4363
4364/* {{{ proto DatePeriod::__construct(DateTime $start, DateInterval $interval, int recurrences|DateTime $end)
4365   Creates new DatePeriod object.
4366*/
4367PHP_METHOD(DatePeriod, __construct)
4368{
4369    php_period_obj   *dpobj;
4370    php_date_obj     *dateobj;
4371    php_interval_obj *intobj;
4372    zval *start, *end = NULL, *interval;
4373    long  recurrences = 0, options = 0;
4374    char *isostr = NULL;
4375    int   isostr_len = 0;
4376    timelib_time *clone;
4377    zend_error_handling error_handling;
4378
4379    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
4380    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) {
4381        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) {
4382            if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &isostr, &isostr_len, &options) == FAILURE) {
4383                php_error_docref(NULL TSRMLS_CC, E_WARNING, "This constructor accepts either (DateTimeInterface, DateInterval, int) OR (DateTimeInterface, DateInterval, DateTime) OR (string) as arguments.");
4384                zend_restore_error_handling(&error_handling TSRMLS_CC);
4385                return;
4386            }
4387        }
4388    }
4389
4390    dpobj = zend_object_store_get_object(getThis() TSRMLS_CC);
4391    dpobj->current = NULL;
4392
4393    if (isostr) {
4394        date_period_initialize(&(dpobj->start), &(dpobj->end), &(dpobj->interval), &recurrences, isostr, isostr_len TSRMLS_CC);
4395        if (dpobj->start == NULL) {
4396            php_error_docref(NULL TSRMLS_CC, E_WARNING, "The ISO interval '%s' did not contain a start date.", isostr);
4397        }
4398        if (dpobj->interval == NULL) {
4399            php_error_docref(NULL TSRMLS_CC, E_WARNING, "The ISO interval '%s' did not contain an interval.", isostr);
4400        }
4401        if (dpobj->end == NULL && recurrences == 0) {
4402            php_error_docref(NULL TSRMLS_CC, E_WARNING, "The ISO interval '%s' did not contain an end date or a recurrence count.", isostr);
4403        }
4404
4405        if (dpobj->start) {
4406            timelib_update_ts(dpobj->start, NULL);
4407        }
4408        if (dpobj->end) {
4409            timelib_update_ts(dpobj->end, NULL);
4410        }
4411        dpobj->start_ce = date_ce_date;
4412    } else {
4413        /* init */
4414        intobj  = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC);
4415
4416        /* start date */
4417        dateobj = (php_date_obj *) zend_object_store_get_object(start TSRMLS_CC);
4418        clone = timelib_time_ctor();
4419        memcpy(clone, dateobj->time, sizeof(timelib_time));
4420        if (dateobj->time->tz_abbr) {
4421            clone->tz_abbr = strdup(dateobj->time->tz_abbr);
4422        }
4423        if (dateobj->time->tz_info) {
4424            clone->tz_info = dateobj->time->tz_info;
4425        }
4426        dpobj->start = clone;
4427        dpobj->start_ce = Z_OBJCE_P(start);
4428
4429        /* interval */
4430        dpobj->interval = timelib_rel_time_clone(intobj->diff);
4431
4432        /* end date */
4433        if (end) {
4434            dateobj = (php_date_obj *) zend_object_store_get_object(end TSRMLS_CC);
4435            clone = timelib_time_clone(dateobj->time);
4436            dpobj->end = clone;
4437        }
4438    }
4439
4440    /* options */
4441    dpobj->include_start_date = !(options & PHP_DATE_PERIOD_EXCLUDE_START_DATE);
4442
4443    /* recurrrences */
4444    dpobj->recurrences = recurrences + dpobj->include_start_date;
4445
4446    dpobj->initialized = 1;
4447
4448    zend_restore_error_handling(&error_handling TSRMLS_CC);
4449}
4450/* }}} */
4451
4452static int check_id_allowed(char *id, long what)
4453{
4454    if (what & PHP_DATE_TIMEZONE_GROUP_AFRICA     && strncasecmp(id, "Africa/",      7) == 0) return 1;
4455    if (what & PHP_DATE_TIMEZONE_GROUP_AMERICA    && strncasecmp(id, "America/",     8) == 0) return 1;
4456    if (what & PHP_DATE_TIMEZONE_GROUP_ANTARCTICA && strncasecmp(id, "Antarctica/", 11) == 0) return 1;
4457    if (what & PHP_DATE_TIMEZONE_GROUP_ARCTIC     && strncasecmp(id, "Arctic/",      7) == 0) return 1;
4458    if (what & PHP_DATE_TIMEZONE_GROUP_ASIA       && strncasecmp(id, "Asia/",        5) == 0) return 1;
4459    if (what & PHP_DATE_TIMEZONE_GROUP_ATLANTIC   && strncasecmp(id, "Atlantic/",    9) == 0) return 1;
4460    if (what & PHP_DATE_TIMEZONE_GROUP_AUSTRALIA  && strncasecmp(id, "Australia/",  10) == 0) return 1;
4461    if (what & PHP_DATE_TIMEZONE_GROUP_EUROPE     && strncasecmp(id, "Europe/",      7) == 0) return 1;
4462    if (what & PHP_DATE_TIMEZONE_GROUP_INDIAN     && strncasecmp(id, "Indian/",      7) == 0) return 1;
4463    if (what & PHP_DATE_TIMEZONE_GROUP_PACIFIC    && strncasecmp(id, "Pacific/",     8) == 0) return 1;
4464    if (what & PHP_DATE_TIMEZONE_GROUP_UTC        && strncasecmp(id, "UTC",          3) == 0) return 1;
4465    return 0;
4466}
4467
4468/* {{{ proto array timezone_identifiers_list([long what[, string country]])
4469   Returns numerically index array with all timezone identifiers.
4470*/
4471PHP_FUNCTION(timezone_identifiers_list)
4472{
4473    const timelib_tzdb             *tzdb;
4474    const timelib_tzdb_index_entry *table;
4475    int                             i, item_count;
4476    long                            what = PHP_DATE_TIMEZONE_GROUP_ALL;
4477    char                           *option = NULL;
4478    int                             option_len = 0;
4479
4480    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ls", &what, &option, &option_len) == FAILURE) {
4481        RETURN_FALSE;
4482    }
4483
4484    /* Extra validation */
4485    if (what == PHP_DATE_TIMEZONE_PER_COUNTRY && option_len != 2) {
4486        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "A two-letter ISO 3166-1 compatible country code is expected");
4487        RETURN_FALSE;
4488    }
4489
4490    tzdb = DATE_TIMEZONEDB;
4491    item_count = tzdb->index_size;
4492    table = tzdb->index;
4493
4494    array_init(return_value);
4495
4496    for (i = 0; i < item_count; ++i) {
4497        if (what == PHP_DATE_TIMEZONE_PER_COUNTRY) {
4498            if (tzdb->data[table[i].pos + 5] == option[0] && tzdb->data[table[i].pos + 6] == option[1]) {
4499                add_next_index_string(return_value, table[i].id, 1);
4500            }
4501        } else if (what == PHP_DATE_TIMEZONE_GROUP_ALL_W_BC || (check_id_allowed(table[i].id, what) && (tzdb->data[table[i].pos + 4] == '\1'))) {
4502            add_next_index_string(return_value, table[i].id, 1);
4503        }
4504    };
4505}
4506/* }}} */
4507
4508/* {{{ proto array timezone_version_get()
4509   Returns the Olson database version number.
4510*/
4511PHP_FUNCTION(timezone_version_get)
4512{
4513    const timelib_tzdb *tzdb;
4514
4515    tzdb = DATE_TIMEZONEDB;
4516    RETURN_STRING(tzdb->version, 1);
4517}
4518/* }}} */
4519
4520/* {{{ proto array timezone_abbreviations_list()
4521   Returns associative array containing dst, offset and the timezone name
4522*/
4523PHP_FUNCTION(timezone_abbreviations_list)
4524{
4525    const timelib_tz_lookup_table *table, *entry;
4526    zval                          *element, **abbr_array_pp, *abbr_array;
4527
4528    table = timelib_timezone_abbreviations_list();
4529    array_init(return_value);
4530    entry = table;
4531
4532    do {
4533        MAKE_STD_ZVAL(element);
4534        array_init(element);
4535        add_assoc_bool(element, "dst", entry->type);
4536        add_assoc_long(element, "offset", entry->gmtoffset);
4537        if (entry->full_tz_name) {
4538            add_assoc_string(element, "timezone_id", entry->full_tz_name, 1);
4539        } else {
4540            add_assoc_null(element, "timezone_id");
4541        }
4542
4543        if (zend_hash_find(HASH_OF(return_value), entry->name, strlen(entry->name) + 1, (void **) &abbr_array_pp) == FAILURE) {
4544            MAKE_STD_ZVAL(abbr_array);
4545            array_init(abbr_array);
4546            add_assoc_zval(return_value, entry->name, abbr_array);
4547        } else {
4548            abbr_array = *abbr_array_pp;
4549        }
4550        add_next_index_zval(abbr_array, element);
4551        entry++;
4552    } while (entry->name);
4553}
4554/* }}} */
4555
4556/* {{{ proto bool date_default_timezone_set(string timezone_identifier)
4557   Sets the default timezone used by all date/time functions in a script */
4558PHP_FUNCTION(date_default_timezone_set)
4559{
4560    char *zone;
4561    int   zone_len;
4562
4563    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &zone, &zone_len) == FAILURE) {
4564        RETURN_FALSE;
4565    }
4566    if (!timelib_timezone_id_is_valid(zone, DATE_TIMEZONEDB)) {
4567        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Timezone ID '%s' is invalid", zone);
4568        RETURN_FALSE;
4569    }
4570    if (DATEG(timezone)) {
4571        efree(DATEG(timezone));
4572        DATEG(timezone) = NULL;
4573    }
4574    DATEG(timezone) = estrndup(zone, zone_len);
4575    RETURN_TRUE;
4576}
4577/* }}} */
4578
4579/* {{{ proto string date_default_timezone_get()
4580   Gets the default timezone used by all date/time functions in a script */
4581PHP_FUNCTION(date_default_timezone_get)
4582{
4583    timelib_tzinfo *default_tz;
4584
4585    default_tz = get_timezone_info(TSRMLS_C);
4586    RETVAL_STRING(default_tz->name, 1);
4587}
4588/* }}} */
4589
4590/* {{{ php_do_date_sunrise_sunset
4591 *  Common for date_sunrise() and date_sunset() functions
4592 */
4593static void php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAMETERS, int calc_sunset)
4594{
4595    double latitude = 0.0, longitude = 0.0, zenith = 0.0, gmt_offset = 0, altitude;
4596    double h_rise, h_set, N;
4597    timelib_sll rise, set, transit;
4598    long time, retformat = 0;
4599    int             rs;
4600    timelib_time   *t;
4601    timelib_tzinfo *tzi;
4602    char           *retstr;
4603
4604    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|ldddd", &time, &retformat, &latitude, &longitude, &zenith, &gmt_offset) == FAILURE) {
4605        RETURN_FALSE;
4606    }
4607
4608    switch (ZEND_NUM_ARGS()) {
4609        case 1:
4610            retformat = SUNFUNCS_RET_STRING;
4611        case 2:
4612            latitude = INI_FLT("date.default_latitude");
4613        case 3:
4614            longitude = INI_FLT("date.default_longitude");
4615        case 4:
4616            if (calc_sunset) {
4617                zenith = INI_FLT("date.sunset_zenith");
4618            } else {
4619                zenith = INI_FLT("date.sunrise_zenith");
4620            }
4621        case 5:
4622        case 6:
4623            break;
4624        default:
4625            php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid format");
4626            RETURN_FALSE;
4627            break;
4628    }
4629    if (retformat != SUNFUNCS_RET_TIMESTAMP &&
4630        retformat != SUNFUNCS_RET_STRING &&
4631        retformat != SUNFUNCS_RET_DOUBLE)
4632    {
4633        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");
4634        RETURN_FALSE;
4635    }
4636    altitude = 90 - zenith;
4637
4638    /* Initialize time struct */
4639    t = timelib_time_ctor();
4640    tzi = get_timezone_info(TSRMLS_C);
4641    t->tz_info = tzi;
4642    t->zone_type = TIMELIB_ZONETYPE_ID;
4643
4644    if (ZEND_NUM_ARGS() <= 5) {
4645        gmt_offset = timelib_get_current_offset(t) / 3600;
4646    }
4647
4648    timelib_unixtime2local(t, time);
4649    rs = timelib_astro_rise_set_altitude(t, longitude, latitude, altitude, 1, &h_rise, &h_set, &rise, &set, &transit);
4650    timelib_time_dtor(t);
4651
4652    if (rs != 0) {
4653        RETURN_FALSE;
4654    }
4655
4656    if (retformat == SUNFUNCS_RET_TIMESTAMP) {
4657        RETURN_LONG(calc_sunset ? set : rise);
4658    }
4659    N = (calc_sunset ? h_set : h_rise) + gmt_offset;
4660
4661    if (N > 24 || N < 0) {
4662        N -= floor(N / 24) * 24;
4663    }
4664
4665    switch (retformat) {
4666        case SUNFUNCS_RET_STRING:
4667            spprintf(&retstr, 0, "%02d:%02d", (int) N, (int) (60 * (N - (int) N)));
4668            RETURN_STRINGL(retstr, 5, 0);
4669            break;
4670        case SUNFUNCS_RET_DOUBLE:
4671            RETURN_DOUBLE(N);
4672            break;
4673    }
4674}
4675/* }}} */
4676
4677/* {{{ proto mixed date_sunrise(mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]])
4678   Returns time of sunrise for a given day and location */
4679PHP_FUNCTION(date_sunrise)
4680{
4681    php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4682}
4683/* }}} */
4684
4685/* {{{ proto mixed date_sunset(mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]])
4686   Returns time of sunset for a given day and location */
4687PHP_FUNCTION(date_sunset)
4688{
4689    php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4690}
4691/* }}} */
4692
4693/* {{{ proto array date_sun_info(long time, float latitude, float longitude)
4694   Returns an array with information about sun set/rise and twilight begin/end */
4695PHP_FUNCTION(date_sun_info)
4696{
4697    long            time;
4698    double          latitude, longitude;
4699    timelib_time   *t, *t2;
4700    timelib_tzinfo *tzi;
4701    int             rs;
4702    timelib_sll     rise, set, transit;
4703    int             dummy;
4704    double          ddummy;
4705
4706    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ldd", &time, &latitude, &longitude) == FAILURE) {
4707        RETURN_FALSE;
4708    }
4709    /* Initialize time struct */
4710    t = timelib_time_ctor();
4711    tzi = get_timezone_info(TSRMLS_C);
4712    t->tz_info = tzi;
4713    t->zone_type = TIMELIB_ZONETYPE_ID;
4714    timelib_unixtime2local(t, time);
4715
4716    /* Setup */
4717    t2 = timelib_time_ctor();
4718    array_init(return_value);
4719
4720    /* Get sun up/down and transit */
4721    rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -35.0/60, 1, &ddummy, &ddummy, &rise, &set, &transit);
4722    switch (rs) {
4723        case -1: /* always below */
4724            add_assoc_bool(return_value, "sunrise", 0);
4725            add_assoc_bool(return_value, "sunset", 0);
4726            break;
4727        case 1: /* always above */
4728            add_assoc_bool(return_value, "sunrise", 1);
4729            add_assoc_bool(return_value, "sunset", 1);
4730            break;
4731        default:
4732            t2->sse = rise;
4733            add_assoc_long(return_value, "sunrise", timelib_date_to_int(t2, &dummy));
4734            t2->sse = set;
4735            add_assoc_long(return_value, "sunset", timelib_date_to_int(t2, &dummy));
4736    }
4737    t2->sse = transit;
4738    add_assoc_long(return_value, "transit", timelib_date_to_int(t2, &dummy));
4739
4740    /* Get civil twilight */
4741    rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -6.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4742    switch (rs) {
4743        case -1: /* always below */
4744            add_assoc_bool(return_value, "civil_twilight_begin", 0);
4745            add_assoc_bool(return_value, "civil_twilight_end", 0);
4746            break;
4747        case 1: /* always above */
4748            add_assoc_bool(return_value, "civil_twilight_begin", 1);
4749            add_assoc_bool(return_value, "civil_twilight_end", 1);
4750            break;
4751        default:
4752            t2->sse = rise;
4753            add_assoc_long(return_value, "civil_twilight_begin", timelib_date_to_int(t2, &dummy));
4754            t2->sse = set;
4755            add_assoc_long(return_value, "civil_twilight_end", timelib_date_to_int(t2, &dummy));
4756    }
4757
4758    /* Get nautical twilight */
4759    rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -12.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4760    switch (rs) {
4761        case -1: /* always below */
4762            add_assoc_bool(return_value, "nautical_twilight_begin", 0);
4763            add_assoc_bool(return_value, "nautical_twilight_end", 0);
4764            break;
4765        case 1: /* always above */
4766            add_assoc_bool(return_value, "nautical_twilight_begin", 1);
4767            add_assoc_bool(return_value, "nautical_twilight_end", 1);
4768            break;
4769        default:
4770            t2->sse = rise;
4771            add_assoc_long(return_value, "nautical_twilight_begin", timelib_date_to_int(t2, &dummy));
4772            t2->sse = set;
4773            add_assoc_long(return_value, "nautical_twilight_end", timelib_date_to_int(t2, &dummy));
4774    }
4775
4776    /* Get astronomical twilight */
4777    rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -18.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4778    switch (rs) {
4779        case -1: /* always below */
4780            add_assoc_bool(return_value, "astronomical_twilight_begin", 0);
4781            add_assoc_bool(return_value, "astronomical_twilight_end", 0);
4782            break;
4783        case 1: /* always above */
4784            add_assoc_bool(return_value, "astronomical_twilight_begin", 1);
4785            add_assoc_bool(return_value, "astronomical_twilight_end", 1);
4786            break;
4787        default:
4788            t2->sse = rise;
4789            add_assoc_long(return_value, "astronomical_twilight_begin", timelib_date_to_int(t2, &dummy));
4790            t2->sse = set;
4791            add_assoc_long(return_value, "astronomical_twilight_end", timelib_date_to_int(t2, &dummy));
4792    }
4793    timelib_time_dtor(t);
4794    timelib_time_dtor(t2);
4795}
4796/* }}} */
4797
4798static HashTable *date_object_get_gc_period(zval *object, zval ***table, int *n TSRMLS_DC)
4799{
4800    *table = NULL;
4801    *n = 0;
4802    return zend_std_get_properties(object TSRMLS_CC);
4803}
4804
4805static HashTable *date_object_get_properties_period(zval *object TSRMLS_DC)
4806{
4807    HashTable       *props;
4808    zval            *zv;
4809    php_period_obj  *period_obj;
4810
4811    period_obj = zend_object_store_get_object(object TSRMLS_CC);
4812
4813    props = zend_std_get_properties(object TSRMLS_CC);
4814
4815    if (!period_obj->start || GC_G(gc_active)) {
4816        return props;
4817    }
4818
4819    MAKE_STD_ZVAL(zv);
4820    if (period_obj->start) {
4821        php_date_obj *date_obj;
4822        object_init_ex(zv, date_ce_date);
4823        date_obj = zend_object_store_get_object(zv TSRMLS_CC);
4824        date_obj->time = timelib_time_clone(period_obj->start);
4825    } else {
4826        ZVAL_NULL(zv);
4827    }
4828    zend_hash_update(props, "start", sizeof("start"), &zv, sizeof(zv), NULL);
4829
4830    MAKE_STD_ZVAL(zv);
4831    if (period_obj->current) {
4832        php_date_obj *date_obj;
4833        object_init_ex(zv, date_ce_date);
4834        date_obj = zend_object_store_get_object(zv TSRMLS_CC);
4835        date_obj->time = timelib_time_clone(period_obj->current);
4836    } else {
4837        ZVAL_NULL(zv);
4838    }
4839    zend_hash_update(props, "current", sizeof("current"), &zv, sizeof(zv), NULL);
4840
4841    MAKE_STD_ZVAL(zv);
4842    if (period_obj->end) {
4843        php_date_obj *date_obj;
4844        object_init_ex(zv, date_ce_date);
4845        date_obj = zend_object_store_get_object(zv TSRMLS_CC);
4846        date_obj->time = timelib_time_clone(period_obj->end);
4847    } else {
4848        ZVAL_NULL(zv);
4849    }
4850    zend_hash_update(props, "end", sizeof("end"), &zv, sizeof(zv), NULL);
4851
4852    MAKE_STD_ZVAL(zv);
4853    if (period_obj->interval) {
4854        php_interval_obj *interval_obj;
4855        object_init_ex(zv, date_ce_interval);
4856        interval_obj = zend_object_store_get_object(zv TSRMLS_CC);
4857        interval_obj->diff = timelib_rel_time_clone(period_obj->interval);
4858        interval_obj->initialized = 1;
4859    } else {
4860        ZVAL_NULL(zv);
4861    }
4862    zend_hash_update(props, "interval", sizeof("interval"), &zv, sizeof(zv), NULL);
4863
4864    /* converted to larger type (int->long); must check when unserializing */
4865    MAKE_STD_ZVAL(zv);
4866    ZVAL_LONG(zv, (long) period_obj->recurrences);
4867    zend_hash_update(props, "recurrences", sizeof("recurrences"), &zv, sizeof(zv), NULL);
4868
4869    MAKE_STD_ZVAL(zv);
4870    ZVAL_BOOL(zv, period_obj->include_start_date);
4871    zend_hash_update(props, "include_start_date", sizeof("include_start_date"), &zv, sizeof(zv), NULL);
4872
4873    return props;
4874}
4875
4876static int php_date_period_initialize_from_hash(php_period_obj *period_obj, HashTable *myht TSRMLS_DC)
4877{
4878    zval **ht_entry;
4879
4880    /* this function does no rollback on error */
4881
4882    if (zend_hash_find(myht, "start", sizeof("start"), (void**) &ht_entry) == SUCCESS) {
4883        if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) {
4884            php_date_obj *date_obj;
4885            date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
4886            period_obj->start = timelib_time_clone(date_obj->time);
4887            period_obj->start_ce = Z_OBJCE_PP(ht_entry);
4888        } else if (Z_TYPE_PP(ht_entry) != IS_NULL) {
4889            return 0;
4890        }
4891    } else {
4892        return 0;
4893    }
4894
4895    if (zend_hash_find(myht, "end", sizeof("end"), (void**) &ht_entry) == SUCCESS) {
4896        if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) {
4897            php_date_obj *date_obj;
4898            date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
4899            period_obj->end = timelib_time_clone(date_obj->time);
4900        } else if (Z_TYPE_PP(ht_entry) != IS_NULL) {
4901            return 0;
4902        }
4903    } else {
4904        return 0;
4905    }
4906
4907    if (zend_hash_find(myht, "current", sizeof("current"), (void**) &ht_entry) == SUCCESS) {
4908        if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) {
4909            php_date_obj *date_obj;
4910            date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
4911            period_obj->current = timelib_time_clone(date_obj->time);
4912        } else if (Z_TYPE_PP(ht_entry) != IS_NULL)  {
4913            return 0;
4914        }
4915    } else {
4916        return 0;
4917    }
4918
4919    if (zend_hash_find(myht, "interval", sizeof("interval"), (void**) &ht_entry) == SUCCESS) {
4920        if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_interval) {
4921            php_interval_obj *interval_obj;
4922            interval_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
4923            period_obj->interval = timelib_rel_time_clone(interval_obj->diff);
4924        } else { /* interval is required */
4925            return 0;
4926        }
4927    } else {
4928        return 0;
4929    }
4930
4931    if (zend_hash_find(myht, "recurrences", sizeof("recurrences"), (void**) &ht_entry) == SUCCESS &&
4932            Z_TYPE_PP(ht_entry) == IS_LONG && Z_LVAL_PP(ht_entry) >= 0 && Z_LVAL_PP(ht_entry) <= INT_MAX) {
4933        period_obj->recurrences = Z_LVAL_PP(ht_entry);
4934    } else {
4935        return 0;
4936    }
4937
4938    if (zend_hash_find(myht, "include_start_date", sizeof("include_start_date"), (void**) &ht_entry) == SUCCESS &&
4939            Z_TYPE_PP(ht_entry) == IS_BOOL) {
4940        period_obj->include_start_date = Z_BVAL_PP(ht_entry);
4941    } else {
4942        return 0;
4943    }
4944
4945    period_obj->initialized = 1;
4946
4947    return 1;
4948}
4949
4950/* {{{ proto DatePeriod::__set_state()
4951*/
4952PHP_METHOD(DatePeriod, __set_state)
4953{
4954    php_period_obj   *period_obj;
4955    zval             *array;
4956    HashTable        *myht;
4957
4958    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
4959        RETURN_FALSE;
4960    }
4961
4962    myht = Z_ARRVAL_P(array);
4963
4964    object_init_ex(return_value, date_ce_period);
4965    period_obj = zend_object_store_get_object(return_value TSRMLS_CC);
4966    if (!php_date_period_initialize_from_hash(period_obj, myht TSRMLS_CC)) {
4967        php_error(E_ERROR, "Invalid serialization data for DatePeriod object");
4968    }
4969}
4970/* }}} */
4971
4972/* {{{ proto DatePeriod::__wakeup()
4973*/
4974PHP_METHOD(DatePeriod, __wakeup)
4975{
4976    zval             *object = getThis();
4977    php_period_obj   *period_obj;
4978    HashTable        *myht;
4979
4980    period_obj = zend_object_store_get_object(object TSRMLS_CC);
4981
4982    myht = Z_OBJPROP_P(object);
4983
4984    if (!php_date_period_initialize_from_hash(period_obj, myht TSRMLS_CC)) {
4985        php_error(E_ERROR, "Invalid serialization data for DatePeriod object");
4986    }
4987}
4988/* }}} */
4989
4990/* {{{ date_period_read_property */
4991static zval *date_period_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
4992{
4993    zval *zv;
4994    if (type != BP_VAR_IS && type != BP_VAR_R) {
4995        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Retrieval of DatePeriod properties for modification is unsupported");
4996    }
4997
4998    Z_OBJPROP_P(object); /* build properties hash table */
4999
5000    zv = std_object_handlers.read_property(object, member, type, key TSRMLS_CC);
5001    if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJ_HANDLER_P(zv, clone_obj)) {
5002        /* defensive copy */
5003        zend_object_value zov = Z_OBJ_HANDLER_P(zv, clone_obj)(zv TSRMLS_CC);
5004        MAKE_STD_ZVAL(zv);
5005        Z_TYPE_P(zv) = IS_OBJECT;
5006        Z_OBJVAL_P(zv) = zov;
5007    }
5008
5009    return zv;
5010}
5011/* }}} */
5012
5013/* {{{ date_period_write_property */
5014static void date_period_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
5015{
5016    php_error_docref(NULL TSRMLS_CC, E_ERROR, "Writing to DatePeriod properties is unsupported");
5017}
5018/* }}} */
5019
5020
5021/*
5022 * Local variables:
5023 * tab-width: 4
5024 * c-basic-offset: 4
5025 * End:
5026 * vim600: fdm=marker
5027 * vim: noet sw=4 ts=4
5028 */
5029