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

Last change on this file since 31879 was 31879, checked in by brainslayer, 6 weeks ago

update php

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