xref: /PHP-8.0/ext/date/php_date.c (revision 7b480532)
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    | http://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) {
720 					length = slprintf(buffer, sizeof(buffer), "%s", "Z");
721 					break;
722 				}
723 				/* break intentionally missing */
724 			case 'P': rfc_colon = 1; /* break intentionally missing */
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++; /* break intentionally missing */
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 	zend_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 	zend_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 	zend_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 	zend_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 	zend_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 	zend_bool timestamp_is_null = 1;
1320 	zend_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 	zend_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 	zend_class_entry ce_date, ce_immutable, ce_timezone, ce_interval, ce_period, ce_interface;
1649 
1650 	INIT_CLASS_ENTRY(ce_interface, "DateTimeInterface", class_DateTimeInterface_methods);
1651 	date_ce_interface = zend_register_internal_interface(&ce_interface);
1652 	date_ce_interface->interface_gets_implemented = implement_date_interface_handler;
1653 
1654 #define REGISTER_DATE_INTERFACE_CONST_STRING(const_name, value) \
1655 	zend_declare_class_constant_stringl(date_ce_interface, const_name, sizeof(const_name)-1, value, sizeof(value)-1);
1656 
1657 	REGISTER_DATE_INTERFACE_CONST_STRING("ATOM",             DATE_FORMAT_RFC3339);
1658 	REGISTER_DATE_INTERFACE_CONST_STRING("COOKIE",           DATE_FORMAT_COOKIE);
1659 	REGISTER_DATE_INTERFACE_CONST_STRING("ISO8601",          DATE_FORMAT_ISO8601);
1660 	REGISTER_DATE_INTERFACE_CONST_STRING("RFC822",           DATE_FORMAT_RFC822);
1661 	REGISTER_DATE_INTERFACE_CONST_STRING("RFC850",           DATE_FORMAT_RFC850);
1662 	REGISTER_DATE_INTERFACE_CONST_STRING("RFC1036",          DATE_FORMAT_RFC1036);
1663 	REGISTER_DATE_INTERFACE_CONST_STRING("RFC1123",          DATE_FORMAT_RFC1123);
1664 	REGISTER_DATE_INTERFACE_CONST_STRING("RFC7231",          DATE_FORMAT_RFC7231);
1665 	REGISTER_DATE_INTERFACE_CONST_STRING("RFC2822",          DATE_FORMAT_RFC2822);
1666 	REGISTER_DATE_INTERFACE_CONST_STRING("RFC3339",          DATE_FORMAT_RFC3339);
1667 	REGISTER_DATE_INTERFACE_CONST_STRING("RFC3339_EXTENDED", DATE_FORMAT_RFC3339_EXTENDED);
1668 	REGISTER_DATE_INTERFACE_CONST_STRING("RSS",              DATE_FORMAT_RFC1123);
1669 	REGISTER_DATE_INTERFACE_CONST_STRING("W3C",              DATE_FORMAT_RFC3339);
1670 
1671 	INIT_CLASS_ENTRY(ce_date, "DateTime", class_DateTime_methods);
1672 	ce_date.create_object = date_object_new_date;
1673 	date_ce_date = zend_register_internal_class_ex(&ce_date, NULL);
1674 	memcpy(&date_object_handlers_date, &std_object_handlers, sizeof(zend_object_handlers));
1675 	date_object_handlers_date.offset = XtOffsetOf(php_date_obj, std);
1676 	date_object_handlers_date.free_obj = date_object_free_storage_date;
1677 	date_object_handlers_date.clone_obj = date_object_clone_date;
1678 	date_object_handlers_date.compare = date_object_compare_date;
1679 	date_object_handlers_date.get_properties_for = date_object_get_properties_for;
1680 	date_object_handlers_date.get_gc = date_object_get_gc;
1681 	zend_class_implements(date_ce_date, 1, date_ce_interface);
1682 
1683 	INIT_CLASS_ENTRY(ce_immutable, "DateTimeImmutable", class_DateTimeImmutable_methods);
1684 	ce_immutable.create_object = date_object_new_date;
1685 	date_ce_immutable = zend_register_internal_class_ex(&ce_immutable, NULL);
1686 	memcpy(&date_object_handlers_immutable, &std_object_handlers, sizeof(zend_object_handlers));
1687 	date_object_handlers_immutable.clone_obj = date_object_clone_date;
1688 	date_object_handlers_immutable.compare = date_object_compare_date;
1689 	date_object_handlers_immutable.get_properties_for = date_object_get_properties_for;
1690 	date_object_handlers_immutable.get_gc = date_object_get_gc;
1691 	zend_class_implements(date_ce_immutable, 1, date_ce_interface);
1692 
1693 	INIT_CLASS_ENTRY(ce_timezone, "DateTimeZone", class_DateTimeZone_methods);
1694 	ce_timezone.create_object = date_object_new_timezone;
1695 	date_ce_timezone = zend_register_internal_class_ex(&ce_timezone, NULL);
1696 	memcpy(&date_object_handlers_timezone, &std_object_handlers, sizeof(zend_object_handlers));
1697 	date_object_handlers_timezone.offset = XtOffsetOf(php_timezone_obj, std);
1698 	date_object_handlers_timezone.free_obj = date_object_free_storage_timezone;
1699 	date_object_handlers_timezone.clone_obj = date_object_clone_timezone;
1700 	date_object_handlers_timezone.get_properties_for = date_object_get_properties_for_timezone;
1701 	date_object_handlers_timezone.get_gc = date_object_get_gc_timezone;
1702 	date_object_handlers_timezone.get_debug_info = date_object_get_debug_info_timezone;
1703 	date_object_handlers_timezone.compare = date_object_compare_timezone;
1704 
1705 #define REGISTER_TIMEZONE_CLASS_CONST_STRING(const_name, value) \
1706 	zend_declare_class_constant_long(date_ce_timezone, const_name, sizeof(const_name)-1, value);
1707 
1708 	REGISTER_TIMEZONE_CLASS_CONST_STRING("AFRICA",      PHP_DATE_TIMEZONE_GROUP_AFRICA);
1709 	REGISTER_TIMEZONE_CLASS_CONST_STRING("AMERICA",     PHP_DATE_TIMEZONE_GROUP_AMERICA);
1710 	REGISTER_TIMEZONE_CLASS_CONST_STRING("ANTARCTICA",  PHP_DATE_TIMEZONE_GROUP_ANTARCTICA);
1711 	REGISTER_TIMEZONE_CLASS_CONST_STRING("ARCTIC",      PHP_DATE_TIMEZONE_GROUP_ARCTIC);
1712 	REGISTER_TIMEZONE_CLASS_CONST_STRING("ASIA",        PHP_DATE_TIMEZONE_GROUP_ASIA);
1713 	REGISTER_TIMEZONE_CLASS_CONST_STRING("ATLANTIC",    PHP_DATE_TIMEZONE_GROUP_ATLANTIC);
1714 	REGISTER_TIMEZONE_CLASS_CONST_STRING("AUSTRALIA",   PHP_DATE_TIMEZONE_GROUP_AUSTRALIA);
1715 	REGISTER_TIMEZONE_CLASS_CONST_STRING("EUROPE",      PHP_DATE_TIMEZONE_GROUP_EUROPE);
1716 	REGISTER_TIMEZONE_CLASS_CONST_STRING("INDIAN",      PHP_DATE_TIMEZONE_GROUP_INDIAN);
1717 	REGISTER_TIMEZONE_CLASS_CONST_STRING("PACIFIC",     PHP_DATE_TIMEZONE_GROUP_PACIFIC);
1718 	REGISTER_TIMEZONE_CLASS_CONST_STRING("UTC",         PHP_DATE_TIMEZONE_GROUP_UTC);
1719 	REGISTER_TIMEZONE_CLASS_CONST_STRING("ALL",         PHP_DATE_TIMEZONE_GROUP_ALL);
1720 	REGISTER_TIMEZONE_CLASS_CONST_STRING("ALL_WITH_BC", PHP_DATE_TIMEZONE_GROUP_ALL_W_BC);
1721 	REGISTER_TIMEZONE_CLASS_CONST_STRING("PER_COUNTRY", PHP_DATE_TIMEZONE_PER_COUNTRY);
1722 
1723 	INIT_CLASS_ENTRY(ce_interval, "DateInterval", class_DateInterval_methods);
1724 	ce_interval.create_object = date_object_new_interval;
1725 	date_ce_interval = zend_register_internal_class_ex(&ce_interval, NULL);
1726 	memcpy(&date_object_handlers_interval, &std_object_handlers, sizeof(zend_object_handlers));
1727 	date_object_handlers_interval.offset = XtOffsetOf(php_interval_obj, std);
1728 	date_object_handlers_interval.free_obj = date_object_free_storage_interval;
1729 	date_object_handlers_interval.clone_obj = date_object_clone_interval;
1730 	date_object_handlers_interval.has_property = date_interval_has_property;
1731 	date_object_handlers_interval.read_property = date_interval_read_property;
1732 	date_object_handlers_interval.write_property = date_interval_write_property;
1733 	date_object_handlers_interval.get_properties = date_object_get_properties_interval;
1734 	date_object_handlers_interval.get_property_ptr_ptr = date_interval_get_property_ptr_ptr;
1735 	date_object_handlers_interval.get_gc = date_object_get_gc_interval;
1736 	date_object_handlers_interval.compare = date_interval_compare_objects;
1737 
1738 	INIT_CLASS_ENTRY(ce_period, "DatePeriod", class_DatePeriod_methods);
1739 	ce_period.create_object = date_object_new_period;
1740 	date_ce_period = zend_register_internal_class_ex(&ce_period, NULL);
1741 	date_ce_period->get_iterator = date_object_period_get_iterator;
1742 	zend_class_implements(date_ce_period, 1, zend_ce_aggregate);
1743 	memcpy(&date_object_handlers_period, &std_object_handlers, sizeof(zend_object_handlers));
1744 	date_object_handlers_period.offset = XtOffsetOf(php_period_obj, std);
1745 	date_object_handlers_period.free_obj = date_object_free_storage_period;
1746 	date_object_handlers_period.clone_obj = date_object_clone_period;
1747 	date_object_handlers_period.get_properties = date_object_get_properties_period;
1748 	date_object_handlers_period.get_property_ptr_ptr = date_period_get_property_ptr_ptr;
1749 	date_object_handlers_period.get_gc = date_object_get_gc_period;
1750 	date_object_handlers_period.read_property = date_period_read_property;
1751 	date_object_handlers_period.write_property = date_period_write_property;
1752 
1753 #define REGISTER_PERIOD_CLASS_CONST_STRING(const_name, value) \
1754 	zend_declare_class_constant_long(date_ce_period, const_name, sizeof(const_name)-1, value);
1755 
1756 	REGISTER_PERIOD_CLASS_CONST_STRING("EXCLUDE_START_DATE", PHP_DATE_PERIOD_EXCLUDE_START_DATE);
1757 } /* }}} */
1758 
date_object_new_date(zend_class_entry * class_type)1759 static zend_object *date_object_new_date(zend_class_entry *class_type) /* {{{ */
1760 {
1761 	php_date_obj *intern = zend_object_alloc(sizeof(php_date_obj), class_type);
1762 
1763 	zend_object_std_init(&intern->std, class_type);
1764 	object_properties_init(&intern->std, class_type);
1765 	intern->std.handlers = &date_object_handlers_date;
1766 
1767 	return &intern->std;
1768 } /* }}} */
1769 
date_object_clone_date(zend_object * this_ptr)1770 static zend_object *date_object_clone_date(zend_object *this_ptr) /* {{{ */
1771 {
1772 	php_date_obj *old_obj = php_date_obj_from_obj(this_ptr);
1773 	php_date_obj *new_obj = php_date_obj_from_obj(date_object_new_date(old_obj->std.ce));
1774 
1775 	zend_objects_clone_members(&new_obj->std, &old_obj->std);
1776 	if (!old_obj->time) {
1777 		return &new_obj->std;
1778 	}
1779 
1780 	/* this should probably moved to a new `timelib_time *timelime_time_clone(timelib_time *)` */
1781 	new_obj->time = timelib_time_ctor();
1782 	*new_obj->time = *old_obj->time;
1783 	if (old_obj->time->tz_abbr) {
1784 		new_obj->time->tz_abbr = timelib_strdup(old_obj->time->tz_abbr);
1785 	}
1786 	if (old_obj->time->tz_info) {
1787 		new_obj->time->tz_info = old_obj->time->tz_info;
1788 	}
1789 
1790 	return &new_obj->std;
1791 } /* }}} */
1792 
date_clone_immutable(zval * object,zval * new_object)1793 static void date_clone_immutable(zval *object, zval *new_object) /* {{{ */
1794 {
1795 	ZVAL_OBJ(new_object, date_object_clone_date(Z_OBJ_P(object)));
1796 } /* }}} */
1797 
date_object_compare_date(zval * d1,zval * d2)1798 static int date_object_compare_date(zval *d1, zval *d2) /* {{{ */
1799 {
1800 	php_date_obj *o1;
1801 	php_date_obj *o2;
1802 
1803 	ZEND_COMPARE_OBJECTS_FALLBACK(d1, d2);
1804 
1805 	o1 = Z_PHPDATE_P(d1);
1806 	o2 = Z_PHPDATE_P(d2);
1807 
1808 	if (!o1->time || !o2->time) {
1809 		php_error_docref(NULL, E_WARNING, "Trying to compare an incomplete DateTime or DateTimeImmutable object");
1810 		return ZEND_UNCOMPARABLE;
1811 	}
1812 	if (!o1->time->sse_uptodate) {
1813 		timelib_update_ts(o1->time, o1->time->tz_info);
1814 	}
1815 	if (!o2->time->sse_uptodate) {
1816 		timelib_update_ts(o2->time, o2->time->tz_info);
1817 	}
1818 
1819 	return timelib_time_compare(o1->time, o2->time);
1820 } /* }}} */
1821 
date_object_get_gc(zend_object * object,zval ** table,int * n)1822 static HashTable *date_object_get_gc(zend_object *object, zval **table, int *n) /* {{{ */
1823 {
1824 	*table = NULL;
1825 	*n = 0;
1826 	return zend_std_get_properties(object);
1827 } /* }}} */
1828 
date_object_get_gc_timezone(zend_object * object,zval ** table,int * n)1829 static HashTable *date_object_get_gc_timezone(zend_object *object, zval **table, int *n) /* {{{ */
1830 {
1831        *table = NULL;
1832        *n = 0;
1833        return zend_std_get_properties(object);
1834 } /* }}} */
1835 
date_object_get_properties_for(zend_object * object,zend_prop_purpose purpose)1836 static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_purpose purpose) /* {{{ */
1837 {
1838 	HashTable *props;
1839 	zval zv;
1840 	php_date_obj *dateobj;
1841 
1842 	switch (purpose) {
1843 		case ZEND_PROP_PURPOSE_DEBUG:
1844 		case ZEND_PROP_PURPOSE_SERIALIZE:
1845 		case ZEND_PROP_PURPOSE_VAR_EXPORT:
1846 		case ZEND_PROP_PURPOSE_JSON:
1847 		case ZEND_PROP_PURPOSE_ARRAY_CAST:
1848 			break;
1849 		default:
1850 			return zend_std_get_properties_for(object, purpose);
1851 	}
1852 
1853 	dateobj = php_date_obj_from_obj(object);
1854 	props = zend_array_dup(zend_std_get_properties(object));
1855 	if (!dateobj->time) {
1856 		return props;
1857 	}
1858 
1859 	/* first we add the date and time in ISO format */
1860 	ZVAL_STR(&zv, date_format("Y-m-d H:i:s.u", sizeof("Y-m-d H:i:s.u")-1, dateobj->time, 1));
1861 	zend_hash_str_update(props, "date", sizeof("date")-1, &zv);
1862 
1863 	/* then we add the timezone name (or similar) */
1864 	if (dateobj->time->is_localtime) {
1865 		ZVAL_LONG(&zv, dateobj->time->zone_type);
1866 		zend_hash_str_update(props, "timezone_type", sizeof("timezone_type")-1, &zv);
1867 
1868 		switch (dateobj->time->zone_type) {
1869 			case TIMELIB_ZONETYPE_ID:
1870 				ZVAL_STRING(&zv, dateobj->time->tz_info->name);
1871 				break;
1872 			case TIMELIB_ZONETYPE_OFFSET: {
1873 				zend_string *tmpstr = zend_string_alloc(sizeof("UTC+05:00")-1, 0);
1874 				int utc_offset = dateobj->time->z;
1875 
1876 				ZSTR_LEN(tmpstr) = snprintf(ZSTR_VAL(tmpstr), sizeof("+05:00"), "%c%02d:%02d",
1877 					utc_offset < 0 ? '-' : '+',
1878 					abs(utc_offset / 3600),
1879 					abs(((utc_offset % 3600) / 60)));
1880 
1881 				ZVAL_NEW_STR(&zv, tmpstr);
1882 				}
1883 				break;
1884 			case TIMELIB_ZONETYPE_ABBR:
1885 				ZVAL_STRING(&zv, dateobj->time->tz_abbr);
1886 				break;
1887 		}
1888 		zend_hash_str_update(props, "timezone", sizeof("timezone")-1, &zv);
1889 	}
1890 
1891 	return props;
1892 } /* }}} */
1893 
date_object_new_timezone(zend_class_entry * class_type)1894 static zend_object *date_object_new_timezone(zend_class_entry *class_type) /* {{{ */
1895 {
1896 	php_timezone_obj *intern = zend_object_alloc(sizeof(php_timezone_obj), class_type);
1897 
1898 	zend_object_std_init(&intern->std, class_type);
1899 	object_properties_init(&intern->std, class_type);
1900 	intern->std.handlers = &date_object_handlers_timezone;
1901 
1902 	return &intern->std;
1903 } /* }}} */
1904 
date_object_clone_timezone(zend_object * this_ptr)1905 static zend_object *date_object_clone_timezone(zend_object *this_ptr) /* {{{ */
1906 {
1907 	php_timezone_obj *old_obj = php_timezone_obj_from_obj(this_ptr);
1908 	php_timezone_obj *new_obj = php_timezone_obj_from_obj(date_object_new_timezone(old_obj->std.ce));
1909 
1910 	zend_objects_clone_members(&new_obj->std, &old_obj->std);
1911 	if (!old_obj->initialized) {
1912 		return &new_obj->std;
1913 	}
1914 
1915 	new_obj->type = old_obj->type;
1916 	new_obj->initialized = 1;
1917 	switch (new_obj->type) {
1918 		case TIMELIB_ZONETYPE_ID:
1919 			new_obj->tzi.tz = old_obj->tzi.tz;
1920 			break;
1921 		case TIMELIB_ZONETYPE_OFFSET:
1922 			new_obj->tzi.utc_offset = old_obj->tzi.utc_offset;
1923 			break;
1924 		case TIMELIB_ZONETYPE_ABBR:
1925 			new_obj->tzi.z.utc_offset = old_obj->tzi.z.utc_offset;
1926 			new_obj->tzi.z.dst        = old_obj->tzi.z.dst;
1927 			new_obj->tzi.z.abbr       = timelib_strdup(old_obj->tzi.z.abbr);
1928 			break;
1929 	}
1930 
1931 	return &new_obj->std;
1932 } /* }}} */
1933 
date_object_compare_timezone(zval * tz1,zval * tz2)1934 static int date_object_compare_timezone(zval *tz1, zval *tz2) /* {{{ */
1935 {
1936 	php_timezone_obj *o1, *o2;
1937 
1938 	ZEND_COMPARE_OBJECTS_FALLBACK(tz1, tz2);
1939 
1940 	o1 = Z_PHPTIMEZONE_P(tz1);
1941 	o2 = Z_PHPTIMEZONE_P(tz2);
1942 
1943 	if (!o1->initialized || !o2->initialized) {
1944 		zend_throw_error(NULL, "Trying to compare uninitialized DateTimeZone objects");
1945 		return 1;
1946 	}
1947 
1948 	if (o1->type != o2->type) {
1949 		php_error_docref(NULL, E_WARNING, "Trying to compare different kinds of DateTimeZone objects");
1950 		return ZEND_UNCOMPARABLE;
1951 	}
1952 
1953 	switch (o1->type) {
1954 		case TIMELIB_ZONETYPE_OFFSET:
1955 			return o1->tzi.utc_offset == o2->tzi.utc_offset ? 0 : 1;
1956 		case TIMELIB_ZONETYPE_ABBR:
1957 			return strcmp(o1->tzi.z.abbr, o2->tzi.z.abbr) ? 1 : 0;
1958 		case TIMELIB_ZONETYPE_ID:
1959 			return strcmp(o1->tzi.tz->name, o2->tzi.tz->name) ? 1 : 0;
1960 		EMPTY_SWITCH_DEFAULT_CASE();
1961 	}
1962 } /* }}} */
1963 
php_timezone_to_string(php_timezone_obj * tzobj,zval * zv)1964 static void php_timezone_to_string(php_timezone_obj *tzobj, zval *zv)
1965 {
1966 	switch (tzobj->type) {
1967 		case TIMELIB_ZONETYPE_ID:
1968 			ZVAL_STRING(zv, tzobj->tzi.tz->name);
1969 			break;
1970 		case TIMELIB_ZONETYPE_OFFSET: {
1971 			zend_string *tmpstr = zend_string_alloc(sizeof("UTC+05:00")-1, 0);
1972 			timelib_sll utc_offset = tzobj->tzi.utc_offset;
1973 
1974 			ZSTR_LEN(tmpstr) = snprintf(ZSTR_VAL(tmpstr), sizeof("+05:00"), "%c%02d:%02d",
1975 				utc_offset < 0 ? '-' : '+',
1976 				abs((int)(utc_offset / 3600)),
1977 				abs((int)(utc_offset % 3600) / 60));
1978 
1979 			ZVAL_NEW_STR(zv, tmpstr);
1980 			}
1981 			break;
1982 		case TIMELIB_ZONETYPE_ABBR:
1983 			ZVAL_STRING(zv, tzobj->tzi.z.abbr);
1984 			break;
1985 	}
1986 }
1987 
date_object_get_properties_for_timezone(zend_object * object,zend_prop_purpose purpose)1988 static HashTable *date_object_get_properties_for_timezone(zend_object *object, zend_prop_purpose purpose) /* {{{ */
1989 {
1990 	HashTable *props;
1991 	zval zv;
1992 	php_timezone_obj *tzobj;
1993 
1994 	switch (purpose) {
1995 		case ZEND_PROP_PURPOSE_DEBUG:
1996 		case ZEND_PROP_PURPOSE_SERIALIZE:
1997 		case ZEND_PROP_PURPOSE_VAR_EXPORT:
1998 		case ZEND_PROP_PURPOSE_JSON:
1999 		case ZEND_PROP_PURPOSE_ARRAY_CAST:
2000 			break;
2001 		default:
2002 			return zend_std_get_properties_for(object, purpose);
2003 	}
2004 
2005 	tzobj = php_timezone_obj_from_obj(object);
2006 	props = zend_array_dup(zend_std_get_properties(object));
2007 	if (!tzobj->initialized) {
2008 		return props;
2009 	}
2010 
2011 	ZVAL_LONG(&zv, tzobj->type);
2012 	zend_hash_str_update(props, "timezone_type", sizeof("timezone_type")-1, &zv);
2013 
2014 	php_timezone_to_string(tzobj, &zv);
2015 	zend_hash_str_update(props, "timezone", sizeof("timezone")-1, &zv);
2016 
2017 	return props;
2018 } /* }}} */
2019 
date_object_get_debug_info_timezone(zend_object * object,int * is_temp)2020 static HashTable *date_object_get_debug_info_timezone(zend_object *object, int *is_temp) /* {{{ */
2021 {
2022 	HashTable *ht, *props;
2023 	zval zv;
2024 	php_timezone_obj *tzobj;
2025 
2026 	tzobj = php_timezone_obj_from_obj(object);
2027 	props = zend_std_get_properties(object);
2028 
2029 	*is_temp = 1;
2030 	ht = zend_array_dup(props);
2031 
2032 	ZVAL_LONG(&zv, tzobj->type);
2033 	zend_hash_str_update(ht, "timezone_type", sizeof("timezone_type")-1, &zv);
2034 
2035 	php_timezone_to_string(tzobj, &zv);
2036 	zend_hash_str_update(ht, "timezone", sizeof("timezone")-1, &zv);
2037 
2038 	return ht;
2039 } /* }}} */
2040 
date_object_new_interval(zend_class_entry * class_type)2041 static zend_object *date_object_new_interval(zend_class_entry *class_type) /* {{{ */
2042 {
2043 	php_interval_obj *intern = zend_object_alloc(sizeof(php_interval_obj), class_type);
2044 
2045 	zend_object_std_init(&intern->std, class_type);
2046 	object_properties_init(&intern->std, class_type);
2047 	intern->std.handlers = &date_object_handlers_interval;
2048 
2049 	return &intern->std;
2050 } /* }}} */
2051 
date_object_clone_interval(zend_object * this_ptr)2052 static zend_object *date_object_clone_interval(zend_object *this_ptr) /* {{{ */
2053 {
2054 	php_interval_obj *old_obj = php_interval_obj_from_obj(this_ptr);
2055 	php_interval_obj *new_obj = php_interval_obj_from_obj(date_object_new_interval(old_obj->std.ce));
2056 
2057 	zend_objects_clone_members(&new_obj->std, &old_obj->std);
2058 	new_obj->initialized = old_obj->initialized;
2059 	if (old_obj->diff) {
2060 		new_obj->diff = timelib_rel_time_clone(old_obj->diff);
2061 	}
2062 
2063 	return &new_obj->std;
2064 } /* }}} */
2065 
date_object_get_gc_interval(zend_object * object,zval ** table,int * n)2066 static HashTable *date_object_get_gc_interval(zend_object *object, zval **table, int *n) /* {{{ */
2067 {
2068 
2069 	*table = NULL;
2070 	*n = 0;
2071 	return zend_std_get_properties(object);
2072 } /* }}} */
2073 
date_object_get_properties_interval(zend_object * object)2074 static HashTable *date_object_get_properties_interval(zend_object *object) /* {{{ */
2075 {
2076 	HashTable *props;
2077 	zval zv;
2078 	php_interval_obj *intervalobj;
2079 
2080 	intervalobj = php_interval_obj_from_obj(object);
2081 	props = zend_std_get_properties(object);
2082 	if (!intervalobj->initialized) {
2083 		return props;
2084 	}
2085 
2086 #define PHP_DATE_INTERVAL_ADD_PROPERTY(n,f) \
2087 	ZVAL_LONG(&zv, (zend_long)intervalobj->diff->f); \
2088 	zend_hash_str_update(props, n, sizeof(n)-1, &zv);
2089 
2090 	PHP_DATE_INTERVAL_ADD_PROPERTY("y", y);
2091 	PHP_DATE_INTERVAL_ADD_PROPERTY("m", m);
2092 	PHP_DATE_INTERVAL_ADD_PROPERTY("d", d);
2093 	PHP_DATE_INTERVAL_ADD_PROPERTY("h", h);
2094 	PHP_DATE_INTERVAL_ADD_PROPERTY("i", i);
2095 	PHP_DATE_INTERVAL_ADD_PROPERTY("s", s);
2096 	ZVAL_DOUBLE(&zv, (double)intervalobj->diff->us / 1000000.0);
2097 	zend_hash_str_update(props, "f", sizeof("f") - 1, &zv);
2098 	PHP_DATE_INTERVAL_ADD_PROPERTY("weekday", weekday);
2099 	PHP_DATE_INTERVAL_ADD_PROPERTY("weekday_behavior", weekday_behavior);
2100 	PHP_DATE_INTERVAL_ADD_PROPERTY("first_last_day_of", first_last_day_of);
2101 	PHP_DATE_INTERVAL_ADD_PROPERTY("invert", invert);
2102 	if (intervalobj->diff->days != -99999) {
2103 		PHP_DATE_INTERVAL_ADD_PROPERTY("days", days);
2104 	} else {
2105 		ZVAL_FALSE(&zv);
2106 		zend_hash_str_update(props, "days", sizeof("days")-1, &zv);
2107 	}
2108 	PHP_DATE_INTERVAL_ADD_PROPERTY("special_type", special.type);
2109 	PHP_DATE_INTERVAL_ADD_PROPERTY("special_amount", special.amount);
2110 	PHP_DATE_INTERVAL_ADD_PROPERTY("have_weekday_relative", have_weekday_relative);
2111 	PHP_DATE_INTERVAL_ADD_PROPERTY("have_special_relative", have_special_relative);
2112 
2113 	return props;
2114 } /* }}} */
2115 
date_object_new_period(zend_class_entry * class_type)2116 static zend_object *date_object_new_period(zend_class_entry *class_type) /* {{{ */
2117 {
2118 	php_period_obj *intern = zend_object_alloc(sizeof(php_period_obj), class_type);
2119 
2120 	zend_object_std_init(&intern->std, class_type);
2121 	object_properties_init(&intern->std, class_type);
2122 
2123 	intern->std.handlers = &date_object_handlers_period;
2124 
2125 	return &intern->std;
2126 } /* }}} */
2127 
date_object_clone_period(zend_object * this_ptr)2128 static zend_object *date_object_clone_period(zend_object *this_ptr) /* {{{ */
2129 {
2130 	php_period_obj *old_obj = php_period_obj_from_obj(this_ptr);
2131 	php_period_obj *new_obj = php_period_obj_from_obj(date_object_new_period(old_obj->std.ce));
2132 
2133 	zend_objects_clone_members(&new_obj->std, &old_obj->std);
2134 	new_obj->initialized = old_obj->initialized;
2135 	new_obj->recurrences = old_obj->recurrences;
2136 	new_obj->include_start_date = old_obj->include_start_date;
2137 	new_obj->start_ce = old_obj->start_ce;
2138 
2139 	if (old_obj->start) {
2140 		new_obj->start = timelib_time_clone(old_obj->start);
2141 	}
2142 	if (old_obj->current) {
2143 		new_obj->current = timelib_time_clone(old_obj->current);
2144 	}
2145 	if (old_obj->end) {
2146         new_obj->end = timelib_time_clone(old_obj->end);
2147     }
2148     if (old_obj->interval) {
2149         new_obj->interval = timelib_rel_time_clone(old_obj->interval);
2150     }
2151 	return &new_obj->std;
2152 } /* }}} */
2153 
date_object_free_storage_date(zend_object * object)2154 static void date_object_free_storage_date(zend_object *object) /* {{{ */
2155 {
2156 	php_date_obj *intern = php_date_obj_from_obj(object);
2157 
2158 	if (intern->time) {
2159 		timelib_time_dtor(intern->time);
2160 	}
2161 
2162 	zend_object_std_dtor(&intern->std);
2163 } /* }}} */
2164 
date_object_free_storage_timezone(zend_object * object)2165 static void date_object_free_storage_timezone(zend_object *object) /* {{{ */
2166 {
2167 	php_timezone_obj *intern = php_timezone_obj_from_obj(object);
2168 
2169 	if (intern->type == TIMELIB_ZONETYPE_ABBR) {
2170 		timelib_free(intern->tzi.z.abbr);
2171 	}
2172 	zend_object_std_dtor(&intern->std);
2173 } /* }}} */
2174 
date_object_free_storage_interval(zend_object * object)2175 static void date_object_free_storage_interval(zend_object *object) /* {{{ */
2176 {
2177 	php_interval_obj *intern = php_interval_obj_from_obj(object);
2178 
2179 	timelib_rel_time_dtor(intern->diff);
2180 	zend_object_std_dtor(&intern->std);
2181 } /* }}} */
2182 
date_object_free_storage_period(zend_object * object)2183 static void date_object_free_storage_period(zend_object *object) /* {{{ */
2184 {
2185 	php_period_obj *intern = php_period_obj_from_obj(object);
2186 
2187 	if (intern->start) {
2188 		timelib_time_dtor(intern->start);
2189 	}
2190 
2191 	if (intern->current) {
2192 		timelib_time_dtor(intern->current);
2193 	}
2194 
2195 	if (intern->end) {
2196 		timelib_time_dtor(intern->end);
2197 	}
2198 
2199 	timelib_rel_time_dtor(intern->interval);
2200 	zend_object_std_dtor(&intern->std);
2201 } /* }}} */
2202 
2203 /* Advanced Interface */
php_date_instantiate(zend_class_entry * pce,zval * object)2204 PHPAPI zval *php_date_instantiate(zend_class_entry *pce, zval *object) /* {{{ */
2205 {
2206 	object_init_ex(object, pce);
2207 	return object;
2208 } /* }}} */
2209 
2210 /* Helper function used to store the latest found warnings and errors while
2211  * parsing, from either strtotime or parse_from_format. */
update_errors_warnings(timelib_error_container * last_errors)2212 static void update_errors_warnings(timelib_error_container *last_errors) /* {{{ */
2213 {
2214 	if (DATEG(last_errors)) {
2215 		timelib_error_container_dtor(DATEG(last_errors));
2216 		DATEG(last_errors) = NULL;
2217 	}
2218 	DATEG(last_errors) = last_errors;
2219 } /* }}} */
2220 
php_date_set_time_fraction(timelib_time * time,int microseconds)2221 static void php_date_set_time_fraction(timelib_time *time, int microseconds)
2222 {
2223 	time->us = microseconds;
2224 }
2225 
php_date_get_current_time_with_fraction(time_t * sec,suseconds_t * usec)2226 static void php_date_get_current_time_with_fraction(time_t *sec, suseconds_t *usec)
2227 {
2228 #if HAVE_GETTIMEOFDAY
2229 	struct timeval tp = {0}; /* For setting microseconds */
2230 
2231 	gettimeofday(&tp, NULL);
2232 	*sec = tp.tv_sec;
2233 	*usec = tp.tv_usec;
2234 #else
2235 	*sec = time(NULL);
2236 	*usec = 0;
2237 #endif
2238 }
2239 
php_date_initialize(php_date_obj * dateobj,const char * time_str,size_t time_str_len,const char * format,zval * timezone_object,int flags)2240 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) /* {{{ */
2241 {
2242 	timelib_time   *now;
2243 	timelib_tzinfo *tzi = NULL;
2244 	timelib_error_container *err = NULL;
2245 	int type = TIMELIB_ZONETYPE_ID, new_dst = 0;
2246 	char *new_abbr = NULL;
2247 	timelib_sll new_offset = 0;
2248 	time_t sec;
2249 	suseconds_t usec;
2250 	int options = 0;
2251 
2252 	if (dateobj->time) {
2253 		timelib_time_dtor(dateobj->time);
2254 	}
2255 	if (format) {
2256 		if (time_str_len == 0) {
2257 			time_str = "";
2258 		}
2259 		dateobj->time = timelib_parse_from_format(format, time_str, time_str_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2260 	} else {
2261 		if (time_str_len == 0) {
2262 			time_str = "now";
2263 			time_str_len = sizeof("now") - 1;
2264 		}
2265 		dateobj->time = timelib_strtotime(time_str, time_str_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2266 	}
2267 
2268 	/* update last errors and warnings */
2269 	update_errors_warnings(err);
2270 
2271 
2272 	if ((flags & PHP_DATE_INIT_CTOR) && err && err->error_count) {
2273 		/* spit out the first library error message, at least */
2274 		php_error_docref(NULL, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", time_str,
2275 			err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message);
2276 	}
2277 	if (err && err->error_count) {
2278 		timelib_time_dtor(dateobj->time);
2279 		dateobj->time = 0;
2280 		return 0;
2281 	}
2282 
2283 	if (timezone_object) {
2284 		php_timezone_obj *tzobj;
2285 
2286 		tzobj = Z_PHPTIMEZONE_P(timezone_object);
2287 		switch (tzobj->type) {
2288 			case TIMELIB_ZONETYPE_ID:
2289 				tzi = tzobj->tzi.tz;
2290 				break;
2291 			case TIMELIB_ZONETYPE_OFFSET:
2292 				new_offset = tzobj->tzi.utc_offset;
2293 				break;
2294 			case TIMELIB_ZONETYPE_ABBR:
2295 				new_offset = tzobj->tzi.z.utc_offset;
2296 				new_dst    = tzobj->tzi.z.dst;
2297 				new_abbr   = timelib_strdup(tzobj->tzi.z.abbr);
2298 				break;
2299 		}
2300 		type = tzobj->type;
2301 	} else if (dateobj->time->tz_info) {
2302 		tzi = dateobj->time->tz_info;
2303 	} else {
2304 		tzi = get_timezone_info();
2305 		if (!tzi) {
2306 			return 0;
2307 		}
2308 	}
2309 
2310 	now = timelib_time_ctor();
2311 	now->zone_type = type;
2312 	switch (type) {
2313 		case TIMELIB_ZONETYPE_ID:
2314 			now->tz_info = tzi;
2315 			break;
2316 		case TIMELIB_ZONETYPE_OFFSET:
2317 			now->z = new_offset;
2318 			break;
2319 		case TIMELIB_ZONETYPE_ABBR:
2320 			now->z = new_offset;
2321 			now->dst = new_dst;
2322 			now->tz_abbr = new_abbr;
2323 			break;
2324 	}
2325 	php_date_get_current_time_with_fraction(&sec, &usec);
2326 	timelib_unixtime2local(now, (timelib_sll) sec);
2327 	php_date_set_time_fraction(now, usec);
2328 
2329 	options = TIMELIB_NO_CLONE;
2330 	if (flags & PHP_DATE_INIT_FORMAT) {
2331 		options |= TIMELIB_OVERRIDE_TIME;
2332 	}
2333 	timelib_fill_holes(dateobj->time, now, options);
2334 
2335 	timelib_update_ts(dateobj->time, tzi);
2336 	timelib_update_from_sse(dateobj->time);
2337 
2338 	dateobj->time->have_relative = 0;
2339 
2340 	timelib_time_dtor(now);
2341 
2342 	return 1;
2343 } /* }}} */
2344 
2345 /* {{{ Returns new DateTime object */
PHP_FUNCTION(date_create)2346 PHP_FUNCTION(date_create)
2347 {
2348 	zval           *timezone_object = NULL;
2349 	char           *time_str = NULL;
2350 	size_t          time_str_len = 0;
2351 
2352 	ZEND_PARSE_PARAMETERS_START(0, 2)
2353 		Z_PARAM_OPTIONAL
2354 		Z_PARAM_STRING(time_str, time_str_len)
2355 		Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2356 	ZEND_PARSE_PARAMETERS_END();
2357 
2358 	php_date_instantiate(date_ce_date, return_value);
2359 	if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, NULL, timezone_object, 0)) {
2360 		zval_ptr_dtor(return_value);
2361 		RETURN_FALSE;
2362 	}
2363 }
2364 /* }}} */
2365 
2366 /* {{{ Returns new DateTimeImmutable object */
PHP_FUNCTION(date_create_immutable)2367 PHP_FUNCTION(date_create_immutable)
2368 {
2369 	zval           *timezone_object = NULL;
2370 	char           *time_str = NULL;
2371 	size_t          time_str_len = 0;
2372 
2373 	ZEND_PARSE_PARAMETERS_START(0, 2)
2374 		Z_PARAM_OPTIONAL
2375 		Z_PARAM_STRING(time_str, time_str_len)
2376 		Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2377 	ZEND_PARSE_PARAMETERS_END();
2378 
2379 	php_date_instantiate(date_ce_immutable, return_value);
2380 	if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, NULL, timezone_object, 0)) {
2381 		zval_ptr_dtor(return_value);
2382 		RETURN_FALSE;
2383 	}
2384 }
2385 /* }}} */
2386 
2387 /* {{{ Returns new DateTime object formatted according to the specified format */
PHP_FUNCTION(date_create_from_format)2388 PHP_FUNCTION(date_create_from_format)
2389 {
2390 	zval           *timezone_object = NULL;
2391 	char           *time_str = NULL, *format_str = NULL;
2392 	size_t          time_str_len = 0, format_str_len = 0;
2393 
2394 	ZEND_PARSE_PARAMETERS_START(2, 3)
2395 		Z_PARAM_STRING(format_str, format_str_len)
2396 		Z_PARAM_PATH(time_str, time_str_len)
2397 		Z_PARAM_OPTIONAL
2398 		Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2399 	ZEND_PARSE_PARAMETERS_END();
2400 
2401 	php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value);
2402 	if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, PHP_DATE_INIT_FORMAT)) {
2403 		zval_ptr_dtor(return_value);
2404 		RETURN_FALSE;
2405 	}
2406 }
2407 /* }}} */
2408 
2409 /* {{{ Returns new DateTime object formatted according to the specified format */
PHP_FUNCTION(date_create_immutable_from_format)2410 PHP_FUNCTION(date_create_immutable_from_format)
2411 {
2412 	zval           *timezone_object = NULL;
2413 	char           *time_str = NULL, *format_str = NULL;
2414 	size_t          time_str_len = 0, format_str_len = 0;
2415 
2416 	ZEND_PARSE_PARAMETERS_START(2, 3)
2417 		Z_PARAM_STRING(format_str, format_str_len)
2418 		Z_PARAM_PATH(time_str, time_str_len)
2419 		Z_PARAM_OPTIONAL
2420 		Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2421 	ZEND_PARSE_PARAMETERS_END();
2422 
2423 	php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value);
2424 	if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, PHP_DATE_INIT_FORMAT)) {
2425 		zval_ptr_dtor(return_value);
2426 		RETURN_FALSE;
2427 	}
2428 }
2429 /* }}} */
2430 
2431 /* {{{ Creates new DateTime object */
PHP_METHOD(DateTime,__construct)2432 PHP_METHOD(DateTime, __construct)
2433 {
2434 	zval *timezone_object = NULL;
2435 	char *time_str = NULL;
2436 	size_t time_str_len = 0;
2437 	zend_error_handling error_handling;
2438 
2439 	ZEND_PARSE_PARAMETERS_START(0, 2)
2440 		Z_PARAM_OPTIONAL
2441 		Z_PARAM_STRING(time_str, time_str_len)
2442 		Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2443 	ZEND_PARSE_PARAMETERS_END();
2444 
2445 	zend_replace_error_handling(EH_THROW, NULL, &error_handling);
2446 	php_date_initialize(Z_PHPDATE_P(ZEND_THIS), time_str, time_str_len, NULL, timezone_object, PHP_DATE_INIT_CTOR);
2447 	zend_restore_error_handling(&error_handling);
2448 }
2449 /* }}} */
2450 
2451 /* {{{ Creates new DateTimeImmutable object */
PHP_METHOD(DateTimeImmutable,__construct)2452 PHP_METHOD(DateTimeImmutable, __construct)
2453 {
2454 	zval *timezone_object = NULL;
2455 	char *time_str = NULL;
2456 	size_t time_str_len = 0;
2457 	zend_error_handling error_handling;
2458 
2459 	ZEND_PARSE_PARAMETERS_START(0, 2)
2460 		Z_PARAM_OPTIONAL
2461 		Z_PARAM_STRING(time_str, time_str_len)
2462 		Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2463 	ZEND_PARSE_PARAMETERS_END();
2464 
2465 	zend_replace_error_handling(EH_THROW, NULL, &error_handling);
2466 	php_date_initialize(Z_PHPDATE_P(ZEND_THIS), time_str, time_str_len, NULL, timezone_object, PHP_DATE_INIT_CTOR);
2467 	zend_restore_error_handling(&error_handling);
2468 }
2469 /* }}} */
2470 
2471 /* {{{ Creates new DateTime object from an existing immutable DateTimeImmutable object. */
PHP_METHOD(DateTime,createFromImmutable)2472 PHP_METHOD(DateTime, createFromImmutable)
2473 {
2474 	zval *datetimeimmutable_object = NULL;
2475 	php_date_obj *new_obj = NULL;
2476 	php_date_obj *old_obj = NULL;
2477 
2478 	ZEND_PARSE_PARAMETERS_START(1, 1)
2479 		Z_PARAM_OBJECT_OF_CLASS(datetimeimmutable_object, date_ce_immutable)
2480 	ZEND_PARSE_PARAMETERS_END();
2481 
2482 	old_obj = Z_PHPDATE_P(datetimeimmutable_object);
2483 	DATE_CHECK_INITIALIZED(old_obj->time, DateTimeImmutable);
2484 
2485 	php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value);
2486 	new_obj = Z_PHPDATE_P(return_value);
2487 
2488 	new_obj->time = timelib_time_clone(old_obj->time);
2489 }
2490 /* }}} */
2491 
2492 /* {{{ Creates new DateTime object from an existing DateTimeInterface object. */
PHP_METHOD(DateTime,createFromInterface)2493 PHP_METHOD(DateTime, createFromInterface)
2494 {
2495 	zval *datetimeinterface_object = NULL;
2496 	php_date_obj *new_obj = NULL;
2497 	php_date_obj *old_obj = NULL;
2498 
2499 	ZEND_PARSE_PARAMETERS_START(1, 1)
2500 		Z_PARAM_OBJECT_OF_CLASS(datetimeinterface_object, date_ce_interface)
2501 	ZEND_PARSE_PARAMETERS_END();
2502 
2503 	old_obj = Z_PHPDATE_P(datetimeinterface_object);
2504 	DATE_CHECK_INITIALIZED(old_obj->time, DateTimeInterface);
2505 
2506 	php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value);
2507 	new_obj = Z_PHPDATE_P(return_value);
2508 
2509 	new_obj->time = timelib_time_clone(old_obj->time);
2510 }
2511 /* }}} */
2512 
2513 /* {{{ Creates new DateTimeImmutable object from an existing mutable DateTime object. */
PHP_METHOD(DateTimeImmutable,createFromMutable)2514 PHP_METHOD(DateTimeImmutable, createFromMutable)
2515 {
2516 	zval *datetime_object = NULL;
2517 	php_date_obj *new_obj = NULL;
2518 	php_date_obj *old_obj = NULL;
2519 
2520 	ZEND_PARSE_PARAMETERS_START(1, 1)
2521 		Z_PARAM_OBJECT_OF_CLASS(datetime_object, date_ce_date)
2522 	ZEND_PARSE_PARAMETERS_END();
2523 
2524 	old_obj = Z_PHPDATE_P(datetime_object);
2525 	DATE_CHECK_INITIALIZED(old_obj->time, DateTime);
2526 
2527 	php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value);
2528 	new_obj = Z_PHPDATE_P(return_value);
2529 
2530 	new_obj->time = timelib_time_clone(old_obj->time);
2531 }
2532 /* }}} */
2533 
2534 /* {{{ Creates new DateTimeImmutable object from an existing DateTimeInterface object. */
PHP_METHOD(DateTimeImmutable,createFromInterface)2535 PHP_METHOD(DateTimeImmutable, createFromInterface)
2536 {
2537 	zval *datetimeinterface_object = NULL;
2538 	php_date_obj *new_obj = NULL;
2539 	php_date_obj *old_obj = NULL;
2540 
2541 	ZEND_PARSE_PARAMETERS_START(1, 1)
2542 		Z_PARAM_OBJECT_OF_CLASS(datetimeinterface_object, date_ce_interface)
2543 	ZEND_PARSE_PARAMETERS_END();
2544 
2545 	old_obj = Z_PHPDATE_P(datetimeinterface_object);
2546 	DATE_CHECK_INITIALIZED(old_obj->time, DateTimeInterface);
2547 
2548 	php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value);
2549 	new_obj = Z_PHPDATE_P(return_value);
2550 
2551 	new_obj->time = timelib_time_clone(old_obj->time);
2552 }
2553 /* }}} */
2554 
php_date_initialize_from_hash(php_date_obj ** dateobj,HashTable * myht)2555 static int php_date_initialize_from_hash(php_date_obj **dateobj, HashTable *myht)
2556 {
2557 	zval             *z_date;
2558 	zval              tmp_obj;
2559 	timelib_tzinfo   *tzi;
2560 
2561 	z_date = zend_hash_str_find(myht, "date", sizeof("date")-1);
2562 	if (z_date && Z_TYPE_P(z_date) == IS_STRING) {
2563 		zval *z_timezone_type = zend_hash_str_find(myht, "timezone_type", sizeof("timezone_type")-1);
2564 		if (z_timezone_type && Z_TYPE_P(z_timezone_type) == IS_LONG) {
2565 			zval *z_timezone = zend_hash_str_find(myht, "timezone", sizeof("timezone")-1);
2566 			if (z_timezone && Z_TYPE_P(z_timezone) == IS_STRING) {
2567 				switch (Z_LVAL_P(z_timezone_type)) {
2568 					case TIMELIB_ZONETYPE_OFFSET:
2569 					case TIMELIB_ZONETYPE_ABBR: {
2570 						char *tmp = emalloc(Z_STRLEN_P(z_date) + Z_STRLEN_P(z_timezone) + 2);
2571 						int ret;
2572 						snprintf(tmp, Z_STRLEN_P(z_date) + Z_STRLEN_P(z_timezone) + 2, "%s %s", Z_STRVAL_P(z_date), Z_STRVAL_P(z_timezone));
2573 						ret = php_date_initialize(*dateobj, tmp, Z_STRLEN_P(z_date) + Z_STRLEN_P(z_timezone) + 1, NULL, NULL, 0);
2574 						efree(tmp);
2575 						return 1 == ret;
2576 					}
2577 
2578 					case TIMELIB_ZONETYPE_ID: {
2579 						int ret;
2580 						php_timezone_obj *tzobj;
2581 
2582 						tzi = php_date_parse_tzfile(Z_STRVAL_P(z_timezone), DATE_TIMEZONEDB);
2583 
2584 						if (tzi == NULL) {
2585 							return 0;
2586 						}
2587 
2588 						tzobj = Z_PHPTIMEZONE_P(php_date_instantiate(date_ce_timezone, &tmp_obj));
2589 						tzobj->type = TIMELIB_ZONETYPE_ID;
2590 						tzobj->tzi.tz = tzi;
2591 						tzobj->initialized = 1;
2592 
2593 						ret = php_date_initialize(*dateobj, Z_STRVAL_P(z_date), Z_STRLEN_P(z_date), NULL, &tmp_obj, 0);
2594 						zval_ptr_dtor(&tmp_obj);
2595 						return 1 == ret;
2596 					}
2597 				}
2598 			}
2599 		}
2600 	}
2601 	return 0;
2602 } /* }}} */
2603 
2604 /* {{{ */
PHP_METHOD(DateTime,__set_state)2605 PHP_METHOD(DateTime, __set_state)
2606 {
2607 	php_date_obj     *dateobj;
2608 	zval             *array;
2609 	HashTable        *myht;
2610 
2611 	ZEND_PARSE_PARAMETERS_START(1, 1)
2612 		Z_PARAM_ARRAY(array)
2613 	ZEND_PARSE_PARAMETERS_END();
2614 
2615 	myht = Z_ARRVAL_P(array);
2616 
2617 	php_date_instantiate(date_ce_date, return_value);
2618 	dateobj = Z_PHPDATE_P(return_value);
2619 	if (!php_date_initialize_from_hash(&dateobj, myht)) {
2620 		zend_throw_error(NULL, "Invalid serialization data for DateTime object");
2621 	}
2622 }
2623 /* }}} */
2624 
2625 /* {{{ */
PHP_METHOD(DateTimeImmutable,__set_state)2626 PHP_METHOD(DateTimeImmutable, __set_state)
2627 {
2628 	php_date_obj     *dateobj;
2629 	zval             *array;
2630 	HashTable        *myht;
2631 
2632 	ZEND_PARSE_PARAMETERS_START(1, 1)
2633 		Z_PARAM_ARRAY(array)
2634 	ZEND_PARSE_PARAMETERS_END();
2635 
2636 	myht = Z_ARRVAL_P(array);
2637 
2638 	php_date_instantiate(date_ce_immutable, return_value);
2639 	dateobj = Z_PHPDATE_P(return_value);
2640 	if (!php_date_initialize_from_hash(&dateobj, myht)) {
2641 		zend_throw_error(NULL, "Invalid serialization data for DateTimeImmutable object");
2642 	}
2643 }
2644 /* }}} */
2645 
2646 /* {{{ */
PHP_METHOD(DateTime,__wakeup)2647 PHP_METHOD(DateTime, __wakeup)
2648 {
2649 	zval             *object = ZEND_THIS;
2650 	php_date_obj     *dateobj;
2651 	HashTable        *myht;
2652 
2653 	ZEND_PARSE_PARAMETERS_NONE();
2654 
2655 	dateobj = Z_PHPDATE_P(object);
2656 
2657 	myht = Z_OBJPROP_P(object);
2658 
2659 	if (!php_date_initialize_from_hash(&dateobj, myht)) {
2660 		zend_throw_error(NULL, "Invalid serialization data for DateTime object");
2661 	}
2662 }
2663 /* }}} */
2664 
2665 /* {{{ */
PHP_METHOD(DateTimeImmutable,__wakeup)2666 PHP_METHOD(DateTimeImmutable, __wakeup)
2667 {
2668 	zval             *object = ZEND_THIS;
2669 	php_date_obj     *dateobj;
2670 	HashTable        *myht;
2671 
2672 	ZEND_PARSE_PARAMETERS_NONE();
2673 
2674 	dateobj = Z_PHPDATE_P(object);
2675 
2676 	myht = Z_OBJPROP_P(object);
2677 
2678 	if (!php_date_initialize_from_hash(&dateobj, myht)) {
2679 		zend_throw_error(NULL, "Invalid serialization data for DateTimeImmutable object");
2680 	}
2681 }
2682 /* }}} */
2683 
2684 /* 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)2685 static void zval_from_error_container(zval *z, timelib_error_container *error) /* {{{ */
2686 {
2687 	int   i;
2688 	zval element;
2689 
2690 	add_assoc_long(z, "warning_count", error->warning_count);
2691 	array_init(&element);
2692 	for (i = 0; i < error->warning_count; i++) {
2693 		add_index_string(&element, error->warning_messages[i].position, error->warning_messages[i].message);
2694 	}
2695 	add_assoc_zval(z, "warnings", &element);
2696 
2697 	add_assoc_long(z, "error_count", error->error_count);
2698 	array_init(&element);
2699 	for (i = 0; i < error->error_count; i++) {
2700 		add_index_string(&element, error->error_messages[i].position, error->error_messages[i].message);
2701 	}
2702 	add_assoc_zval(z, "errors", &element);
2703 } /* }}} */
2704 
2705 /* {{{ Returns the warnings and errors found while parsing a date/time string. */
PHP_FUNCTION(date_get_last_errors)2706 PHP_FUNCTION(date_get_last_errors)
2707 {
2708 	ZEND_PARSE_PARAMETERS_NONE();
2709 
2710 	if (DATEG(last_errors)) {
2711 		array_init(return_value);
2712 		zval_from_error_container(return_value, DATEG(last_errors));
2713 	} else {
2714 		RETURN_FALSE;
2715 	}
2716 }
2717 /* }}} */
2718 
php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAMETERS,timelib_time * parsed_time,timelib_error_container * error)2719 void php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAMETERS, timelib_time *parsed_time, timelib_error_container *error) /* {{{ */
2720 {
2721 	zval element;
2722 
2723 	array_init(return_value);
2724 #define PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(name, elem) \
2725 	if (parsed_time->elem == -99999) {               \
2726 		add_assoc_bool(return_value, #name, 0); \
2727 	} else {                                       \
2728 		add_assoc_long(return_value, #name, parsed_time->elem); \
2729 	}
2730 	PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(year,      y);
2731 	PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(month,     m);
2732 	PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(day,       d);
2733 	PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(hour,      h);
2734 	PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(minute,    i);
2735 	PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(second,    s);
2736 
2737 	if (parsed_time->us == -99999) {
2738 		add_assoc_bool(return_value, "fraction", 0);
2739 	} else {
2740 		add_assoc_double(return_value, "fraction", (double)parsed_time->us / 1000000.0);
2741 	}
2742 
2743 	zval_from_error_container(return_value, error);
2744 
2745 	timelib_error_container_dtor(error);
2746 
2747 	add_assoc_bool(return_value, "is_localtime", parsed_time->is_localtime);
2748 
2749 	if (parsed_time->is_localtime) {
2750 		PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone_type, zone_type);
2751 		switch (parsed_time->zone_type) {
2752 			case TIMELIB_ZONETYPE_OFFSET:
2753 				PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone, z);
2754 				add_assoc_bool(return_value, "is_dst", parsed_time->dst);
2755 				break;
2756 			case TIMELIB_ZONETYPE_ID:
2757 				if (parsed_time->tz_abbr) {
2758 					add_assoc_string(return_value, "tz_abbr", parsed_time->tz_abbr);
2759 				}
2760 				if (parsed_time->tz_info) {
2761 					add_assoc_string(return_value, "tz_id", parsed_time->tz_info->name);
2762 				}
2763 				break;
2764 			case TIMELIB_ZONETYPE_ABBR:
2765 				PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone, z);
2766 				add_assoc_bool(return_value, "is_dst", parsed_time->dst);
2767 				add_assoc_string(return_value, "tz_abbr", parsed_time->tz_abbr);
2768 				break;
2769 		}
2770 	}
2771 	if (parsed_time->have_relative) {
2772 		array_init(&element);
2773 		add_assoc_long(&element, "year",   parsed_time->relative.y);
2774 		add_assoc_long(&element, "month",  parsed_time->relative.m);
2775 		add_assoc_long(&element, "day",    parsed_time->relative.d);
2776 		add_assoc_long(&element, "hour",   parsed_time->relative.h);
2777 		add_assoc_long(&element, "minute", parsed_time->relative.i);
2778 		add_assoc_long(&element, "second", parsed_time->relative.s);
2779 		if (parsed_time->relative.have_weekday_relative) {
2780 			add_assoc_long(&element, "weekday", parsed_time->relative.weekday);
2781 		}
2782 		if (parsed_time->relative.have_special_relative && (parsed_time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY)) {
2783 			add_assoc_long(&element, "weekdays", parsed_time->relative.special.amount);
2784 		}
2785 		if (parsed_time->relative.first_last_day_of) {
2786 			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);
2787 		}
2788 		add_assoc_zval(return_value, "relative", &element);
2789 	}
2790 	timelib_time_dtor(parsed_time);
2791 } /* }}} */
2792 
2793 /* {{{ Returns associative array with detailed info about given date */
PHP_FUNCTION(date_parse)2794 PHP_FUNCTION(date_parse)
2795 {
2796 	zend_string                    *date;
2797 	timelib_error_container *error;
2798 	timelib_time                   *parsed_time;
2799 
2800 	ZEND_PARSE_PARAMETERS_START(1, 1)
2801 		Z_PARAM_STR(date)
2802 	ZEND_PARSE_PARAMETERS_END();
2803 
2804 	parsed_time = timelib_strtotime(ZSTR_VAL(date), ZSTR_LEN(date), &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2805 	php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAM_PASSTHRU, parsed_time, error);
2806 }
2807 /* }}} */
2808 
2809 /* {{{ Returns associative array with detailed info about given date */
PHP_FUNCTION(date_parse_from_format)2810 PHP_FUNCTION(date_parse_from_format)
2811 {
2812 	zend_string                    *date, *format;
2813 	timelib_error_container *error;
2814 	timelib_time                   *parsed_time;
2815 
2816 	ZEND_PARSE_PARAMETERS_START(2, 2)
2817 		Z_PARAM_STR(format)
2818 		Z_PARAM_PATH_STR(date)
2819 	ZEND_PARSE_PARAMETERS_END();
2820 
2821 	parsed_time = timelib_parse_from_format(ZSTR_VAL(format), ZSTR_VAL(date), ZSTR_LEN(date), &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2822 	php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAM_PASSTHRU, parsed_time, error);
2823 }
2824 /* }}} */
2825 
2826 /* {{{ Returns date formatted according to given format */
PHP_FUNCTION(date_format)2827 PHP_FUNCTION(date_format)
2828 {
2829 	zval         *object;
2830 	php_date_obj *dateobj;
2831 	char         *format;
2832 	size_t       format_len;
2833 
2834 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, date_ce_interface, &format, &format_len) == FAILURE) {
2835 		RETURN_THROWS();
2836 	}
2837 	dateobj = Z_PHPDATE_P(object);
2838 	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
2839 	RETURN_STR(date_format(format, format_len, dateobj->time, dateobj->time->is_localtime));
2840 }
2841 /* }}} */
2842 
php_date_modify(zval * object,char * modify,size_t modify_len)2843 static int php_date_modify(zval *object, char *modify, size_t modify_len) /* {{{ */
2844 {
2845 	php_date_obj *dateobj;
2846 	timelib_time *tmp_time;
2847 	timelib_error_container *err = NULL;
2848 
2849 	dateobj = Z_PHPDATE_P(object);
2850 
2851 	if (!(dateobj->time)) {
2852 		zend_throw_error(NULL, "The DateTime object has not been correctly initialized by its constructor");
2853 		return 0;
2854 	}
2855 
2856 	tmp_time = timelib_strtotime(modify, modify_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2857 
2858 	/* update last errors and warnings */
2859 	update_errors_warnings(err);
2860 	if (err && err->error_count) {
2861 		/* spit out the first library error message, at least */
2862 		php_error_docref(NULL, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", modify,
2863 			err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message);
2864 		timelib_time_dtor(tmp_time);
2865 		return 0;
2866 	}
2867 
2868 	memcpy(&dateobj->time->relative, &tmp_time->relative, sizeof(timelib_rel_time));
2869 	dateobj->time->have_relative = tmp_time->have_relative;
2870 	dateobj->time->sse_uptodate = 0;
2871 
2872 	if (tmp_time->y != -99999) {
2873 		dateobj->time->y = tmp_time->y;
2874 	}
2875 	if (tmp_time->m != -99999) {
2876 		dateobj->time->m = tmp_time->m;
2877 	}
2878 	if (tmp_time->d != -99999) {
2879 		dateobj->time->d = tmp_time->d;
2880 	}
2881 
2882 	if (tmp_time->h != -99999) {
2883 		dateobj->time->h = tmp_time->h;
2884 		if (tmp_time->i != -99999) {
2885 			dateobj->time->i = tmp_time->i;
2886 			if (tmp_time->s != -99999) {
2887 				dateobj->time->s = tmp_time->s;
2888 			} else {
2889 				dateobj->time->s = 0;
2890 			}
2891 		} else {
2892 			dateobj->time->i = 0;
2893 			dateobj->time->s = 0;
2894 		}
2895 	}
2896 
2897 	if (tmp_time->us != -99999) {
2898 		dateobj->time->us = tmp_time->us;
2899 	}
2900 
2901 	timelib_time_dtor(tmp_time);
2902 
2903 	timelib_update_ts(dateobj->time, NULL);
2904 	timelib_update_from_sse(dateobj->time);
2905 	dateobj->time->have_relative = 0;
2906 	memset(&dateobj->time->relative, 0, sizeof(dateobj->time->relative));
2907 
2908 	return 1;
2909 } /* }}} */
2910 
2911 /* {{{ Alters the timestamp. */
PHP_FUNCTION(date_modify)2912 PHP_FUNCTION(date_modify)
2913 {
2914 	zval         *object;
2915 	char         *modify;
2916 	size_t        modify_len;
2917 
2918 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, date_ce_date, &modify, &modify_len) == FAILURE) {
2919 		RETURN_THROWS();
2920 	}
2921 
2922 	if (!php_date_modify(object, modify, modify_len)) {
2923 		RETURN_FALSE;
2924 	}
2925 
2926 	RETURN_OBJ_COPY(Z_OBJ_P(object));
2927 }
2928 /* }}} */
2929 
2930 /* {{{ */
PHP_METHOD(DateTimeImmutable,modify)2931 PHP_METHOD(DateTimeImmutable, modify)
2932 {
2933 	zval *object, new_object;
2934 	char *modify;
2935 	size_t   modify_len;
2936 
2937 	object = ZEND_THIS;
2938 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &modify, &modify_len) == FAILURE) {
2939 		RETURN_THROWS();
2940 	}
2941 
2942 	date_clone_immutable(object, &new_object);
2943 	if (!php_date_modify(&new_object, modify, modify_len)) {
2944 		zval_ptr_dtor(&new_object);
2945 		RETURN_FALSE;
2946 	}
2947 
2948 	RETURN_OBJ(Z_OBJ(new_object));
2949 }
2950 /* }}} */
2951 
php_date_add(zval * object,zval * interval,zval * return_value)2952 static void php_date_add(zval *object, zval *interval, zval *return_value) /* {{{ */
2953 {
2954 	php_date_obj     *dateobj;
2955 	php_interval_obj *intobj;
2956 	timelib_time     *new_time;
2957 
2958 	dateobj = Z_PHPDATE_P(object);
2959 	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
2960 	intobj = Z_PHPINTERVAL_P(interval);
2961 	DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
2962 
2963 	new_time = timelib_add(dateobj->time, intobj->diff);
2964 	timelib_time_dtor(dateobj->time);
2965 	dateobj->time = new_time;
2966 } /* }}} */
2967 
2968 /* {{{ Adds an interval to the current date in object. */
PHP_FUNCTION(date_add)2969 PHP_FUNCTION(date_add)
2970 {
2971 	zval *object, *interval;
2972 
2973 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
2974 		RETURN_THROWS();
2975 	}
2976 
2977 	php_date_add(object, interval, return_value);
2978 
2979 	RETURN_OBJ_COPY(Z_OBJ_P(object));
2980 }
2981 /* }}} */
2982 
2983 /* {{{ */
PHP_METHOD(DateTimeImmutable,add)2984 PHP_METHOD(DateTimeImmutable, add)
2985 {
2986 	zval *object, *interval, new_object;
2987 
2988 	object = ZEND_THIS;
2989 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &interval, date_ce_interval) == FAILURE) {
2990 		RETURN_THROWS();
2991 	}
2992 
2993 	date_clone_immutable(object, &new_object);
2994 	php_date_add(&new_object, interval, return_value);
2995 
2996 	RETURN_OBJ(Z_OBJ(new_object));
2997 }
2998 /* }}} */
2999 
php_date_sub(zval * object,zval * interval,zval * return_value)3000 static void php_date_sub(zval *object, zval *interval, zval *return_value) /* {{{ */
3001 {
3002 	php_date_obj     *dateobj;
3003 	php_interval_obj *intobj;
3004 	timelib_time     *new_time;
3005 
3006 	dateobj = Z_PHPDATE_P(object);
3007 	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3008 	intobj = Z_PHPINTERVAL_P(interval);
3009 	DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
3010 
3011 	if (intobj->diff->have_special_relative) {
3012 		php_error_docref(NULL, E_WARNING, "Only non-special relative time specifications are supported for subtraction");
3013 		return;
3014 	}
3015 
3016 	new_time = timelib_sub(dateobj->time, intobj->diff);
3017 	timelib_time_dtor(dateobj->time);
3018 	dateobj->time = new_time;
3019 } /* }}} */
3020 
3021 /* {{{ Subtracts an interval to the current date in object. */
PHP_FUNCTION(date_sub)3022 PHP_FUNCTION(date_sub)
3023 {
3024 	zval *object, *interval;
3025 
3026 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
3027 		RETURN_THROWS();
3028 	}
3029 
3030 	php_date_sub(object, interval, return_value);
3031 
3032 	RETURN_OBJ_COPY(Z_OBJ_P(object));
3033 }
3034 /* }}} */
3035 
3036 /* {{{ */
PHP_METHOD(DateTimeImmutable,sub)3037 PHP_METHOD(DateTimeImmutable, sub)
3038 {
3039 	zval *object, *interval, new_object;
3040 
3041 	object = ZEND_THIS;
3042 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &interval, date_ce_interval) == FAILURE) {
3043 		RETURN_THROWS();
3044 	}
3045 
3046 	date_clone_immutable(object, &new_object);
3047 	php_date_sub(&new_object, interval, return_value);
3048 
3049 	RETURN_OBJ(Z_OBJ(new_object));
3050 }
3051 /* }}} */
3052 
set_timezone_from_timelib_time(php_timezone_obj * tzobj,timelib_time * t)3053 static void set_timezone_from_timelib_time(php_timezone_obj *tzobj, timelib_time *t)
3054 {
3055        tzobj->initialized = 1;
3056        tzobj->type = t->zone_type;
3057        switch (t->zone_type) {
3058                case TIMELIB_ZONETYPE_ID:
3059                        tzobj->tzi.tz = t->tz_info;
3060                        break;
3061                case TIMELIB_ZONETYPE_OFFSET:
3062                        tzobj->tzi.utc_offset = t->z;
3063                        break;
3064                case TIMELIB_ZONETYPE_ABBR:
3065                        tzobj->tzi.z.utc_offset = t->z;
3066                        tzobj->tzi.z.dst = t->dst;
3067                        tzobj->tzi.z.abbr = timelib_strdup(t->tz_abbr);
3068                        break;
3069        }
3070 }
3071 
3072 
3073 /* {{{ Return new DateTimeZone object relative to give DateTime */
PHP_FUNCTION(date_timezone_get)3074 PHP_FUNCTION(date_timezone_get)
3075 {
3076 	zval             *object;
3077 	php_date_obj     *dateobj;
3078 
3079 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_interface) == FAILURE) {
3080 		RETURN_THROWS();
3081 	}
3082 	dateobj = Z_PHPDATE_P(object);
3083 	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3084 	if (dateobj->time->is_localtime) {
3085 		php_timezone_obj *tzobj;
3086 		php_date_instantiate(date_ce_timezone, return_value);
3087 		tzobj = Z_PHPTIMEZONE_P(return_value);
3088 		set_timezone_from_timelib_time(tzobj, dateobj->time);
3089 	} else {
3090 		RETURN_FALSE;
3091 	}
3092 }
3093 /* }}} */
3094 
php_date_timezone_set(zval * object,zval * timezone_object,zval * return_value)3095 static void php_date_timezone_set(zval *object, zval *timezone_object, zval *return_value) /* {{{ */
3096 {
3097 	php_date_obj     *dateobj;
3098 	php_timezone_obj *tzobj;
3099 
3100 	dateobj = Z_PHPDATE_P(object);
3101 	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3102 	tzobj = Z_PHPTIMEZONE_P(timezone_object);
3103 
3104 	switch (tzobj->type) {
3105 		case TIMELIB_ZONETYPE_OFFSET:
3106 			timelib_set_timezone_from_offset(dateobj->time, tzobj->tzi.utc_offset);
3107 			break;
3108 		case TIMELIB_ZONETYPE_ABBR:
3109 			timelib_set_timezone_from_abbr(dateobj->time, tzobj->tzi.z);
3110 			break;
3111 		case TIMELIB_ZONETYPE_ID:
3112 			timelib_set_timezone(dateobj->time, tzobj->tzi.tz);
3113 			break;
3114 	}
3115 	timelib_unixtime2local(dateobj->time, dateobj->time->sse);
3116 } /* }}} */
3117 
3118 /* {{{ Sets the timezone for the DateTime object. */
PHP_FUNCTION(date_timezone_set)3119 PHP_FUNCTION(date_timezone_set)
3120 {
3121 	zval *object;
3122 	zval *timezone_object;
3123 
3124 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_date, &timezone_object, date_ce_timezone) == FAILURE) {
3125 		RETURN_THROWS();
3126 	}
3127 
3128 	php_date_timezone_set(object, timezone_object, return_value);
3129 
3130 	RETURN_OBJ_COPY(Z_OBJ_P(object));
3131 }
3132 /* }}} */
3133 
3134 /* {{{ */
PHP_METHOD(DateTimeImmutable,setTimezone)3135 PHP_METHOD(DateTimeImmutable, setTimezone)
3136 {
3137 	zval *object, new_object;
3138 	zval *timezone_object;
3139 
3140 	object = ZEND_THIS;
3141 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &timezone_object, date_ce_timezone) == FAILURE) {
3142 		RETURN_THROWS();
3143 	}
3144 
3145 	date_clone_immutable(object, &new_object);
3146 	php_date_timezone_set(&new_object, timezone_object, return_value);
3147 
3148 	RETURN_OBJ(Z_OBJ(new_object));
3149 }
3150 /* }}} */
3151 
3152 /* {{{ Returns the DST offset. */
PHP_FUNCTION(date_offset_get)3153 PHP_FUNCTION(date_offset_get)
3154 {
3155 	zval                *object;
3156 	php_date_obj        *dateobj;
3157 	timelib_time_offset *offset;
3158 
3159 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_interface) == FAILURE) {
3160 		RETURN_THROWS();
3161 	}
3162 	dateobj = Z_PHPDATE_P(object);
3163 	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3164 	if (dateobj->time->is_localtime) {
3165 		switch (dateobj->time->zone_type) {
3166 			case TIMELIB_ZONETYPE_ID:
3167 				offset = timelib_get_time_zone_info(dateobj->time->sse, dateobj->time->tz_info);
3168 				RETVAL_LONG(offset->offset);
3169 				timelib_time_offset_dtor(offset);
3170 				break;
3171 			case TIMELIB_ZONETYPE_OFFSET:
3172 				RETVAL_LONG(dateobj->time->z);
3173 				break;
3174 			case TIMELIB_ZONETYPE_ABBR:
3175 				RETVAL_LONG((dateobj->time->z + (3600 * dateobj->time->dst)));
3176 				break;
3177 		}
3178 		return;
3179 	} else {
3180 		RETURN_LONG(0);
3181 	}
3182 }
3183 /* }}} */
3184 
php_date_time_set(zval * object,zend_long h,zend_long i,zend_long s,zend_long ms,zval * return_value)3185 static void php_date_time_set(zval *object, zend_long h, zend_long i, zend_long s, zend_long ms, zval *return_value) /* {{{ */
3186 {
3187 	php_date_obj *dateobj;
3188 
3189 	dateobj = Z_PHPDATE_P(object);
3190 	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3191 	dateobj->time->h = h;
3192 	dateobj->time->i = i;
3193 	dateobj->time->s = s;
3194 	dateobj->time->us = ms;
3195 	timelib_update_ts(dateobj->time, NULL);
3196 	timelib_update_from_sse(dateobj->time);
3197 } /* }}} */
3198 
3199 /* {{{ Sets the time. */
PHP_FUNCTION(date_time_set)3200 PHP_FUNCTION(date_time_set)
3201 {
3202 	zval *object;
3203 	zend_long  h, i, s = 0, ms = 0;
3204 
3205 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|ll", &object, date_ce_date, &h, &i, &s, &ms) == FAILURE) {
3206 		RETURN_THROWS();
3207 	}
3208 
3209 	php_date_time_set(object, h, i, s, ms, return_value);
3210 
3211 	RETURN_OBJ_COPY(Z_OBJ_P(object));
3212 }
3213 /* }}} */
3214 
3215 /* {{{ */
PHP_METHOD(DateTimeImmutable,setTime)3216 PHP_METHOD(DateTimeImmutable, setTime)
3217 {
3218 	zval *object, new_object;
3219 	zend_long  h, i, s = 0, ms = 0;
3220 
3221 	object = ZEND_THIS;
3222 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|ll", &h, &i, &s, &ms) == FAILURE) {
3223 		RETURN_THROWS();
3224 	}
3225 
3226 	date_clone_immutable(object, &new_object);
3227 	php_date_time_set(&new_object, h, i, s, ms, return_value);
3228 
3229 	RETURN_OBJ(Z_OBJ(new_object));
3230 }
3231 /* }}} */
3232 
php_date_date_set(zval * object,zend_long y,zend_long m,zend_long d,zval * return_value)3233 static void php_date_date_set(zval *object, zend_long y, zend_long m, zend_long d, zval *return_value) /* {{{ */
3234 {
3235 	php_date_obj *dateobj;
3236 
3237 	dateobj = Z_PHPDATE_P(object);
3238 	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3239 	dateobj->time->y = y;
3240 	dateobj->time->m = m;
3241 	dateobj->time->d = d;
3242 	timelib_update_ts(dateobj->time, NULL);
3243 } /* }}} */
3244 
3245 /* {{{ Sets the date. */
PHP_FUNCTION(date_date_set)3246 PHP_FUNCTION(date_date_set)
3247 {
3248 	zval *object;
3249 	zend_long  y, m, d;
3250 
3251 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Olll", &object, date_ce_date, &y, &m, &d) == FAILURE) {
3252 		RETURN_THROWS();
3253 	}
3254 
3255 	php_date_date_set(object, y, m, d, return_value);
3256 
3257 	RETURN_OBJ_COPY(Z_OBJ_P(object));
3258 }
3259 /* }}} */
3260 
3261 /* {{{ */
PHP_METHOD(DateTimeImmutable,setDate)3262 PHP_METHOD(DateTimeImmutable, setDate)
3263 {
3264 	zval *object, new_object;
3265 	zend_long  y, m, d;
3266 
3267 	object = ZEND_THIS;
3268 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &y, &m, &d) == FAILURE) {
3269 		RETURN_THROWS();
3270 	}
3271 
3272 	date_clone_immutable(object, &new_object);
3273 	php_date_date_set(&new_object, y, m, d, return_value);
3274 
3275 	RETURN_OBJ(Z_OBJ(new_object));
3276 }
3277 /* }}} */
3278 
php_date_isodate_set(zval * object,zend_long y,zend_long w,zend_long d,zval * return_value)3279 static void php_date_isodate_set(zval *object, zend_long y, zend_long w, zend_long d, zval *return_value) /* {{{ */
3280 {
3281 	php_date_obj *dateobj;
3282 
3283 	dateobj = Z_PHPDATE_P(object);
3284 	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3285 	dateobj->time->y = y;
3286 	dateobj->time->m = 1;
3287 	dateobj->time->d = 1;
3288 	memset(&dateobj->time->relative, 0, sizeof(dateobj->time->relative));
3289 	dateobj->time->relative.d = timelib_daynr_from_weeknr(y, w, d);
3290 	dateobj->time->have_relative = 1;
3291 
3292 	timelib_update_ts(dateobj->time, NULL);
3293 } /* }}} */
3294 
3295 /* {{{ Sets the ISO date. */
PHP_FUNCTION(date_isodate_set)3296 PHP_FUNCTION(date_isodate_set)
3297 {
3298 	zval *object;
3299 	zend_long  y, w, d = 1;
3300 
3301 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|l", &object, date_ce_date, &y, &w, &d) == FAILURE) {
3302 		RETURN_THROWS();
3303 	}
3304 
3305 	php_date_isodate_set(object, y, w, d, return_value);
3306 
3307 	RETURN_OBJ_COPY(Z_OBJ_P(object));
3308 }
3309 /* }}} */
3310 
3311 /* {{{ */
PHP_METHOD(DateTimeImmutable,setISODate)3312 PHP_METHOD(DateTimeImmutable, setISODate)
3313 {
3314 	zval *object, new_object;
3315 	zend_long  y, w, d = 1;
3316 
3317 	object = ZEND_THIS;
3318 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|l", &y, &w, &d) == FAILURE) {
3319 		RETURN_THROWS();
3320 	}
3321 
3322 	date_clone_immutable(object, &new_object);
3323 	php_date_isodate_set(&new_object, y, w, d, return_value);
3324 
3325 	RETURN_OBJ(Z_OBJ(new_object));
3326 }
3327 /* }}} */
3328 
php_date_timestamp_set(zval * object,zend_long timestamp,zval * return_value)3329 static void php_date_timestamp_set(zval *object, zend_long timestamp, zval *return_value) /* {{{ */
3330 {
3331 	php_date_obj *dateobj;
3332 
3333 	dateobj = Z_PHPDATE_P(object);
3334 	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3335 	timelib_unixtime2local(dateobj->time, (timelib_sll)timestamp);
3336 	timelib_update_ts(dateobj->time, NULL);
3337 	php_date_set_time_fraction(dateobj->time, 0);
3338 } /* }}} */
3339 
3340 /* {{{ Sets the date and time based on an Unix timestamp. */
PHP_FUNCTION(date_timestamp_set)3341 PHP_FUNCTION(date_timestamp_set)
3342 {
3343 	zval *object;
3344 	zend_long  timestamp;
3345 
3346 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol", &object, date_ce_date, &timestamp) == FAILURE) {
3347 		RETURN_THROWS();
3348 	}
3349 
3350 	php_date_timestamp_set(object, timestamp, return_value);
3351 
3352 	RETURN_OBJ_COPY(Z_OBJ_P(object));
3353 }
3354 /* }}} */
3355 
3356 /* {{{ */
PHP_METHOD(DateTimeImmutable,setTimestamp)3357 PHP_METHOD(DateTimeImmutable, setTimestamp)
3358 {
3359 	zval *object, new_object;
3360 	zend_long  timestamp;
3361 
3362 	object = ZEND_THIS;
3363 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &timestamp) == FAILURE) {
3364 		RETURN_THROWS();
3365 	}
3366 
3367 	date_clone_immutable(object, &new_object);
3368 	php_date_timestamp_set(&new_object, timestamp, return_value);
3369 
3370 	RETURN_OBJ(Z_OBJ(new_object));
3371 }
3372 /* }}} */
3373 
3374 /* {{{ Gets the Unix timestamp. */
PHP_FUNCTION(date_timestamp_get)3375 PHP_FUNCTION(date_timestamp_get)
3376 {
3377 	zval         *object;
3378 	php_date_obj *dateobj;
3379 	zend_long          timestamp;
3380 	int           epoch_does_not_fit_in_zend_long;
3381 
3382 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_interface) == FAILURE) {
3383 		RETURN_THROWS();
3384 	}
3385 	dateobj = Z_PHPDATE_P(object);
3386 	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3387 	timelib_update_ts(dateobj->time, NULL);
3388 
3389 	timestamp = timelib_date_to_int(dateobj->time, &epoch_does_not_fit_in_zend_long);
3390 
3391 	if (epoch_does_not_fit_in_zend_long) {
3392 		zend_value_error("Epoch doesn't fit in a PHP integer");
3393 		RETURN_THROWS();
3394 	}
3395 
3396 	RETURN_LONG(timestamp);
3397 }
3398 /* }}} */
3399 
3400 /* {{{ Returns the difference between two DateTime objects. */
PHP_FUNCTION(date_diff)3401 PHP_FUNCTION(date_diff)
3402 {
3403 	zval         *object1, *object2;
3404 	php_date_obj *dateobj1, *dateobj2;
3405 	php_interval_obj *interval;
3406 	zend_bool      absolute = 0;
3407 
3408 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO|b", &object1, date_ce_interface, &object2, date_ce_interface, &absolute) == FAILURE) {
3409 		RETURN_THROWS();
3410 	}
3411 	dateobj1 = Z_PHPDATE_P(object1);
3412 	dateobj2 = Z_PHPDATE_P(object2);
3413 	DATE_CHECK_INITIALIZED(dateobj1->time, DateTimeInterface);
3414 	DATE_CHECK_INITIALIZED(dateobj2->time, DateTimeInterface);
3415 	timelib_update_ts(dateobj1->time, NULL);
3416 	timelib_update_ts(dateobj2->time, NULL);
3417 
3418 	php_date_instantiate(date_ce_interval, return_value);
3419 	interval = Z_PHPINTERVAL_P(return_value);
3420 	interval->diff = timelib_diff(dateobj1->time, dateobj2->time);
3421 	if (absolute) {
3422 		interval->diff->invert = 0;
3423 	}
3424 	interval->initialized = 1;
3425 }
3426 /* }}} */
3427 
timezone_initialize(php_timezone_obj * tzobj,const char * tz,size_t tz_len)3428 static int timezone_initialize(php_timezone_obj *tzobj, const char *tz, size_t tz_len) /* {{{ */
3429 {
3430 	timelib_time *dummy_t = ecalloc(1, sizeof(timelib_time));
3431 	int           dst, not_found;
3432 	const char   *orig_tz = tz;
3433 
3434 	if (strlen(tz) != tz_len) {
3435 		php_error_docref(NULL, E_WARNING, "Timezone must not contain null bytes");
3436 		efree(dummy_t);
3437 		return FAILURE;
3438 	}
3439 
3440 	dummy_t->z = timelib_parse_zone(&tz, &dst, dummy_t, &not_found, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3441 	if ((dummy_t->z >= (100 * 60 * 60)) || (dummy_t->z <= (-100 * 60 * 60))) {
3442 		php_error_docref(NULL, E_WARNING, "Timezone offset is out of range (%s)", orig_tz);
3443 		timelib_free(dummy_t->tz_abbr);
3444 		efree(dummy_t);
3445 		return FAILURE;
3446 	}
3447 	dummy_t->dst = dst;
3448 	if (!not_found && (*tz != '\0')) {
3449 		php_error_docref(NULL, E_WARNING, "Unknown or bad timezone (%s)", orig_tz);
3450 		timelib_free(dummy_t->tz_abbr);
3451 		efree(dummy_t);
3452 		return FAILURE;
3453 	}
3454 	if (not_found) {
3455 		php_error_docref(NULL, E_WARNING, "Unknown or bad timezone (%s)", orig_tz);
3456 		efree(dummy_t);
3457 		return FAILURE;
3458 	} else {
3459 		set_timezone_from_timelib_time(tzobj, dummy_t);
3460 		timelib_free(dummy_t->tz_abbr);
3461 		efree(dummy_t);
3462 		return SUCCESS;
3463 	}
3464 } /* }}} */
3465 
3466 /* {{{ Returns new DateTimeZone object */
PHP_FUNCTION(timezone_open)3467 PHP_FUNCTION(timezone_open)
3468 {
3469 	zend_string *tz;
3470 	php_timezone_obj *tzobj;
3471 
3472 	ZEND_PARSE_PARAMETERS_START(1, 1)
3473 		Z_PARAM_PATH_STR(tz) /* To prevent null bytes */
3474 	ZEND_PARSE_PARAMETERS_END();
3475 
3476 	tzobj = Z_PHPTIMEZONE_P(php_date_instantiate(date_ce_timezone, return_value));
3477 	if (SUCCESS != timezone_initialize(tzobj, ZSTR_VAL(tz), ZSTR_LEN(tz))) {
3478 		zval_ptr_dtor(return_value);
3479 		RETURN_FALSE;
3480 	}
3481 }
3482 /* }}} */
3483 
3484 /* {{{ Creates new DateTimeZone object. */
PHP_METHOD(DateTimeZone,__construct)3485 PHP_METHOD(DateTimeZone, __construct)
3486 {
3487 	zend_string *tz;
3488 	php_timezone_obj *tzobj;
3489 	zend_error_handling error_handling;
3490 
3491 	ZEND_PARSE_PARAMETERS_START(1, 1)
3492 		Z_PARAM_PATH_STR(tz) /* To prevent null bytes */
3493 	ZEND_PARSE_PARAMETERS_END();
3494 
3495 	zend_replace_error_handling(EH_THROW, NULL, &error_handling);
3496 	tzobj = Z_PHPTIMEZONE_P(ZEND_THIS);
3497 	timezone_initialize(tzobj, ZSTR_VAL(tz), ZSTR_LEN(tz));
3498 	zend_restore_error_handling(&error_handling);
3499 }
3500 /* }}} */
3501 
php_date_timezone_initialize_from_hash(zval ** return_value,php_timezone_obj ** tzobj,HashTable * myht)3502 static int php_date_timezone_initialize_from_hash(zval **return_value, php_timezone_obj **tzobj, HashTable *myht) /* {{{ */
3503 {
3504 	zval            *z_timezone_type;
3505 
3506 	if ((z_timezone_type = zend_hash_str_find(myht, "timezone_type", sizeof("timezone_type") - 1)) != NULL) {
3507 		zval *z_timezone;
3508 
3509 		if ((z_timezone = zend_hash_str_find(myht, "timezone", sizeof("timezone") - 1)) != NULL) {
3510 			if (Z_TYPE_P(z_timezone_type) != IS_LONG) {
3511 				return FAILURE;
3512 			}
3513 			if (Z_TYPE_P(z_timezone) != IS_STRING) {
3514 				return FAILURE;
3515 			}
3516 			if (SUCCESS == timezone_initialize(*tzobj, Z_STRVAL_P(z_timezone), Z_STRLEN_P(z_timezone))) {
3517 				return SUCCESS;
3518 			}
3519 		}
3520 	}
3521 	return FAILURE;
3522 } /* }}} */
3523 
3524 /* {{{  */
PHP_METHOD(DateTimeZone,__set_state)3525 PHP_METHOD(DateTimeZone, __set_state)
3526 {
3527 	php_timezone_obj *tzobj;
3528 	zval             *array;
3529 	HashTable        *myht;
3530 
3531 	ZEND_PARSE_PARAMETERS_START(1, 1)
3532 		Z_PARAM_ARRAY(array)
3533 	ZEND_PARSE_PARAMETERS_END();
3534 
3535 	myht = Z_ARRVAL_P(array);
3536 
3537 	php_date_instantiate(date_ce_timezone, return_value);
3538 	tzobj = Z_PHPTIMEZONE_P(return_value);
3539 	if(php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht) != SUCCESS) {
3540 		zend_throw_error(NULL, "Timezone initialization failed");
3541 		zval_ptr_dtor(return_value);
3542 	}
3543 }
3544 /* }}} */
3545 
3546 /* {{{  */
PHP_METHOD(DateTimeZone,__wakeup)3547 PHP_METHOD(DateTimeZone, __wakeup)
3548 {
3549 	zval             *object = ZEND_THIS;
3550 	php_timezone_obj *tzobj;
3551 	HashTable        *myht;
3552 
3553 	ZEND_PARSE_PARAMETERS_NONE();
3554 
3555 	tzobj = Z_PHPTIMEZONE_P(object);
3556 
3557 	myht = Z_OBJPROP_P(object);
3558 
3559 	if(php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht) != SUCCESS) {
3560 		zend_throw_error(NULL, "Timezone initialization failed");
3561 	}
3562 }
3563 /* }}} */
3564 
3565 /* {{{ Returns the name of the timezone. */
PHP_FUNCTION(timezone_name_get)3566 PHP_FUNCTION(timezone_name_get)
3567 {
3568 	zval             *object;
3569 	php_timezone_obj *tzobj;
3570 
3571 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_timezone) == FAILURE) {
3572 		RETURN_THROWS();
3573 	}
3574 	tzobj = Z_PHPTIMEZONE_P(object);
3575 	DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3576 	php_timezone_to_string(tzobj, return_value);
3577 }
3578 /* }}} */
3579 
3580 /* {{{ Returns the timezone name from abbreviation */
PHP_FUNCTION(timezone_name_from_abbr)3581 PHP_FUNCTION(timezone_name_from_abbr)
3582 {
3583 	zend_string  *abbr;
3584 	char         *tzid;
3585 	zend_long     gmtoffset = -1;
3586 	zend_long     isdst = -1;
3587 
3588 	ZEND_PARSE_PARAMETERS_START(1, 3)
3589 		Z_PARAM_STR(abbr)
3590 		Z_PARAM_OPTIONAL
3591 		Z_PARAM_LONG(gmtoffset)
3592 		Z_PARAM_LONG(isdst)
3593 	ZEND_PARSE_PARAMETERS_END();
3594 
3595 	tzid = timelib_timezone_id_from_abbr(ZSTR_VAL(abbr), gmtoffset, isdst);
3596 
3597 	if (tzid) {
3598 		RETURN_STRING(tzid);
3599 	} else {
3600 		RETURN_FALSE;
3601 	}
3602 }
3603 /* }}} */
3604 
3605 /* {{{ Returns the timezone offset. */
PHP_FUNCTION(timezone_offset_get)3606 PHP_FUNCTION(timezone_offset_get)
3607 {
3608 	zval                *object, *dateobject;
3609 	php_timezone_obj    *tzobj;
3610 	php_date_obj        *dateobj;
3611 	timelib_time_offset *offset;
3612 
3613 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_timezone, &dateobject, date_ce_interface) == FAILURE) {
3614 		RETURN_THROWS();
3615 	}
3616 	tzobj = Z_PHPTIMEZONE_P(object);
3617 	DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3618 	dateobj = Z_PHPDATE_P(dateobject);
3619 	DATE_CHECK_INITIALIZED(dateobj->time, DateTimeInterface);
3620 
3621 	switch (tzobj->type) {
3622 		case TIMELIB_ZONETYPE_ID:
3623 			offset = timelib_get_time_zone_info(dateobj->time->sse, tzobj->tzi.tz);
3624 			RETVAL_LONG(offset->offset);
3625 			timelib_time_offset_dtor(offset);
3626 			break;
3627 		case TIMELIB_ZONETYPE_OFFSET:
3628 			RETURN_LONG(tzobj->tzi.utc_offset);
3629 			break;
3630 		case TIMELIB_ZONETYPE_ABBR:
3631 			RETURN_LONG(tzobj->tzi.z.utc_offset + (tzobj->tzi.z.dst * 3600));
3632 			break;
3633 	}
3634 }
3635 /* }}} */
3636 
3637 /* {{{ Returns numerically indexed array containing associative array for all transitions in the specified range for the timezone. */
PHP_FUNCTION(timezone_transitions_get)3638 PHP_FUNCTION(timezone_transitions_get)
3639 {
3640 	zval                *object, element;
3641 	php_timezone_obj    *tzobj;
3642 	unsigned int         begin = 0, found;
3643 	zend_long            timestamp_begin = ZEND_LONG_MIN, timestamp_end = ZEND_LONG_MAX;
3644 
3645 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|ll", &object, date_ce_timezone, &timestamp_begin, &timestamp_end) == FAILURE) {
3646 		RETURN_THROWS();
3647 	}
3648 	tzobj = Z_PHPTIMEZONE_P(object);
3649 	DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3650 	if (tzobj->type != TIMELIB_ZONETYPE_ID) {
3651 		RETURN_FALSE;
3652 	}
3653 
3654 #define add_nominal() \
3655 		array_init(&element); \
3656 		add_assoc_long(&element, "ts",     timestamp_begin); \
3657 		add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601, 13, timestamp_begin, 0)); \
3658 		add_assoc_long(&element, "offset", tzobj->tzi.tz->type[0].offset); \
3659 		add_assoc_bool(&element, "isdst",  tzobj->tzi.tz->type[0].isdst); \
3660 		add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[0].abbr_idx]); \
3661 		add_next_index_zval(return_value, &element);
3662 
3663 #define add(i,ts) \
3664 		array_init(&element); \
3665 		add_assoc_long(&element, "ts",     ts); \
3666 		add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601, 13, ts, 0)); \
3667 		add_assoc_long(&element, "offset", tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].offset); \
3668 		add_assoc_bool(&element, "isdst",  tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].isdst); \
3669 		add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].abbr_idx]); \
3670 		add_next_index_zval(return_value, &element);
3671 
3672 #define add_last() add(tzobj->tzi.tz->bit64.timecnt - 1, timestamp_begin)
3673 
3674 	array_init(return_value);
3675 
3676 	if (timestamp_begin == ZEND_LONG_MIN) {
3677 		add_nominal();
3678 		begin = 0;
3679 		found = 1;
3680 	} else {
3681 		begin = 0;
3682 		found = 0;
3683 		if (tzobj->tzi.tz->bit64.timecnt > 0) {
3684 			do {
3685 				if (tzobj->tzi.tz->trans[begin] > timestamp_begin) {
3686 					if (begin > 0) {
3687 						add(begin - 1, timestamp_begin);
3688 					} else {
3689 						add_nominal();
3690 					}
3691 					found = 1;
3692 					break;
3693 				}
3694 				begin++;
3695 			} while (begin < tzobj->tzi.tz->bit64.timecnt);
3696 		}
3697 	}
3698 
3699 	if (!found) {
3700 		if (tzobj->tzi.tz->bit64.timecnt > 0) {
3701 			add_last();
3702 		} else {
3703 			add_nominal();
3704 		}
3705 	} else {
3706 		unsigned int i;
3707 		for (i = begin; i < tzobj->tzi.tz->bit64.timecnt; ++i) {
3708 			if (tzobj->tzi.tz->trans[i] < timestamp_end) {
3709 				add(i, tzobj->tzi.tz->trans[i]);
3710 			}
3711 		}
3712 	}
3713 }
3714 /* }}} */
3715 
3716 /* {{{ Returns location information for a timezone, including country code, latitude/longitude and comments */
PHP_FUNCTION(timezone_location_get)3717 PHP_FUNCTION(timezone_location_get)
3718 {
3719 	zval                *object;
3720 	php_timezone_obj    *tzobj;
3721 
3722 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_timezone) == FAILURE) {
3723 		RETURN_THROWS();
3724 	}
3725 	tzobj = Z_PHPTIMEZONE_P(object);
3726 	DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3727 	if (tzobj->type != TIMELIB_ZONETYPE_ID) {
3728 		RETURN_FALSE;
3729 	}
3730 
3731 	array_init(return_value);
3732 	add_assoc_string(return_value, "country_code", tzobj->tzi.tz->location.country_code);
3733 	add_assoc_double(return_value, "latitude", tzobj->tzi.tz->location.latitude);
3734 	add_assoc_double(return_value, "longitude", tzobj->tzi.tz->location.longitude);
3735 	add_assoc_string(return_value, "comments", tzobj->tzi.tz->location.comments);
3736 }
3737 /* }}} */
3738 
date_interval_initialize(timelib_rel_time ** rt,char * format,size_t format_length)3739 static int date_interval_initialize(timelib_rel_time **rt, /*const*/ char *format, size_t format_length) /* {{{ */
3740 {
3741 	timelib_time     *b = NULL, *e = NULL;
3742 	timelib_rel_time *p = NULL;
3743 	int               r = 0;
3744 	int               retval = 0;
3745 	timelib_error_container *errors;
3746 
3747 	timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);
3748 
3749 	if (errors->error_count > 0) {
3750 		php_error_docref(NULL, E_WARNING, "Unknown or bad format (%s)", format);
3751 		retval = FAILURE;
3752 		if (p) {
3753 			timelib_rel_time_dtor(p);
3754 		}
3755 	} else {
3756 		if(p) {
3757 			*rt = p;
3758 			retval = SUCCESS;
3759 		} else {
3760 			if(b && e) {
3761 				timelib_update_ts(b, NULL);
3762 				timelib_update_ts(e, NULL);
3763 				*rt = timelib_diff(b, e);
3764 				retval = SUCCESS;
3765 			} else {
3766 				php_error_docref(NULL, E_WARNING, "Failed to parse interval (%s)", format);
3767 				retval = FAILURE;
3768 			}
3769 		}
3770 	}
3771 	timelib_error_container_dtor(errors);
3772 	timelib_free(b);
3773 	timelib_free(e);
3774 	return retval;
3775 } /* }}} */
3776 
date_interval_compare_objects(zval * o1,zval * o2)3777 static int date_interval_compare_objects(zval *o1, zval *o2) {
3778 	ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2);
3779 	/* There is no well defined way to compare intervals like P1M and P30D, which may compare
3780 	 * smaller, equal or greater depending on the point in time at which the interval starts. As
3781 	 * such, we treat DateInterval objects are non-comparable and emit a warning. */
3782 	zend_error(E_WARNING, "Cannot compare DateInterval objects");
3783 	return ZEND_UNCOMPARABLE;
3784 }
3785 
3786 /* {{{ date_interval_read_property */
date_interval_read_property(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)3787 static zval *date_interval_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
3788 {
3789 	php_interval_obj *obj;
3790 	zval *retval;
3791 	timelib_sll value = -1;
3792 	double      fvalue = -1;
3793 
3794 	obj = php_interval_obj_from_obj(object);
3795 
3796 	if (!obj->initialized) {
3797 		retval = zend_std_read_property(object, name, type, cache_slot, rv);
3798 		return retval;
3799 	}
3800 
3801 #define GET_VALUE_FROM_STRUCT(n,m)            \
3802 	if (strcmp(ZSTR_VAL(name), m) == 0) { \
3803 		value = obj->diff->n; \
3804 		break; \
3805 	}
3806 	do {
3807 		GET_VALUE_FROM_STRUCT(y, "y");
3808 		GET_VALUE_FROM_STRUCT(m, "m");
3809 		GET_VALUE_FROM_STRUCT(d, "d");
3810 		GET_VALUE_FROM_STRUCT(h, "h");
3811 		GET_VALUE_FROM_STRUCT(i, "i");
3812 		GET_VALUE_FROM_STRUCT(s, "s");
3813 		if (strcmp(ZSTR_VAL(name), "f") == 0) {
3814 			fvalue = obj->diff->us / 1000000.0;
3815 			break;
3816 		}
3817 		GET_VALUE_FROM_STRUCT(invert, "invert");
3818 		GET_VALUE_FROM_STRUCT(days, "days");
3819 		/* didn't find any */
3820 		retval = zend_std_read_property(object, name, type, cache_slot, rv);
3821 
3822 		return retval;
3823 	} while(0);
3824 
3825 	retval = rv;
3826 
3827 	if (fvalue != -1) {
3828 		ZVAL_DOUBLE(retval, fvalue);
3829 	} else if (value != -99999) {
3830 		ZVAL_LONG(retval, value);
3831 	} else {
3832 		ZVAL_FALSE(retval);
3833 	}
3834 
3835 	return retval;
3836 }
3837 /* }}} */
3838 
3839 /* {{{ date_interval_write_property */
date_interval_write_property(zend_object * object,zend_string * name,zval * value,void ** cache_slot)3840 static zval *date_interval_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
3841 {
3842 	php_interval_obj *obj;
3843 
3844 	obj = php_interval_obj_from_obj(object);
3845 
3846 	if (!obj->initialized) {
3847 		return zend_std_write_property(object, name, value, cache_slot);
3848 	}
3849 
3850 #define SET_VALUE_FROM_STRUCT(n,m) \
3851 	if (strcmp(ZSTR_VAL(name), m) == 0) { \
3852 		obj->diff->n = zval_get_long(value); \
3853 		break; \
3854 	}
3855 
3856 	do {
3857 		SET_VALUE_FROM_STRUCT(y, "y");
3858 		SET_VALUE_FROM_STRUCT(m, "m");
3859 		SET_VALUE_FROM_STRUCT(d, "d");
3860 		SET_VALUE_FROM_STRUCT(h, "h");
3861 		SET_VALUE_FROM_STRUCT(i, "i");
3862 		SET_VALUE_FROM_STRUCT(s, "s");
3863 		if (strcmp(ZSTR_VAL(name), "f") == 0) {
3864 			obj->diff->us = zend_dval_to_lval(zval_get_double(value) * 1000000.0);
3865 			break;
3866 		}
3867 		SET_VALUE_FROM_STRUCT(invert, "invert");
3868 		/* didn't find any */
3869 		value = zend_std_write_property(object, name, value, cache_slot);
3870 	} while(0);
3871 
3872 	return value;
3873 }
3874 /* }}} */
3875 
3876 /* {{{ date_interval_get_property_ptr_ptr */
date_interval_get_property_ptr_ptr(zend_object * object,zend_string * name,int type,void ** cache_slot)3877 static zval *date_interval_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
3878 {
3879 	zval *ret;
3880 
3881 	if(zend_binary_strcmp("y", sizeof("y") - 1, ZSTR_VAL(name), ZSTR_LEN(name)) == 0 ||
3882 		zend_binary_strcmp("m", sizeof("m") - 1, ZSTR_VAL(name), ZSTR_LEN(name)) == 0 ||
3883 		zend_binary_strcmp("d", sizeof("d") - 1, ZSTR_VAL(name), ZSTR_LEN(name)) == 0 ||
3884 		zend_binary_strcmp("h", sizeof("h") - 1, ZSTR_VAL(name), ZSTR_LEN(name)) == 0 ||
3885 		zend_binary_strcmp("i", sizeof("i") - 1, ZSTR_VAL(name), ZSTR_LEN(name)) == 0 ||
3886 		zend_binary_strcmp("s", sizeof("s") - 1, ZSTR_VAL(name), ZSTR_LEN(name)) == 0 ||
3887 		zend_binary_strcmp("f", sizeof("f") - 1, ZSTR_VAL(name), ZSTR_LEN(name)) == 0 ||
3888 		zend_binary_strcmp("days", sizeof("days") - 1, ZSTR_VAL(name), ZSTR_LEN(name)) == 0 ||
3889 		zend_binary_strcmp("invert", sizeof("invert") - 1, ZSTR_VAL(name), ZSTR_LEN(name)) == 0) {
3890 		/* Fallback to read_property. */
3891 		ret = NULL;
3892 	} else {
3893 		ret = zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
3894 	}
3895 
3896 	return ret;
3897 }
3898 /* }}} */
3899 
3900 /* {{{ Creates new DateInterval object. */
PHP_METHOD(DateInterval,__construct)3901 PHP_METHOD(DateInterval, __construct)
3902 {
3903 	zend_string *interval_string = NULL;
3904 	timelib_rel_time *reltime;
3905 	zend_error_handling error_handling;
3906 
3907 	ZEND_PARSE_PARAMETERS_START(1, 1)
3908 		Z_PARAM_STR(interval_string)
3909 	ZEND_PARSE_PARAMETERS_END();
3910 
3911 	zend_replace_error_handling(EH_THROW, NULL, &error_handling);
3912 	if (date_interval_initialize(&reltime, ZSTR_VAL(interval_string), ZSTR_LEN(interval_string)) == SUCCESS) {
3913 		php_interval_obj *diobj = Z_PHPINTERVAL_P(ZEND_THIS);
3914 		diobj->diff = reltime;
3915 		diobj->initialized = 1;
3916 	}
3917 	zend_restore_error_handling(&error_handling);
3918 }
3919 /* }}} */
3920 
3921 
php_date_interval_initialize_from_hash(zval ** return_value,php_interval_obj ** intobj,HashTable * myht)3922 static int php_date_interval_initialize_from_hash(zval **return_value, php_interval_obj **intobj, HashTable *myht) /* {{{ */
3923 {
3924 	(*intobj)->diff = timelib_rel_time_ctor();
3925 
3926 #define PHP_DATE_INTERVAL_READ_PROPERTY(element, member, itype, def) \
3927 	do { \
3928 		zval *z_arg = zend_hash_str_find(myht, element, sizeof(element) - 1); \
3929 		if (z_arg && Z_TYPE_P(z_arg) <= IS_STRING) { \
3930 			(*intobj)->diff->member = (itype)zval_get_long(z_arg); \
3931 		} else { \
3932 			(*intobj)->diff->member = (itype)def; \
3933 		} \
3934 	} while (0);
3935 
3936 #define PHP_DATE_INTERVAL_READ_PROPERTY_I64(element, member) \
3937 	do { \
3938 		zval *z_arg = zend_hash_str_find(myht, element, sizeof(element) - 1); \
3939 		if (z_arg && Z_TYPE_P(z_arg) <= IS_STRING) { \
3940 			zend_string *tmp_str; \
3941 			zend_string *str = zval_get_tmp_string(z_arg, &tmp_str); \
3942 			DATE_A64I((*intobj)->diff->member, ZSTR_VAL(str)); \
3943 			zend_tmp_string_release(tmp_str); \
3944 		} else { \
3945 			(*intobj)->diff->member = -1LL; \
3946 		} \
3947 	} while (0);
3948 
3949 #define PHP_DATE_INTERVAL_READ_PROPERTY_DAYS(member) \
3950 	do { \
3951 		zval *z_arg = zend_hash_str_find(myht, "days", sizeof("days") - 1); \
3952 		if (z_arg && Z_TYPE_P(z_arg) == IS_FALSE) { \
3953 			(*intobj)->diff->member = -99999; \
3954 		} else if (z_arg && Z_TYPE_P(z_arg) <= IS_STRING) { \
3955 			zend_string *str = zval_get_string(z_arg); \
3956 			DATE_A64I((*intobj)->diff->member, ZSTR_VAL(str)); \
3957 			zend_string_release(str); \
3958 		} else { \
3959 			(*intobj)->diff->member = -1LL; \
3960 		} \
3961 	} while (0);
3962 
3963 #define PHP_DATE_INTERVAL_READ_PROPERTY_DOUBLE(element, member, def) \
3964 	do { \
3965 		zval *z_arg = zend_hash_str_find(myht, element, sizeof(element) - 1); \
3966 		if (z_arg) { \
3967 			(*intobj)->diff->member = (double)zval_get_double(z_arg); \
3968 		} else { \
3969 			(*intobj)->diff->member = (double)def; \
3970 		} \
3971 	} while (0);
3972 
3973 	PHP_DATE_INTERVAL_READ_PROPERTY("y", y, timelib_sll, -1)
3974 	PHP_DATE_INTERVAL_READ_PROPERTY("m", m, timelib_sll, -1)
3975 	PHP_DATE_INTERVAL_READ_PROPERTY("d", d, timelib_sll, -1)
3976 	PHP_DATE_INTERVAL_READ_PROPERTY("h", h, timelib_sll, -1)
3977 	PHP_DATE_INTERVAL_READ_PROPERTY("i", i, timelib_sll, -1)
3978 	PHP_DATE_INTERVAL_READ_PROPERTY("s", s, timelib_sll, -1)
3979 	{
3980 		zval *z_arg = zend_hash_str_find(myht, "f", sizeof("f") - 1);
3981 		if (z_arg) {
3982 			(*intobj)->diff->us = zend_dval_to_lval(zval_get_double(z_arg) * 1000000.0);
3983 		}
3984 	}
3985 	PHP_DATE_INTERVAL_READ_PROPERTY("weekday", weekday, int, -1)
3986 	PHP_DATE_INTERVAL_READ_PROPERTY("weekday_behavior", weekday_behavior, int, -1)
3987 	PHP_DATE_INTERVAL_READ_PROPERTY("first_last_day_of", first_last_day_of, int, -1)
3988 	PHP_DATE_INTERVAL_READ_PROPERTY("invert", invert, int, 0);
3989 	PHP_DATE_INTERVAL_READ_PROPERTY_DAYS(days);
3990 	PHP_DATE_INTERVAL_READ_PROPERTY("special_type", special.type, unsigned int, 0);
3991 	PHP_DATE_INTERVAL_READ_PROPERTY_I64("special_amount", special.amount);
3992 	PHP_DATE_INTERVAL_READ_PROPERTY("have_weekday_relative", have_weekday_relative, unsigned int, 0);
3993 	PHP_DATE_INTERVAL_READ_PROPERTY("have_special_relative", have_special_relative, unsigned int, 0);
3994 	(*intobj)->initialized = 1;
3995 
3996 	return 0;
3997 } /* }}} */
3998 
3999 /* {{{ */
PHP_METHOD(DateInterval,__set_state)4000 PHP_METHOD(DateInterval, __set_state)
4001 {
4002 	php_interval_obj *intobj;
4003 	zval             *array;
4004 	HashTable        *myht;
4005 
4006 	ZEND_PARSE_PARAMETERS_START(1, 1)
4007 		Z_PARAM_ARRAY(array)
4008 	ZEND_PARSE_PARAMETERS_END();
4009 
4010 	myht = Z_ARRVAL_P(array);
4011 
4012 	php_date_instantiate(date_ce_interval, return_value);
4013 	intobj = Z_PHPINTERVAL_P(return_value);
4014 	php_date_interval_initialize_from_hash(&return_value, &intobj, myht);
4015 }
4016 /* }}} */
4017 
4018 /* {{{ */
PHP_METHOD(DateInterval,__wakeup)4019 PHP_METHOD(DateInterval, __wakeup)
4020 {
4021 	zval             *object = ZEND_THIS;
4022 	php_interval_obj *intobj;
4023 	HashTable        *myht;
4024 
4025 	ZEND_PARSE_PARAMETERS_NONE();
4026 
4027 	intobj = Z_PHPINTERVAL_P(object);
4028 
4029 	myht = Z_OBJPROP_P(object);
4030 
4031 	php_date_interval_initialize_from_hash(&return_value, &intobj, myht);
4032 }
4033 /* }}} */
4034 
4035 /* {{{ 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)4036 PHP_FUNCTION(date_interval_create_from_date_string)
4037 {
4038 	zend_string    *time_str = NULL;
4039 	timelib_time   *time;
4040 	timelib_error_container *err = NULL;
4041 	php_interval_obj *diobj;
4042 
4043 	ZEND_PARSE_PARAMETERS_START(1, 1)
4044 		Z_PARAM_STR(time_str)
4045 	ZEND_PARSE_PARAMETERS_END();
4046 
4047 	time = timelib_strtotime(ZSTR_VAL(time_str), ZSTR_LEN(time_str), &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
4048 
4049 	if (err->error_count > 0)  {
4050 		php_error_docref(NULL, E_WARNING, "Unknown or bad format (%s) at position %d (%c): %s", ZSTR_VAL(time_str),
4051 			err->error_messages[0].position, err->error_messages[0].character ? err->error_messages[0].character : ' ', err->error_messages[0].message);
4052 		RETVAL_FALSE;
4053 		goto cleanup;
4054 	}
4055 
4056 	php_date_instantiate(date_ce_interval, return_value);
4057 	diobj = Z_PHPINTERVAL_P(return_value);
4058 	diobj->diff = timelib_rel_time_clone(&time->relative);
4059 	diobj->initialized = 1;
4060 
4061 cleanup:
4062 	timelib_time_dtor(time);
4063 	timelib_error_container_dtor(err);
4064 }
4065 /* }}} */
4066 
4067 /* {{{ date_interval_format -  */
date_interval_format(char * format,size_t format_len,timelib_rel_time * t)4068 static zend_string *date_interval_format(char *format, size_t format_len, timelib_rel_time *t)
4069 {
4070 	smart_str            string = {0};
4071 	size_t               i;
4072 	int                  length, have_format_spec = 0;
4073 	char                 buffer[33];
4074 
4075 	if (!format_len) {
4076 		return ZSTR_EMPTY_ALLOC();
4077 	}
4078 
4079 	for (i = 0; i < format_len; i++) {
4080 		if (have_format_spec) {
4081 			switch (format[i]) {
4082 				case 'Y': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->y); break;
4083 				case 'y': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->y); break;
4084 
4085 				case 'M': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->m); break;
4086 				case 'm': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->m); break;
4087 
4088 				case 'D': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->d); break;
4089 				case 'd': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->d); break;
4090 
4091 				case 'H': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->h); break;
4092 				case 'h': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->h); break;
4093 
4094 				case 'I': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->i); break;
4095 				case 'i': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->i); break;
4096 
4097 				case 'S': length = slprintf(buffer, sizeof(buffer), "%02" ZEND_LONG_FMT_SPEC, (zend_long) t->s); break;
4098 				case 's': length = slprintf(buffer, sizeof(buffer), ZEND_LONG_FMT, (zend_long) t->s); break;
4099 
4100 				case 'F': length = slprintf(buffer, sizeof(buffer), "%06" ZEND_LONG_FMT_SPEC, (zend_long) t->us); break;
4101 				case 'f': length = slprintf(buffer, sizeof(buffer), ZEND_LONG_FMT, (zend_long) t->us); break;
4102 
4103 				case 'a': {
4104 					if ((int) t->days != -99999) {
4105 						length = slprintf(buffer, sizeof(buffer), "%d", (int) t->days);
4106 					} else {
4107 						length = slprintf(buffer, sizeof(buffer), "(unknown)");
4108 					}
4109 				} break;
4110 				case 'r': length = slprintf(buffer, sizeof(buffer), "%s", t->invert ? "-" : ""); break;
4111 				case 'R': length = slprintf(buffer, sizeof(buffer), "%c", t->invert ? '-' : '+'); break;
4112 
4113 				case '%': length = slprintf(buffer, sizeof(buffer), "%%"); break;
4114 				default: buffer[0] = '%'; buffer[1] = format[i]; buffer[2] = '\0'; length = 2; break;
4115 			}
4116 			smart_str_appendl(&string, buffer, length);
4117 			have_format_spec = 0;
4118 		} else {
4119 			if (format[i] == '%') {
4120 				have_format_spec = 1;
4121 			} else {
4122 				smart_str_appendc(&string, format[i]);
4123 			}
4124 		}
4125 	}
4126 
4127 	smart_str_0(&string);
4128 
4129 	if (string.s == NULL) {
4130 		return ZSTR_EMPTY_ALLOC();
4131 	}
4132 
4133 	return string.s;
4134 }
4135 /* }}} */
4136 
4137 /* {{{ Formats the interval. */
PHP_FUNCTION(date_interval_format)4138 PHP_FUNCTION(date_interval_format)
4139 {
4140 	zval             *object;
4141 	php_interval_obj *diobj;
4142 	char             *format;
4143 	size_t            format_len;
4144 
4145 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, date_ce_interval, &format, &format_len) == FAILURE) {
4146 		RETURN_THROWS();
4147 	}
4148 	diobj = Z_PHPINTERVAL_P(object);
4149 	DATE_CHECK_INITIALIZED(diobj->initialized, DateInterval);
4150 
4151 	RETURN_STR(date_interval_format(format, format_len, diobj->diff));
4152 }
4153 /* }}} */
4154 
date_period_initialize(timelib_time ** st,timelib_time ** et,timelib_rel_time ** d,zend_long * recurrences,char * format,size_t format_length)4155 static int date_period_initialize(timelib_time **st, timelib_time **et, timelib_rel_time **d, zend_long *recurrences, /*const*/ char *format, size_t format_length) /* {{{ */
4156 {
4157 	timelib_time     *b = NULL, *e = NULL;
4158 	timelib_rel_time *p = NULL;
4159 	int               r = 0;
4160 	int               retval = 0;
4161 	timelib_error_container *errors;
4162 
4163 	timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);
4164 
4165 	if (errors->error_count > 0) {
4166 		php_error_docref(NULL, E_WARNING, "Unknown or bad format (%s)", format);
4167 		retval = FAILURE;
4168 		if (b) {
4169 			timelib_time_dtor(b);
4170 		}
4171 		if (e) {
4172 			timelib_time_dtor(e);
4173 		}
4174 		if (p) {
4175 			timelib_rel_time_dtor(p);
4176 		}
4177 	} else {
4178 		*st = b;
4179 		*et = e;
4180 		*d  = p;
4181 		*recurrences = r;
4182 		retval = SUCCESS;
4183 	}
4184 	timelib_error_container_dtor(errors);
4185 	return retval;
4186 } /* }}} */
4187 
4188 /* {{{ Creates new DatePeriod object. */
PHP_METHOD(DatePeriod,__construct)4189 PHP_METHOD(DatePeriod, __construct)
4190 {
4191 	php_period_obj   *dpobj;
4192 	php_date_obj     *dateobj;
4193 	zval *start, *end = NULL, *interval;
4194 	zend_long  recurrences = 0, options = 0;
4195 	char *isostr = NULL;
4196 	size_t   isostr_len = 0;
4197 	timelib_time *clone;
4198 	zend_error_handling error_handling;
4199 
4200 	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) {
4201 		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) {
4202 			if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "s|l", &isostr, &isostr_len, &options) == FAILURE) {
4203 				zend_type_error("DatePeriod::__construct() accepts (DateTimeInterface, DateInterval, int [, int]), or (DateTimeInterface, DateInterval, DateTime [, int]), or (string [, int]) as arguments");
4204 				RETURN_THROWS();
4205 			}
4206 		}
4207 	}
4208 
4209 	dpobj = Z_PHPPERIOD_P(ZEND_THIS);
4210 	dpobj->current = NULL;
4211 
4212 	if (isostr) {
4213 		zend_replace_error_handling(EH_THROW, NULL, &error_handling);
4214 		date_period_initialize(&(dpobj->start), &(dpobj->end), &(dpobj->interval), &recurrences, isostr, isostr_len);
4215 		zend_restore_error_handling(&error_handling);
4216 		if (EG(exception)) {
4217 			RETURN_THROWS();
4218 		}
4219 
4220 		if (dpobj->start == NULL) {
4221 			zend_string *func = get_active_function_or_method_name();
4222 			zend_throw_error(zend_ce_exception, "%s(): ISO interval must contain a start date, \"%s\" given", ZSTR_VAL(func), isostr);
4223 			zend_string_release(func);
4224 			RETURN_THROWS();
4225 		}
4226 		if (dpobj->interval == NULL) {
4227 			zend_string *func = get_active_function_or_method_name();
4228 			zend_throw_error(zend_ce_exception, "%s(): ISO interval must contain an interval, \"%s\" given", ZSTR_VAL(func), isostr);
4229 			zend_string_release(func);
4230 			RETURN_THROWS();
4231 		}
4232 		if (dpobj->end == NULL && recurrences == 0) {
4233 			zend_string *func = get_active_function_or_method_name();
4234 			zend_throw_error(zend_ce_exception, "%s(): ISO interval must contain an end date or a recurrence count, \"%s\" given", ZSTR_VAL(func), isostr);
4235 			zend_string_release(func);
4236 			RETURN_THROWS();
4237 		}
4238 
4239 		if (dpobj->start) {
4240 			timelib_update_ts(dpobj->start, NULL);
4241 		}
4242 		if (dpobj->end) {
4243 			timelib_update_ts(dpobj->end, NULL);
4244 		}
4245 		dpobj->start_ce = date_ce_date;
4246 	} else {
4247 		/* init */
4248 		php_interval_obj *intobj = Z_PHPINTERVAL_P(interval);
4249 
4250 		/* start date */
4251 		dateobj = Z_PHPDATE_P(start);
4252 		clone = timelib_time_ctor();
4253 		memcpy(clone, dateobj->time, sizeof(timelib_time));
4254 		if (dateobj->time->tz_abbr) {
4255 			clone->tz_abbr = timelib_strdup(dateobj->time->tz_abbr);
4256 		}
4257 		if (dateobj->time->tz_info) {
4258 			clone->tz_info = dateobj->time->tz_info;
4259 		}
4260 		dpobj->start = clone;
4261 		dpobj->start_ce = Z_OBJCE_P(start);
4262 
4263 		/* interval */
4264 		dpobj->interval = timelib_rel_time_clone(intobj->diff);
4265 
4266 		/* end date */
4267 		if (end) {
4268 			dateobj = Z_PHPDATE_P(end);
4269 			clone = timelib_time_clone(dateobj->time);
4270 			dpobj->end = clone;
4271 		}
4272 	}
4273 
4274 	if (dpobj->end == NULL && recurrences < 1) {
4275 		zend_string *func = get_active_function_or_method_name();
4276 		zend_throw_error(zend_ce_exception, "%s(): Recurrence count must be greater than 0", ZSTR_VAL(func));
4277 		zend_string_release(func);
4278 		RETURN_THROWS();
4279 	}
4280 
4281 	/* options */
4282 	dpobj->include_start_date = !(options & PHP_DATE_PERIOD_EXCLUDE_START_DATE);
4283 
4284 	/* recurrrences */
4285 	dpobj->recurrences = recurrences + dpobj->include_start_date;
4286 
4287 	dpobj->initialized = 1;
4288 }
4289 /* }}} */
4290 
4291 /* {{{ Get start date. */
PHP_METHOD(DatePeriod,getStartDate)4292 PHP_METHOD(DatePeriod, getStartDate)
4293 {
4294 	php_period_obj   *dpobj;
4295 	php_date_obj     *dateobj;
4296 
4297 	ZEND_PARSE_PARAMETERS_NONE();
4298 
4299 	dpobj = Z_PHPPERIOD_P(ZEND_THIS);
4300 	DATE_CHECK_INITIALIZED(dpobj->start, DatePeriod);
4301 
4302 	php_date_instantiate(dpobj->start_ce, return_value);
4303 	dateobj = Z_PHPDATE_P(return_value);
4304 	dateobj->time = timelib_time_ctor();
4305 	*dateobj->time = *dpobj->start;
4306 	if (dpobj->start->tz_abbr) {
4307 		dateobj->time->tz_abbr = timelib_strdup(dpobj->start->tz_abbr);
4308 	}
4309 	if (dpobj->start->tz_info) {
4310 		dateobj->time->tz_info = dpobj->start->tz_info;
4311 	}
4312 }
4313 /* }}} */
4314 
4315 /* {{{ Get end date. */
PHP_METHOD(DatePeriod,getEndDate)4316 PHP_METHOD(DatePeriod, getEndDate)
4317 {
4318         php_period_obj   *dpobj;
4319         php_date_obj     *dateobj;
4320 
4321         ZEND_PARSE_PARAMETERS_NONE();
4322 
4323         dpobj = Z_PHPPERIOD_P(ZEND_THIS);
4324 
4325         if (!dpobj->end) {
4326                 return;
4327         }
4328 
4329         php_date_instantiate(dpobj->start_ce, return_value);
4330         dateobj = Z_PHPDATE_P(return_value);
4331         dateobj->time = timelib_time_ctor();
4332         *dateobj->time = *dpobj->end;
4333         if (dpobj->end->tz_abbr) {
4334                 dateobj->time->tz_abbr = timelib_strdup(dpobj->end->tz_abbr);
4335         }
4336         if (dpobj->end->tz_info) {
4337                 dateobj->time->tz_info = dpobj->end->tz_info;
4338         }
4339 }
4340 /* }}} */
4341 
4342 /* {{{ Get date interval. */
PHP_METHOD(DatePeriod,getDateInterval)4343 PHP_METHOD(DatePeriod, getDateInterval)
4344 {
4345 	php_period_obj   *dpobj;
4346 	php_interval_obj *diobj;
4347 
4348 	ZEND_PARSE_PARAMETERS_NONE();
4349 
4350 	dpobj = Z_PHPPERIOD_P(ZEND_THIS);
4351 	DATE_CHECK_INITIALIZED(dpobj->interval, DatePeriod);
4352 
4353 	php_date_instantiate(date_ce_interval, return_value);
4354 	diobj = Z_PHPINTERVAL_P(return_value);
4355 	diobj->diff = timelib_rel_time_clone(dpobj->interval);
4356 	diobj->initialized = 1;
4357 }
4358 /* }}} */
4359 
4360 /* {{{ Get recurrences. */
PHP_METHOD(DatePeriod,getRecurrences)4361 PHP_METHOD(DatePeriod, getRecurrences)
4362 {
4363 	php_period_obj   *dpobj;
4364 
4365 	ZEND_PARSE_PARAMETERS_NONE();
4366 
4367 	dpobj = Z_PHPPERIOD_P(ZEND_THIS);
4368 
4369 	if (0 == dpobj->recurrences - dpobj->include_start_date) {
4370 		return;
4371 	}
4372 
4373 	RETURN_LONG(dpobj->recurrences - dpobj->include_start_date);
4374 }
4375 /* }}} */
4376 
PHP_METHOD(DatePeriod,getIterator)4377 PHP_METHOD(DatePeriod, getIterator)
4378 {
4379 	ZEND_PARSE_PARAMETERS_NONE();
4380 
4381 	zend_create_internal_iterator_zval(return_value, ZEND_THIS);
4382 }
4383 
check_id_allowed(char * id,zend_long what)4384 static int check_id_allowed(char *id, zend_long what) /* {{{ */
4385 {
4386 	if ((what & PHP_DATE_TIMEZONE_GROUP_AFRICA)     && strncasecmp(id, "Africa/",      7) == 0) return 1;
4387 	if ((what & PHP_DATE_TIMEZONE_GROUP_AMERICA)    && strncasecmp(id, "America/",     8) == 0) return 1;
4388 	if ((what & PHP_DATE_TIMEZONE_GROUP_ANTARCTICA) && strncasecmp(id, "Antarctica/", 11) == 0) return 1;
4389 	if ((what & PHP_DATE_TIMEZONE_GROUP_ARCTIC)     && strncasecmp(id, "Arctic/",      7) == 0) return 1;
4390 	if ((what & PHP_DATE_TIMEZONE_GROUP_ASIA)       && strncasecmp(id, "Asia/",        5) == 0) return 1;
4391 	if ((what & PHP_DATE_TIMEZONE_GROUP_ATLANTIC)   && strncasecmp(id, "Atlantic/",    9) == 0) return 1;
4392 	if ((what & PHP_DATE_TIMEZONE_GROUP_AUSTRALIA)  && strncasecmp(id, "Australia/",  10) == 0) return 1;
4393 	if ((what & PHP_DATE_TIMEZONE_GROUP_EUROPE)     && strncasecmp(id, "Europe/",      7) == 0) return 1;
4394 	if ((what & PHP_DATE_TIMEZONE_GROUP_INDIAN)     && strncasecmp(id, "Indian/",      7) == 0) return 1;
4395 	if ((what & PHP_DATE_TIMEZONE_GROUP_PACIFIC)    && strncasecmp(id, "Pacific/",     8) == 0) return 1;
4396 	if ((what & PHP_DATE_TIMEZONE_GROUP_UTC)        && strncasecmp(id, "UTC",          3) == 0) return 1;
4397 	return 0;
4398 } /* }}} */
4399 
4400 /* {{{ Returns numerically index array with all timezone identifiers. */
PHP_FUNCTION(timezone_identifiers_list)4401 PHP_FUNCTION(timezone_identifiers_list)
4402 {
4403 	const timelib_tzdb             *tzdb;
4404 	const timelib_tzdb_index_entry *table;
4405 	int                             i, item_count;
4406 	zend_long                       what = PHP_DATE_TIMEZONE_GROUP_ALL;
4407 	char                           *option = NULL;
4408 	size_t                          option_len = 0;
4409 
4410 	ZEND_PARSE_PARAMETERS_START(0, 2)
4411 		Z_PARAM_OPTIONAL
4412 		Z_PARAM_LONG(what)
4413 		Z_PARAM_STRING_OR_NULL(option, option_len)
4414 	ZEND_PARSE_PARAMETERS_END();
4415 
4416 	/* Extra validation */
4417 	if (what == PHP_DATE_TIMEZONE_PER_COUNTRY && option_len != 2) {
4418 		zend_argument_value_error(2, "must be a two-letter ISO 3166-1 compatible country code "
4419 			"when argument #1 ($timezoneGroup) is DateTimeZone::PER_COUNTRY");
4420 		RETURN_THROWS();
4421 	}
4422 
4423 	tzdb = DATE_TIMEZONEDB;
4424 	table = timelib_timezone_identifiers_list((timelib_tzdb*) tzdb, &item_count);
4425 
4426 	array_init(return_value);
4427 
4428 	for (i = 0; i < item_count; ++i) {
4429 		if (what == PHP_DATE_TIMEZONE_PER_COUNTRY) {
4430 			if (tzdb->data[table[i].pos + 5] == option[0] && tzdb->data[table[i].pos + 6] == option[1]) {
4431 				add_next_index_string(return_value, table[i].id);
4432 			}
4433 		} else if (what == PHP_DATE_TIMEZONE_GROUP_ALL_W_BC || (check_id_allowed(table[i].id, what) && (tzdb->data[table[i].pos + 4] == '\1'))) {
4434 			add_next_index_string(return_value, table[i].id);
4435 		}
4436 	};
4437 }
4438 /* }}} */
4439 
4440 /* {{{ Returns the Olson database version number. */
PHP_FUNCTION(timezone_version_get)4441 PHP_FUNCTION(timezone_version_get)
4442 {
4443 	const timelib_tzdb *tzdb;
4444 
4445 	ZEND_PARSE_PARAMETERS_NONE();
4446 
4447 	tzdb = DATE_TIMEZONEDB;
4448 	RETURN_STRING(tzdb->version);
4449 }
4450 /* }}} */
4451 
4452 /* {{{ Returns associative array containing dst, offset and the timezone name */
PHP_FUNCTION(timezone_abbreviations_list)4453 PHP_FUNCTION(timezone_abbreviations_list)
4454 {
4455 	const timelib_tz_lookup_table *table, *entry;
4456 	zval                          element, *abbr_array_p, abbr_array;
4457 
4458 	ZEND_PARSE_PARAMETERS_NONE();
4459 
4460 	table = timelib_timezone_abbreviations_list();
4461 	array_init(return_value);
4462 	entry = table;
4463 
4464 	do {
4465 		array_init(&element);
4466 		add_assoc_bool_ex(&element, "dst", sizeof("dst") -1, entry->type);
4467 		add_assoc_long_ex(&element, "offset", sizeof("offset") - 1, entry->gmtoffset);
4468 		if (entry->full_tz_name) {
4469 			add_assoc_string_ex(&element, "timezone_id", sizeof("timezone_id") - 1, entry->full_tz_name);
4470 		} else {
4471 			add_assoc_null_ex(&element, "timezone_id", sizeof("timezone_id") - 1);
4472 		}
4473 
4474 		abbr_array_p = zend_hash_str_find(Z_ARRVAL_P(return_value), entry->name, strlen(entry->name));
4475 		if (!abbr_array_p) {
4476 			array_init(&abbr_array);
4477 			add_assoc_zval(return_value, entry->name, &abbr_array);
4478 		} else {
4479 			ZVAL_COPY_VALUE(&abbr_array, abbr_array_p);
4480 		}
4481 		add_next_index_zval(&abbr_array, &element);
4482 		entry++;
4483 	} while (entry->name);
4484 }
4485 /* }}} */
4486 
4487 /* {{{ Sets the default timezone used by all date/time functions in a script */
PHP_FUNCTION(date_default_timezone_set)4488 PHP_FUNCTION(date_default_timezone_set)
4489 {
4490 	char *zone;
4491 	size_t   zone_len;
4492 
4493 	ZEND_PARSE_PARAMETERS_START(1, 1)
4494 		Z_PARAM_STRING(zone, zone_len)
4495 	ZEND_PARSE_PARAMETERS_END();
4496 
4497 	if (!timelib_timezone_id_is_valid(zone, DATE_TIMEZONEDB)) {
4498 		php_error_docref(NULL, E_NOTICE, "Timezone ID '%s' is invalid", zone);
4499 		RETURN_FALSE;
4500 	}
4501 	if (DATEG(timezone)) {
4502 		efree(DATEG(timezone));
4503 		DATEG(timezone) = NULL;
4504 	}
4505 	DATEG(timezone) = estrndup(zone, zone_len);
4506 	RETURN_TRUE;
4507 }
4508 /* }}} */
4509 
4510 /* {{{ Gets the default timezone used by all date/time functions in a script */
PHP_FUNCTION(date_default_timezone_get)4511 PHP_FUNCTION(date_default_timezone_get)
4512 {
4513 	timelib_tzinfo *default_tz;
4514 	ZEND_PARSE_PARAMETERS_NONE();
4515 
4516 	default_tz = get_timezone_info();
4517 	if (!default_tz) {
4518 		RETURN_THROWS();
4519 	}
4520 	RETVAL_STRING(default_tz->name);
4521 }
4522 /* }}} */
4523 
4524 /* {{{ php_do_date_sunrise_sunset
4525  *  Common for date_sunrise() and date_sunset() functions
4526  */
php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAMETERS,int calc_sunset)4527 static void php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAMETERS, int calc_sunset)
4528 {
4529 	double latitude, longitude, zenith, gmt_offset, altitude;
4530 	zend_bool latitude_is_null = 1, longitude_is_null = 1, zenith_is_null = 1, gmt_offset_is_null = 1;
4531 	double h_rise, h_set, N;
4532 	timelib_sll rise, set, transit;
4533 	zend_long time, retformat = SUNFUNCS_RET_STRING;
4534 	int             rs;
4535 	timelib_time   *t;
4536 	timelib_tzinfo *tzi;
4537 	zend_string    *retstr;
4538 
4539 	ZEND_PARSE_PARAMETERS_START(1, 6)
4540 		Z_PARAM_LONG(time)
4541 		Z_PARAM_OPTIONAL
4542 		Z_PARAM_LONG(retformat)
4543 		Z_PARAM_DOUBLE_OR_NULL(latitude, latitude_is_null)
4544 		Z_PARAM_DOUBLE_OR_NULL(longitude, longitude_is_null)
4545 		Z_PARAM_DOUBLE_OR_NULL(zenith, zenith_is_null)
4546 		Z_PARAM_DOUBLE_OR_NULL(gmt_offset, gmt_offset_is_null)
4547 	ZEND_PARSE_PARAMETERS_END();
4548 
4549 	if (latitude_is_null) {
4550 		latitude = INI_FLT("date.default_latitude");
4551 	}
4552 
4553 	if (longitude_is_null) {
4554 		longitude = INI_FLT("date.default_longitude");
4555 	}
4556 
4557 	if (zenith_is_null) {
4558 		if (calc_sunset) {
4559 			zenith = INI_FLT("date.sunset_zenith");
4560 		} else {
4561 			zenith = INI_FLT("date.sunrise_zenith");
4562 		}
4563 	}
4564 
4565 	if (retformat != SUNFUNCS_RET_TIMESTAMP &&
4566 		retformat != SUNFUNCS_RET_STRING &&
4567 		retformat != SUNFUNCS_RET_DOUBLE)
4568 	{
4569 		zend_argument_value_error(2, "must be one of SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING, or SUNFUNCS_RET_DOUBLE");
4570 		RETURN_THROWS();
4571 	}
4572 	altitude = 90 - zenith;
4573 
4574 	/* Initialize time struct */
4575 	tzi = get_timezone_info();
4576 	if (!tzi) {
4577 		RETURN_THROWS();
4578 	}
4579 	t = timelib_time_ctor();
4580 	t->tz_info = tzi;
4581 	t->zone_type = TIMELIB_ZONETYPE_ID;
4582 
4583 	if (gmt_offset_is_null) {
4584 		gmt_offset = timelib_get_current_offset(t) / 3600;
4585 	}
4586 
4587 	timelib_unixtime2local(t, time);
4588 	rs = timelib_astro_rise_set_altitude(t, longitude, latitude, altitude, 1, &h_rise, &h_set, &rise, &set, &transit);
4589 	timelib_time_dtor(t);
4590 
4591 	if (rs != 0) {
4592 		RETURN_FALSE;
4593 	}
4594 
4595 	if (retformat == SUNFUNCS_RET_TIMESTAMP) {
4596 		RETURN_LONG(calc_sunset ? set : rise);
4597 	}
4598 	N = (calc_sunset ? h_set : h_rise) + gmt_offset;
4599 
4600 	if (N > 24 || N < 0) {
4601 		N -= floor(N / 24) * 24;
4602 	}
4603 
4604 	switch (retformat) {
4605 		case SUNFUNCS_RET_STRING:
4606 			retstr = strpprintf(0, "%02d:%02d", (int) N, (int) (60 * (N - (int) N)));
4607 			RETURN_NEW_STR(retstr);
4608 			break;
4609 		case SUNFUNCS_RET_DOUBLE:
4610 			RETURN_DOUBLE(N);
4611 			break;
4612 	}
4613 }
4614 /* }}} */
4615 
4616 /* {{{ Returns time of sunrise for a given day and location */
PHP_FUNCTION(date_sunrise)4617 PHP_FUNCTION(date_sunrise)
4618 {
4619 	php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4620 }
4621 /* }}} */
4622 
4623 /* {{{ Returns time of sunset for a given day and location */
PHP_FUNCTION(date_sunset)4624 PHP_FUNCTION(date_sunset)
4625 {
4626 	php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4627 }
4628 /* }}} */
4629 
4630 /* {{{ Returns an array with information about sun set/rise and twilight begin/end */
PHP_FUNCTION(date_sun_info)4631 PHP_FUNCTION(date_sun_info)
4632 {
4633 	zend_long       time;
4634 	double          latitude, longitude;
4635 	timelib_time   *t, *t2;
4636 	timelib_tzinfo *tzi;
4637 	int             rs;
4638 	timelib_sll     rise, set, transit;
4639 	int             dummy;
4640 	double          ddummy;
4641 
4642 	ZEND_PARSE_PARAMETERS_START(3, 3)
4643 		Z_PARAM_LONG(time)
4644 		Z_PARAM_DOUBLE(latitude)
4645 		Z_PARAM_DOUBLE(longitude)
4646 	ZEND_PARSE_PARAMETERS_END();
4647 
4648 	/* Initialize time struct */
4649 	tzi = get_timezone_info();
4650 	if (!tzi) {
4651 		RETURN_THROWS();
4652 	}
4653 	t = timelib_time_ctor();
4654 	t->tz_info = tzi;
4655 	t->zone_type = TIMELIB_ZONETYPE_ID;
4656 	timelib_unixtime2local(t, time);
4657 
4658 	/* Setup */
4659 	t2 = timelib_time_ctor();
4660 	array_init(return_value);
4661 
4662 	/* Get sun up/down and transit */
4663 	rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -50.0/60, 1, &ddummy, &ddummy, &rise, &set, &transit);
4664 	switch (rs) {
4665 		case -1: /* always below */
4666 			add_assoc_bool(return_value, "sunrise", 0);
4667 			add_assoc_bool(return_value, "sunset", 0);
4668 			break;
4669 		case 1: /* always above */
4670 			add_assoc_bool(return_value, "sunrise", 1);
4671 			add_assoc_bool(return_value, "sunset", 1);
4672 			break;
4673 		default:
4674 			t2->sse = rise;
4675 			add_assoc_long(return_value, "sunrise", timelib_date_to_int(t2, &dummy));
4676 			t2->sse = set;
4677 			add_assoc_long(return_value, "sunset", timelib_date_to_int(t2, &dummy));
4678 	}
4679 	t2->sse = transit;
4680 	add_assoc_long(return_value, "transit", timelib_date_to_int(t2, &dummy));
4681 
4682 	/* Get civil twilight */
4683 	rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -6.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4684 	switch (rs) {
4685 		case -1: /* always below */
4686 			add_assoc_bool(return_value, "civil_twilight_begin", 0);
4687 			add_assoc_bool(return_value, "civil_twilight_end", 0);
4688 			break;
4689 		case 1: /* always above */
4690 			add_assoc_bool(return_value, "civil_twilight_begin", 1);
4691 			add_assoc_bool(return_value, "civil_twilight_end", 1);
4692 			break;
4693 		default:
4694 			t2->sse = rise;
4695 			add_assoc_long(return_value, "civil_twilight_begin", timelib_date_to_int(t2, &dummy));
4696 			t2->sse = set;
4697 			add_assoc_long(return_value, "civil_twilight_end", timelib_date_to_int(t2, &dummy));
4698 	}
4699 
4700 	/* Get nautical twilight */
4701 	rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -12.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4702 	switch (rs) {
4703 		case -1: /* always below */
4704 			add_assoc_bool(return_value, "nautical_twilight_begin", 0);
4705 			add_assoc_bool(return_value, "nautical_twilight_end", 0);
4706 			break;
4707 		case 1: /* always above */
4708 			add_assoc_bool(return_value, "nautical_twilight_begin", 1);
4709 			add_assoc_bool(return_value, "nautical_twilight_end", 1);
4710 			break;
4711 		default:
4712 			t2->sse = rise;
4713 			add_assoc_long(return_value, "nautical_twilight_begin", timelib_date_to_int(t2, &dummy));
4714 			t2->sse = set;
4715 			add_assoc_long(return_value, "nautical_twilight_end", timelib_date_to_int(t2, &dummy));
4716 	}
4717 
4718 	/* Get astronomical twilight */
4719 	rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -18.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4720 	switch (rs) {
4721 		case -1: /* always below */
4722 			add_assoc_bool(return_value, "astronomical_twilight_begin", 0);
4723 			add_assoc_bool(return_value, "astronomical_twilight_end", 0);
4724 			break;
4725 		case 1: /* always above */
4726 			add_assoc_bool(return_value, "astronomical_twilight_begin", 1);
4727 			add_assoc_bool(return_value, "astronomical_twilight_end", 1);
4728 			break;
4729 		default:
4730 			t2->sse = rise;
4731 			add_assoc_long(return_value, "astronomical_twilight_begin", timelib_date_to_int(t2, &dummy));
4732 			t2->sse = set;
4733 			add_assoc_long(return_value, "astronomical_twilight_end", timelib_date_to_int(t2, &dummy));
4734 	}
4735 	timelib_time_dtor(t);
4736 	timelib_time_dtor(t2);
4737 }
4738 /* }}} */
4739 
date_object_get_gc_period(zend_object * object,zval ** table,int * n)4740 static HashTable *date_object_get_gc_period(zend_object *object, zval **table, int *n) /* {{{ */
4741 {
4742 	*table = NULL;
4743 	*n = 0;
4744 	return zend_std_get_properties(object);
4745 } /* }}} */
4746 
date_object_get_properties_period(zend_object * object)4747 static HashTable *date_object_get_properties_period(zend_object *object) /* {{{ */
4748 {
4749 	HashTable		*props;
4750 	zval			 zv;
4751 	php_period_obj	*period_obj;
4752 
4753 	period_obj = php_period_obj_from_obj(object);
4754 	props = zend_std_get_properties(object);
4755 	if (!period_obj->start) {
4756 		return props;
4757 	}
4758 
4759 	if (period_obj->start) {
4760 		php_date_obj *date_obj;
4761 		object_init_ex(&zv, period_obj->start_ce);
4762 		date_obj = Z_PHPDATE_P(&zv);
4763 		date_obj->time = timelib_time_clone(period_obj->start);
4764 	} else {
4765 		ZVAL_NULL(&zv);
4766 	}
4767 	zend_hash_str_update(props, "start", sizeof("start")-1, &zv);
4768 
4769 	if (period_obj->current) {
4770 		php_date_obj *date_obj;
4771 		object_init_ex(&zv, period_obj->start_ce);
4772 		date_obj = Z_PHPDATE_P(&zv);
4773 		date_obj->time = timelib_time_clone(period_obj->current);
4774 	} else {
4775 		ZVAL_NULL(&zv);
4776 	}
4777 	zend_hash_str_update(props, "current", sizeof("current")-1, &zv);
4778 
4779 	if (period_obj->end) {
4780 		php_date_obj *date_obj;
4781 		object_init_ex(&zv, period_obj->start_ce);
4782 		date_obj = Z_PHPDATE_P(&zv);
4783 		date_obj->time = timelib_time_clone(period_obj->end);
4784 	} else {
4785 		ZVAL_NULL(&zv);
4786 	}
4787 	zend_hash_str_update(props, "end", sizeof("end")-1, &zv);
4788 
4789 	if (period_obj->interval) {
4790 		php_interval_obj *interval_obj;
4791 		object_init_ex(&zv, date_ce_interval);
4792 		interval_obj = Z_PHPINTERVAL_P(&zv);
4793 		interval_obj->diff = timelib_rel_time_clone(period_obj->interval);
4794 		interval_obj->initialized = 1;
4795 	} else {
4796 		ZVAL_NULL(&zv);
4797 	}
4798 	zend_hash_str_update(props, "interval", sizeof("interval")-1, &zv);
4799 
4800 	/* converted to larger type (int->long); must check when unserializing */
4801 	ZVAL_LONG(&zv, (zend_long) period_obj->recurrences);
4802 	zend_hash_str_update(props, "recurrences", sizeof("recurrences")-1, &zv);
4803 
4804 	ZVAL_BOOL(&zv, period_obj->include_start_date);
4805 	zend_hash_str_update(props, "include_start_date", sizeof("include_start_date")-1, &zv);
4806 
4807 	return props;
4808 } /* }}} */
4809 
php_date_period_initialize_from_hash(php_period_obj * period_obj,HashTable * myht)4810 static int php_date_period_initialize_from_hash(php_period_obj *period_obj, HashTable *myht) /* {{{ */
4811 {
4812 	zval *ht_entry;
4813 
4814 	/* this function does no rollback on error */
4815 
4816 	ht_entry = zend_hash_str_find(myht, "start", sizeof("start")-1);
4817 	if (ht_entry) {
4818 		if (Z_TYPE_P(ht_entry) == IS_OBJECT && instanceof_function(Z_OBJCE_P(ht_entry), date_ce_interface)) {
4819 			php_date_obj *date_obj;
4820 			date_obj = Z_PHPDATE_P(ht_entry);
4821 			period_obj->start = timelib_time_clone(date_obj->time);
4822 			period_obj->start_ce = Z_OBJCE_P(ht_entry);
4823 		} else if (Z_TYPE_P(ht_entry) != IS_NULL) {
4824 			return 0;
4825 		}
4826 	} else {
4827 		return 0;
4828 	}
4829 
4830 	ht_entry = zend_hash_str_find(myht, "end", sizeof("end")-1);
4831 	if (ht_entry) {
4832 		if (Z_TYPE_P(ht_entry) == IS_OBJECT && instanceof_function(Z_OBJCE_P(ht_entry), date_ce_interface)) {
4833 			php_date_obj *date_obj;
4834 			date_obj = Z_PHPDATE_P(ht_entry);
4835 			period_obj->end = timelib_time_clone(date_obj->time);
4836 		} else if (Z_TYPE_P(ht_entry) != IS_NULL) {
4837 			return 0;
4838 		}
4839 	} else {
4840 		return 0;
4841 	}
4842 
4843 	ht_entry = zend_hash_str_find(myht, "current", sizeof("current")-1);
4844 	if (ht_entry) {
4845 		if (Z_TYPE_P(ht_entry) == IS_OBJECT && instanceof_function(Z_OBJCE_P(ht_entry), date_ce_interface)) {
4846 			php_date_obj *date_obj;
4847 			date_obj = Z_PHPDATE_P(ht_entry);
4848 			period_obj->current = timelib_time_clone(date_obj->time);
4849 		} else if (Z_TYPE_P(ht_entry) != IS_NULL)  {
4850 			return 0;
4851 		}
4852 	} else {
4853 		return 0;
4854 	}
4855 
4856 	ht_entry = zend_hash_str_find(myht, "interval", sizeof("interval")-1);
4857 	if (ht_entry) {
4858 		if (Z_TYPE_P(ht_entry) == IS_OBJECT && Z_OBJCE_P(ht_entry) == date_ce_interval) {
4859 			php_interval_obj *interval_obj;
4860 			interval_obj = Z_PHPINTERVAL_P(ht_entry);
4861 			period_obj->interval = timelib_rel_time_clone(interval_obj->diff);
4862 		} else { /* interval is required */
4863 			return 0;
4864 		}
4865 	} else {
4866 		return 0;
4867 	}
4868 
4869 	ht_entry = zend_hash_str_find(myht, "recurrences", sizeof("recurrences")-1);
4870 	if (ht_entry &&
4871 			Z_TYPE_P(ht_entry) == IS_LONG && Z_LVAL_P(ht_entry) >= 0 && Z_LVAL_P(ht_entry) <= INT_MAX) {
4872 		period_obj->recurrences = Z_LVAL_P(ht_entry);
4873 	} else {
4874 		return 0;
4875 	}
4876 
4877 	ht_entry = zend_hash_str_find(myht, "include_start_date", sizeof("include_start_date")-1);
4878 	if (ht_entry &&
4879 			(Z_TYPE_P(ht_entry) == IS_FALSE || Z_TYPE_P(ht_entry) == IS_TRUE)) {
4880 		period_obj->include_start_date = (Z_TYPE_P(ht_entry) == IS_TRUE);
4881 	} else {
4882 		return 0;
4883 	}
4884 
4885 	period_obj->initialized = 1;
4886 
4887 	return 1;
4888 } /* }}} */
4889 
4890 /* {{{ */
PHP_METHOD(DatePeriod,__set_state)4891 PHP_METHOD(DatePeriod, __set_state)
4892 {
4893 	php_period_obj   *period_obj;
4894 	zval             *array;
4895 	HashTable        *myht;
4896 
4897 	ZEND_PARSE_PARAMETERS_START(1, 1)
4898 		Z_PARAM_ARRAY(array)
4899 	ZEND_PARSE_PARAMETERS_END();
4900 
4901 	myht = Z_ARRVAL_P(array);
4902 
4903 	object_init_ex(return_value, date_ce_period);
4904 	period_obj = Z_PHPPERIOD_P(return_value);
4905 	if (!php_date_period_initialize_from_hash(period_obj, myht)) {
4906 		zend_throw_error(NULL, "Invalid serialization data for DatePeriod object");
4907 	}
4908 }
4909 /* }}} */
4910 
4911 /* {{{ */
PHP_METHOD(DatePeriod,__wakeup)4912 PHP_METHOD(DatePeriod, __wakeup)
4913 {
4914 	zval             *object = ZEND_THIS;
4915 	php_period_obj   *period_obj;
4916 	HashTable        *myht;
4917 
4918 	ZEND_PARSE_PARAMETERS_NONE();
4919 
4920 	period_obj = Z_PHPPERIOD_P(object);
4921 
4922 	myht = Z_OBJPROP_P(object);
4923 
4924 	if (!php_date_period_initialize_from_hash(period_obj, myht)) {
4925 		zend_throw_error(NULL, "Invalid serialization data for DatePeriod object");
4926 	}
4927 }
4928 /* }}} */
4929 
4930 /* {{{ date_period_is_magic_property
4931  *  Common for date_period_read_property() and date_period_write_property() functions
4932  */
date_period_is_magic_property(zend_string * name)4933 static int date_period_is_magic_property(zend_string *name)
4934 {
4935 	if (zend_string_equals_literal(name, "recurrences")
4936 		|| zend_string_equals_literal(name, "include_start_date")
4937 		|| zend_string_equals_literal(name, "start")
4938 		|| zend_string_equals_literal(name, "current")
4939 		|| zend_string_equals_literal(name, "end")
4940 		|| zend_string_equals_literal(name, "interval")
4941 	) {
4942 		return 1;
4943 	}
4944 	return 0;
4945 }
4946 /* }}} */
4947 
4948 /* {{{ date_period_read_property */
date_period_read_property(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)4949 static zval *date_period_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
4950 {
4951 	if (type != BP_VAR_IS && type != BP_VAR_R) {
4952 		if (date_period_is_magic_property(name)) {
4953 			zend_throw_error(NULL, "Retrieval of DatePeriod->%s for modification is unsupported", ZSTR_VAL(name));
4954 			return &EG(uninitialized_zval);
4955 		}
4956 	}
4957 
4958 	object->handlers->get_properties(object); /* build properties hash table */
4959 
4960 	return zend_std_read_property(object, name, type, cache_slot, rv);
4961 }
4962 /* }}} */
4963 
4964 /* {{{ date_period_write_property */
date_period_write_property(zend_object * object,zend_string * name,zval * value,void ** cache_slot)4965 static zval *date_period_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
4966 {
4967 	if (date_period_is_magic_property(name)) {
4968 		zend_throw_error(NULL, "Writing to DatePeriod->%s is unsupported", ZSTR_VAL(name));
4969 		return value;
4970 	}
4971 
4972 	return zend_std_write_property(object, name, value, cache_slot);
4973 }
4974 /* }}} */
4975 
4976 /* {{{ date_period_get_property_ptr_ptr */
date_period_get_property_ptr_ptr(zend_object * object,zend_string * name,int type,void ** cache_slot)4977 static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
4978 {
4979 	if (date_period_is_magic_property(name)) {
4980 		zend_throw_error(NULL, "Retrieval of DatePeriod->%s for modification is unsupported", ZSTR_VAL(name));
4981 		return &EG(error_zval);
4982 	}
4983 
4984 	return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
4985 }
4986 /* }}} */
4987