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