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