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