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