source: src/router/php7/ext/date/php_date.c.orig @ 31873

Last change on this file since 31873 was 31873, checked in by brainslayer, 2 months ago

update php

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