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