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