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