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