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