1 /*
2    +----------------------------------------------------------------------+
3    | This source file is subject to version 3.01 of the PHP license,      |
4    | that is bundled with this package in the file LICENSE, and is        |
5    | available through the world-wide-web at the following url:           |
6    | https://www.php.net/license/3_01.txt                                 |
7    | If you did not receive a copy of the PHP license and are unable to   |
8    | obtain it through the world-wide-web, please send a note to          |
9    | license@php.net so we can mail you a copy immediately.               |
10    +----------------------------------------------------------------------+
11    | Authors: Gustavo Lopes <cataphract@php.net>                          |
12    +----------------------------------------------------------------------+
13 */
14 
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18 
19 #include <inttypes.h>
20 
21 #include "../intl_cppshims.h"
22 
23 #include <unicode/locid.h>
24 #include <unicode/calendar.h>
25 #include <unicode/ustring.h>
26 
27 #include "../intl_convertcpp.h"
28 #include "../common/common_date.h"
29 
30 extern "C" {
31 #include "../php_intl.h"
32 #define USE_TIMEZONE_POINTER 1
33 #include "../timezone/timezone_class.h"
34 #define USE_CALENDAR_POINTER 1
35 #include "calendar_class.h"
36 #include "../intl_convert.h"
37 #include <zend_exceptions.h>
38 #include <zend_interfaces.h>
39 #include <ext/date/php_date.h>
40 }
41 #include "../common/common_enum.h"
42 
43 using icu::Locale;
44 
45 #define ZEND_VALUE_ERROR_INVALID_FIELD(argument, zpp_arg_position) \
46 	if (argument < 0 || argument >= UCAL_FIELD_COUNT) { \
47 		zend_argument_value_error(hasThis() ? ((zpp_arg_position)-1) : zpp_arg_position, \
48 			"must be a valid field"); \
49 		RETURN_THROWS(); \
50 	}
51 
52 #define ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(argument, zpp_arg_position) \
53 	if (UNEXPECTED(argument < INT32_MIN || argument > INT32_MAX)) { \
54 		zend_argument_value_error(hasThis() ? ((zpp_arg_position)-1) : zpp_arg_position, \
55 			"must be between %d and %d", INT32_MIN, INT32_MAX); \
56 		RETURN_THROWS(); \
57 	}
58 
59 #define ZEND_VALUE_ERROR_INVALID_DAY_OF_WEEK(argument, zpp_arg_position) \
60 	if (argument < UCAL_SUNDAY || argument > UCAL_SATURDAY) { \
61 		zend_argument_value_error(hasThis() ? ((zpp_arg_position)-1) : zpp_arg_position, \
62 			"must be a valid day of the week"); \
63 		RETURN_THROWS(); \
64 	}
65 
PHP_METHOD(IntlCalendar,__construct)66 U_CFUNC PHP_METHOD(IntlCalendar, __construct)
67 {
68 	zend_throw_exception( NULL,
69 		"An object of this type cannot be created with the new operator",
70 		0 );
71 }
72 
PHP_FUNCTION(intlcal_create_instance)73 U_CFUNC PHP_FUNCTION(intlcal_create_instance)
74 {
75 	zval		*zv_timezone	= NULL;
76 	char	        *locale_str	= NULL;
77 	size_t		locale_len      = 0;
78 	TimeZone	*timeZone;
79 	UErrorCode	status			= U_ZERO_ERROR;
80 	intl_error_reset(NULL);
81 
82 	ZEND_PARSE_PARAMETERS_START(0, 2)
83 		Z_PARAM_OPTIONAL
84 		Z_PARAM_ZVAL(zv_timezone)
85 		Z_PARAM_STRING_OR_NULL(locale_str, locale_len)
86 	ZEND_PARSE_PARAMETERS_END();
87 
88 	timeZone = timezone_process_timezone_argument(zv_timezone, NULL,
89 		"intlcal_create_instance");
90 	if (timeZone == NULL) {
91 		RETURN_NULL();
92 	}
93 
94 	if (!locale_str) {
95 		locale_str = (char *)intl_locale_get_default();
96 	}
97 
98 	Calendar *cal = Calendar::createInstance(timeZone,
99 		Locale::createFromName(locale_str), status);
100 	if (UNEXPECTED(cal == NULL)) {
101 		delete timeZone;
102 		intl_error_set(NULL, status, "Error creating ICU Calendar object", 0);
103 		RETURN_NULL();
104 	}
105 
106 	calendar_object_create(return_value, cal);
107 }
108 
109 class BugStringCharEnumeration : public StringEnumeration
110 {
111 public:
BugStringCharEnumeration(UEnumeration * _uenum)112 	explicit BugStringCharEnumeration(UEnumeration* _uenum) : uenum(_uenum) {}
113 
~BugStringCharEnumeration()114 	~BugStringCharEnumeration()
115 	{
116 		uenum_close(uenum);
117 	}
118 
count(UErrorCode & status) const119 	int32_t count(UErrorCode& status) const override {
120 		return uenum_count(uenum, &status);
121 	}
122 
snext(UErrorCode & status)123 	const UnicodeString* snext(UErrorCode& status) override
124 	{
125 		int32_t length;
126 		const UChar* str = uenum_unext(uenum, &length, &status);
127 		if (str == 0 || U_FAILURE(status)) {
128 			return 0;
129 		}
130 		return &unistr.setTo(str, length);
131 	}
132 
next(int32_t * resultLength,UErrorCode & status)133 	const char* next(int32_t *resultLength, UErrorCode &status) override
134 	{
135 		int32_t length = -1;
136 		const char* str = uenum_next(uenum, &length, &status);
137 		if (str == 0 || U_FAILURE(status)) {
138 			return 0;
139 		}
140 		if (resultLength) {
141 			//the bug is that uenum_next doesn't set the length
142 			*resultLength = (length == -1) ? (int32_t)strlen(str) : length;
143 		}
144 
145 		return str;
146 	}
147 
reset(UErrorCode & status)148 	void reset(UErrorCode& status) override
149 	{
150 		uenum_reset(uenum, &status);
151 	}
152 
153 	UClassID getDynamicClassID() const override;
154 
155 	static UClassID U_EXPORT2 getStaticClassID();
156 
157 private:
158 	UEnumeration *uenum;
159 };
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(BugStringCharEnumeration)160 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(BugStringCharEnumeration)
161 
162 U_CFUNC PHP_FUNCTION(intlcal_get_keyword_values_for_locale)
163 {
164 	UErrorCode	status = U_ZERO_ERROR;
165 	char		*key,
166 				*locale;
167 	size_t			key_len,
168 				locale_len;
169 	bool	commonly_used;
170 	intl_error_reset(NULL);
171 
172 	ZEND_PARSE_PARAMETERS_START(3, 3)
173 		Z_PARAM_STRING(key, key_len)
174 		Z_PARAM_STRING(locale, locale_len)
175 		Z_PARAM_BOOL(commonly_used)
176 	ZEND_PARSE_PARAMETERS_END();
177 
178 	StringEnumeration *se = Calendar::getKeywordValuesForLocale(key,
179 		Locale::createFromName(locale), (UBool)commonly_used,
180 		status);
181 	if (se == NULL) {
182 		intl_error_set(NULL, status, "intlcal_get_keyword_values_for_locale: "
183 			"error calling underlying method", 0);
184 		RETURN_FALSE;
185 	}
186 
187 	IntlIterator_from_StringEnumeration(se, return_value);
188 }
189 
PHP_FUNCTION(intlcal_get_now)190 U_CFUNC PHP_FUNCTION(intlcal_get_now)
191 {
192 	intl_error_reset(NULL);
193 
194 	ZEND_PARSE_PARAMETERS_NONE();
195 
196 	RETURN_DOUBLE((double)Calendar::getNow());
197 }
198 
PHP_FUNCTION(intlcal_get_available_locales)199 U_CFUNC PHP_FUNCTION(intlcal_get_available_locales)
200 {
201 	intl_error_reset(NULL);
202 
203 	ZEND_PARSE_PARAMETERS_NONE();
204 
205 	int32_t count;
206 	const Locale *availLocales = Calendar::getAvailableLocales(count);
207 	array_init(return_value);
208 	for (int i = 0; i < count; i++) {
209 		Locale locale = availLocales[i];
210 		add_next_index_string(return_value, locale.getName());
211 	}
212 }
213 
_php_intlcal_field_uec_ret_in32t_method(int32_t (Calendar::* func)(UCalendarDateFields,UErrorCode &)const,INTERNAL_FUNCTION_PARAMETERS)214 static void _php_intlcal_field_uec_ret_in32t_method(
215 		int32_t (Calendar::*func)(UCalendarDateFields, UErrorCode&) const,
216 		INTERNAL_FUNCTION_PARAMETERS)
217 {
218 	zend_long	field;
219 	CALENDAR_METHOD_INIT_VARS;
220 
221 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
222 			"Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
223 		RETURN_THROWS();
224 	}
225 
226 	ZEND_VALUE_ERROR_INVALID_FIELD(field, 2);
227 
228 	CALENDAR_METHOD_FETCH_OBJECT;
229 
230 	int32_t result = (co->ucal->*func)(
231 		(UCalendarDateFields)field, CALENDAR_ERROR_CODE(co));
232 	INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
233 
234 	RETURN_LONG((zend_long)result);
235 }
236 
PHP_FUNCTION(intlcal_get)237 U_CFUNC PHP_FUNCTION(intlcal_get)
238 {
239 	_php_intlcal_field_uec_ret_in32t_method(&Calendar::get,
240 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
241 }
242 
PHP_FUNCTION(intlcal_get_time)243 U_CFUNC PHP_FUNCTION(intlcal_get_time)
244 {
245 	CALENDAR_METHOD_INIT_VARS;
246 
247 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
248 			&object, Calendar_ce_ptr) == FAILURE) {
249 		RETURN_THROWS();
250 	}
251 
252 	CALENDAR_METHOD_FETCH_OBJECT;
253 
254 	UDate result = co->ucal->getTime(CALENDAR_ERROR_CODE(co));
255 	INTL_METHOD_CHECK_STATUS(co,
256 		"intlcal_get_time: error calling ICU Calendar::getTime");
257 
258 	RETURN_DOUBLE((double)result);
259 }
260 
PHP_FUNCTION(intlcal_set_time)261 U_CFUNC PHP_FUNCTION(intlcal_set_time)
262 {
263 	double	time_arg;
264 	CALENDAR_METHOD_INIT_VARS;
265 
266 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Od",
267 			&object, Calendar_ce_ptr, &time_arg) == FAILURE) {
268 		RETURN_THROWS();
269 	}
270 
271 	CALENDAR_METHOD_FETCH_OBJECT;
272 
273 	co->ucal->setTime((UDate)time_arg, CALENDAR_ERROR_CODE(co));
274 	INTL_METHOD_CHECK_STATUS(co, "Call to underlying method failed");
275 
276 	RETURN_TRUE;
277 }
278 
PHP_FUNCTION(intlcal_add)279 U_CFUNC PHP_FUNCTION(intlcal_add)
280 {
281 	zend_long	field,
282 			amount;
283 	CALENDAR_METHOD_INIT_VARS;
284 
285 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
286 			"Oll", &object, Calendar_ce_ptr, &field, &amount) == FAILURE) {
287 		RETURN_THROWS();
288 	}
289 
290 	ZEND_VALUE_ERROR_INVALID_FIELD(field, 2);
291 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(amount, 3);
292 
293 	CALENDAR_METHOD_FETCH_OBJECT;
294 
295 	co->ucal->add((UCalendarDateFields)field, (int32_t)amount, CALENDAR_ERROR_CODE(co));
296 	INTL_METHOD_CHECK_STATUS(co, "intlcal_add: Call to underlying method failed");
297 
298 	RETURN_TRUE;
299 }
300 
PHP_FUNCTION(intlcal_set_time_zone)301 U_CFUNC PHP_FUNCTION(intlcal_set_time_zone)
302 {
303 	zval			*zv_timezone;
304 	TimeZone		*timeZone;
305 	CALENDAR_METHOD_INIT_VARS;
306 
307 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
308 			"Oz!", &object, Calendar_ce_ptr, &zv_timezone) == FAILURE) {
309 		RETURN_THROWS();
310 	}
311 
312 	CALENDAR_METHOD_FETCH_OBJECT;
313 
314 	if (zv_timezone == NULL) {
315 		RETURN_TRUE; /* the method does nothing if passed null */
316 	}
317 
318 	timeZone = timezone_process_timezone_argument(zv_timezone,
319 			CALENDAR_ERROR_P(co), "intlcal_set_time_zone");
320 	if (timeZone == NULL) {
321 		RETURN_FALSE;
322 	}
323 
324 	co->ucal->adoptTimeZone(timeZone);
325 
326 	RETURN_TRUE;
327 }
328 
329 
_php_intlcal_before_after(UBool (Calendar::* func)(const Calendar &,UErrorCode &)const,INTERNAL_FUNCTION_PARAMETERS)330 static void _php_intlcal_before_after(
331 		UBool (Calendar::*func)(const Calendar&, UErrorCode&) const,
332 		INTERNAL_FUNCTION_PARAMETERS)
333 {
334 	zval			*when_object;
335 	Calendar_object	*when_co;
336 	CALENDAR_METHOD_INIT_VARS;
337 
338 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
339 			"OO", &object, Calendar_ce_ptr, &when_object, Calendar_ce_ptr)
340 			== FAILURE) {
341 		RETURN_THROWS();
342 	}
343 
344 	CALENDAR_METHOD_FETCH_OBJECT;
345 
346 	when_co = Z_INTL_CALENDAR_P(when_object);
347 	if (when_co->ucal == NULL) {
348 		zend_argument_error(NULL, 2, "is uninitialized");
349 		RETURN_THROWS();
350 	}
351 
352 	UBool res = (co->ucal->*func)(*when_co->ucal, CALENDAR_ERROR_CODE(co));
353 	INTL_METHOD_CHECK_STATUS(co, "intlcal_before/after: Error calling ICU method");
354 
355 	RETURN_BOOL((int)res);
356 }
357 
PHP_FUNCTION(intlcal_after)358 U_CFUNC PHP_FUNCTION(intlcal_after)
359 {
360 	_php_intlcal_before_after(&Calendar::after, INTERNAL_FUNCTION_PARAM_PASSTHRU);
361 }
362 
PHP_FUNCTION(intlcal_before)363 U_CFUNC PHP_FUNCTION(intlcal_before)
364 {
365 	_php_intlcal_before_after(&Calendar::before, INTERNAL_FUNCTION_PARAM_PASSTHRU);
366 }
367 
PHP_FUNCTION(intlcal_set)368 U_CFUNC PHP_FUNCTION(intlcal_set)
369 {
370 	zend_long args[6];
371 
372 	CALENDAR_METHOD_INIT_VARS;
373 
374 	object = getThis();
375 
376 	int arg_num = ZEND_NUM_ARGS() - (object ? 0 : 1);
377 
378 	if (object && arg_num > 2) {
379 		zend_error(E_DEPRECATED, "Calling IntlCalendar::set() with more than 2 arguments is deprecated, "
380 			"use either IntlCalendar::setDate() or IntlCalendar::setDateTime() instead");
381 		if (UNEXPECTED(EG(exception))) {
382 			RETURN_THROWS();
383 		}
384 	}
385 
386 	if (zend_parse_method_parameters(
387 		ZEND_NUM_ARGS(), object, "Oll|llll",
388 		&object, Calendar_ce_ptr, &args[0], &args[1], &args[2], &args[3], &args[4], &args[5]
389 	) == FAILURE) {
390 		RETURN_THROWS();
391 	}
392 
393 	for (int i = 0; i < arg_num; i++) {
394 		/* Arguments start at 1 */
395 		ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(args[i], i + 1);
396 	}
397 
398 	CALENDAR_METHOD_FETCH_OBJECT;
399 
400 	if (arg_num == 2) {
401 		ZEND_VALUE_ERROR_INVALID_FIELD(args[0], 2);
402 		co->ucal->set((UCalendarDateFields)args[0], (int32_t)args[1]);
403 	} else if (arg_num == 3) {
404 		co->ucal->set((int32_t)args[0], (int32_t)args[1], (int32_t)args[2]);
405 	} else if (arg_num == 4) {
406 		zend_argument_count_error("IntlCalendar::set() has no variant with exactly 4 parameters");
407 		RETURN_THROWS();
408 	} else if (arg_num == 5) {
409 		co->ucal->set((int32_t)args[0], (int32_t)args[1], (int32_t)args[2], (int32_t)args[3], (int32_t)args[4]);
410 	} else {
411 		co->ucal->set((int32_t)args[0], (int32_t)args[1], (int32_t)args[2], (int32_t)args[3], (int32_t)args[4], (int32_t)args[5]);
412 	}
413 
414 	RETURN_TRUE;
415 }
416 
PHP_METHOD(IntlCalendar,setDate)417 U_CFUNC PHP_METHOD(IntlCalendar, setDate)
418 {
419 	zend_long year, month, day;
420 
421 	CALENDAR_METHOD_INIT_VARS;
422 
423 	object = getThis();
424 
425 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), object, "Olll",
426 		&object, Calendar_ce_ptr, &year, &month, &day) == FAILURE) {
427 		RETURN_THROWS();
428 	}
429 
430 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 1);
431 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 2);
432 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 3);
433 
434 	CALENDAR_METHOD_FETCH_OBJECT;
435 
436 	co->ucal->set((int32_t) year, (int32_t) month, (int32_t) day);
437 }
438 
PHP_METHOD(IntlCalendar,setDateTime)439 U_CFUNC PHP_METHOD(IntlCalendar, setDateTime)
440 {
441 	zend_long year, month, day, hour, minute, second = 0;
442 	bool second_is_null = true;
443 
444 	CALENDAR_METHOD_INIT_VARS;
445 
446 	object = getThis();
447 
448 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), object, "Olllll|l!",
449 		&object, Calendar_ce_ptr, &year, &month, &day, &hour, &minute, &second, &second_is_null) == FAILURE) {
450 		RETURN_THROWS();
451 	}
452 
453 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 1);
454 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 2);
455 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 3);
456 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(hour, 4);
457 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(minute, 5);
458 
459 	CALENDAR_METHOD_FETCH_OBJECT;
460 
461 	if (second_is_null) {
462 		co->ucal->set((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute);
463 	} else {
464 		ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(second, 6);
465 		co->ucal->set((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute, (int32_t) second);
466 	}
467 }
468 
PHP_FUNCTION(intlcal_roll)469 U_CFUNC PHP_FUNCTION(intlcal_roll)
470 {
471 	zval *zvalue;
472 	zend_long field, value;
473 	CALENDAR_METHOD_INIT_VARS;
474 
475 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Olz", &object, Calendar_ce_ptr, &field, &zvalue) == FAILURE) {
476 		RETURN_THROWS();
477 	}
478 
479 	CALENDAR_METHOD_FETCH_OBJECT;
480 
481 	ZEND_VALUE_ERROR_INVALID_FIELD(field, 2);
482 
483 	if (Z_TYPE_P(zvalue) == IS_FALSE || Z_TYPE_P(zvalue) == IS_TRUE) {
484 		value = Z_TYPE_P(zvalue) == IS_TRUE ? 1 : -1;
485 		php_error_docref(NULL, E_DEPRECATED, "Passing bool is deprecated, use 1 or -1 instead");
486 	} else {
487 		value = zval_get_long(zvalue);
488 		ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(value, 3);
489 	}
490 
491 	co->ucal->roll((UCalendarDateFields)field, (int32_t)value, CALENDAR_ERROR_CODE(co));
492 
493 	INTL_METHOD_CHECK_STATUS(co, "intlcal_roll: Error calling ICU Calendar::roll");
494 
495 	RETURN_TRUE;
496 }
497 
PHP_FUNCTION(intlcal_clear)498 U_CFUNC PHP_FUNCTION(intlcal_clear)
499 {
500 	zend_long field;
501 	bool field_is_null = 1;
502 	CALENDAR_METHOD_INIT_VARS;
503 
504 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(),
505 			getThis(), "O|l!", &object, Calendar_ce_ptr, &field, &field_is_null) == FAILURE) {
506 		RETURN_THROWS();
507 	}
508 
509 	CALENDAR_METHOD_FETCH_OBJECT;
510 
511 	if (field_is_null) {
512 		co->ucal->clear();
513 	} else {
514 		ZEND_VALUE_ERROR_INVALID_FIELD(field, 2);
515 
516 		co->ucal->clear((UCalendarDateFields)field);
517 	}
518 
519 	RETURN_TRUE;
520 }
521 
PHP_FUNCTION(intlcal_field_difference)522 U_CFUNC PHP_FUNCTION(intlcal_field_difference)
523 {
524 	zend_long	field;
525 	double	when;
526 	CALENDAR_METHOD_INIT_VARS;
527 
528 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
529 			"Odl", &object, Calendar_ce_ptr, &when, &field)	== FAILURE) {
530 		RETURN_THROWS();
531 	}
532 
533 	ZEND_VALUE_ERROR_INVALID_FIELD(field, 3);
534 
535 	CALENDAR_METHOD_FETCH_OBJECT;
536 
537 	int32_t result = co->ucal->fieldDifference((UDate)when,
538 		(UCalendarDateFields)field, CALENDAR_ERROR_CODE(co));
539 	INTL_METHOD_CHECK_STATUS(co,
540 		"intlcal_field_difference: Call to ICU method has failed");
541 
542 	RETURN_LONG((zend_long)result);
543 }
544 
PHP_FUNCTION(intlcal_get_actual_maximum)545 U_CFUNC PHP_FUNCTION(intlcal_get_actual_maximum)
546 {
547 	_php_intlcal_field_uec_ret_in32t_method(&Calendar::getActualMaximum,
548 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
549 }
550 
PHP_FUNCTION(intlcal_get_actual_minimum)551 U_CFUNC PHP_FUNCTION(intlcal_get_actual_minimum)
552 {
553 	_php_intlcal_field_uec_ret_in32t_method(&Calendar::getActualMinimum,
554 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
555 }
556 
PHP_FUNCTION(intlcal_get_day_of_week_type)557 U_CFUNC PHP_FUNCTION(intlcal_get_day_of_week_type)
558 {
559 	zend_long	dow;
560 	CALENDAR_METHOD_INIT_VARS;
561 
562 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
563 			"Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
564 		RETURN_THROWS();
565 	}
566 
567 	ZEND_VALUE_ERROR_INVALID_DAY_OF_WEEK(dow, 2);
568 
569 	CALENDAR_METHOD_FETCH_OBJECT;
570 
571 	int32_t result = co->ucal->getDayOfWeekType(
572 		(UCalendarDaysOfWeek)dow, CALENDAR_ERROR_CODE(co));
573 	INTL_METHOD_CHECK_STATUS(co,
574 		"intlcal_get_day_of_week_type: Call to ICU method has failed");
575 
576 	RETURN_LONG((zend_long)result);
577 }
578 
PHP_FUNCTION(intlcal_get_first_day_of_week)579 U_CFUNC PHP_FUNCTION(intlcal_get_first_day_of_week)
580 {
581 	CALENDAR_METHOD_INIT_VARS;
582 
583 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
584 			"O", &object, Calendar_ce_ptr) == FAILURE) {
585 		RETURN_THROWS();
586 	}
587 
588 	CALENDAR_METHOD_FETCH_OBJECT;
589 
590 	int32_t result = co->ucal->getFirstDayOfWeek(CALENDAR_ERROR_CODE(co));
591 	INTL_METHOD_CHECK_STATUS(co,
592 		"intlcal_get_first_day_of_week: Call to ICU method has failed");
593 
594 	RETURN_LONG((zend_long)result);
595 }
596 
_php_intlcal_field_ret_in32t_method(int32_t (Calendar::* func)(UCalendarDateFields)const,INTERNAL_FUNCTION_PARAMETERS)597 static void _php_intlcal_field_ret_in32t_method(
598 		int32_t (Calendar::*func)(UCalendarDateFields) const,
599 		INTERNAL_FUNCTION_PARAMETERS)
600 {
601 	zend_long	field;
602 	CALENDAR_METHOD_INIT_VARS;
603 
604 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
605 			"Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
606 		RETURN_THROWS();
607 	}
608 
609 	ZEND_VALUE_ERROR_INVALID_FIELD(field, 2);
610 
611 	CALENDAR_METHOD_FETCH_OBJECT;
612 
613 	int32_t result = (co->ucal->*func)((UCalendarDateFields)field);
614 	INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
615 
616 	RETURN_LONG((zend_long)result);
617 }
618 
PHP_FUNCTION(intlcal_get_greatest_minimum)619 U_CFUNC PHP_FUNCTION(intlcal_get_greatest_minimum)
620 {
621 	_php_intlcal_field_ret_in32t_method(&Calendar::getGreatestMinimum,
622 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
623 }
624 
PHP_FUNCTION(intlcal_get_least_maximum)625 U_CFUNC PHP_FUNCTION(intlcal_get_least_maximum)
626 {
627 	_php_intlcal_field_ret_in32t_method(&Calendar::getLeastMaximum,
628 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
629 }
630 
PHP_FUNCTION(intlcal_get_locale)631 U_CFUNC PHP_FUNCTION(intlcal_get_locale)
632 {
633 	zend_long	locale_type;
634 	CALENDAR_METHOD_INIT_VARS;
635 
636 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
637 			"Ol", &object, Calendar_ce_ptr, &locale_type) == FAILURE) {
638 		RETURN_THROWS();
639 	}
640 
641 	if (locale_type != ULOC_ACTUAL_LOCALE && locale_type != ULOC_VALID_LOCALE) {
642 		zend_argument_value_error(hasThis() ? 1 : 2, "must be either Locale::ACTUAL_LOCALE or Locale::VALID_LOCALE");
643 		RETURN_THROWS();
644 	}
645 
646 	CALENDAR_METHOD_FETCH_OBJECT;
647 
648 	Locale locale = co->ucal->getLocale((ULocDataLocaleType)locale_type,
649 		CALENDAR_ERROR_CODE(co));
650 	INTL_METHOD_CHECK_STATUS(co,
651 		"intlcal_get_locale: Call to ICU method has failed");
652 
653 	RETURN_STRING(locale.getName());
654 }
655 
PHP_FUNCTION(intlcal_get_maximum)656 U_CFUNC PHP_FUNCTION(intlcal_get_maximum)
657 {
658 	_php_intlcal_field_ret_in32t_method(&Calendar::getMaximum,
659 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
660 }
661 
PHP_FUNCTION(intlcal_get_minimal_days_in_first_week)662 U_CFUNC PHP_FUNCTION(intlcal_get_minimal_days_in_first_week)
663 {
664 	CALENDAR_METHOD_INIT_VARS;
665 
666 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
667 			"O", &object, Calendar_ce_ptr) == FAILURE) {
668 		RETURN_THROWS();
669 	}
670 
671 	CALENDAR_METHOD_FETCH_OBJECT;
672 
673 	uint8_t result = co->ucal->getMinimalDaysInFirstWeek();
674 	INTL_METHOD_CHECK_STATUS(co,
675 		"intlcal_get_first_day_of_week: Call to ICU method has failed"); /* TODO Is it really a failure? */
676 
677 	RETURN_LONG((zend_long)result);
678 }
679 
PHP_FUNCTION(intlcal_get_minimum)680 U_CFUNC PHP_FUNCTION(intlcal_get_minimum)
681 {
682 	_php_intlcal_field_ret_in32t_method(&Calendar::getMinimum,
683 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
684 }
685 
PHP_FUNCTION(intlcal_get_time_zone)686 U_CFUNC PHP_FUNCTION(intlcal_get_time_zone)
687 {
688 	CALENDAR_METHOD_INIT_VARS;
689 
690 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
691 			"O", &object, Calendar_ce_ptr) == FAILURE) {
692 		RETURN_THROWS();
693 	}
694 
695 	CALENDAR_METHOD_FETCH_OBJECT;
696 
697 	TimeZone *tz = co->ucal->getTimeZone().clone();
698 	if (UNEXPECTED(tz == NULL)) {
699 		intl_errors_set(CALENDAR_ERROR_P(co), U_MEMORY_ALLOCATION_ERROR,
700 			"intlcal_get_time_zone: could not clone TimeZone", 0);
701 		RETURN_FALSE;
702 	}
703 
704 	timezone_object_construct(tz, return_value, 1);
705 }
706 
PHP_FUNCTION(intlcal_get_type)707 U_CFUNC PHP_FUNCTION(intlcal_get_type)
708 {
709 	CALENDAR_METHOD_INIT_VARS;
710 
711 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
712 			"O", &object, Calendar_ce_ptr) == FAILURE) {
713 		RETURN_THROWS();
714 	}
715 
716 	CALENDAR_METHOD_FETCH_OBJECT;
717 
718 	RETURN_STRING(co->ucal->getType());
719 }
720 
PHP_FUNCTION(intlcal_get_weekend_transition)721 U_CFUNC PHP_FUNCTION(intlcal_get_weekend_transition)
722 {
723 	zend_long	dow;
724 	CALENDAR_METHOD_INIT_VARS;
725 
726 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
727 			"Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
728 		RETURN_THROWS();
729 	}
730 
731 	ZEND_VALUE_ERROR_INVALID_DAY_OF_WEEK(dow, 2);
732 
733 	CALENDAR_METHOD_FETCH_OBJECT;
734 
735 	int32_t res = co->ucal->getWeekendTransition((UCalendarDaysOfWeek)dow,
736 		CALENDAR_ERROR_CODE(co));
737 	INTL_METHOD_CHECK_STATUS(co, "intlcal_get_weekend_transition: "
738 		"Error calling ICU method");
739 
740 	RETURN_LONG((zend_long)res);
741 }
742 
PHP_FUNCTION(intlcal_in_daylight_time)743 U_CFUNC PHP_FUNCTION(intlcal_in_daylight_time)
744 {
745 	CALENDAR_METHOD_INIT_VARS;
746 
747 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
748 			"O", &object, Calendar_ce_ptr) == FAILURE) {
749 		RETURN_THROWS();
750 	}
751 
752 	CALENDAR_METHOD_FETCH_OBJECT;
753 
754 	UBool ret = co->ucal->inDaylightTime(CALENDAR_ERROR_CODE(co));
755 	INTL_METHOD_CHECK_STATUS(co, "intlcal_in_daylight_time: "
756 		"Error calling ICU method");
757 
758 	RETURN_BOOL((int)ret);
759 }
760 
PHP_FUNCTION(intlcal_is_equivalent_to)761 U_CFUNC PHP_FUNCTION(intlcal_is_equivalent_to)
762 {
763 	zval			*other_object;
764 	Calendar_object *other_co;
765 	CALENDAR_METHOD_INIT_VARS;
766 
767 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
768 			"OO", &object, Calendar_ce_ptr, &other_object, Calendar_ce_ptr)
769 			== FAILURE) {
770 		RETURN_THROWS();
771 	}
772 
773 	other_co = Z_INTL_CALENDAR_P(other_object);
774 	if (other_co->ucal == NULL) {
775 		zend_argument_error(NULL, 2, "is uninitialized");
776 		RETURN_THROWS();
777 	}
778 
779 	CALENDAR_METHOD_FETCH_OBJECT;
780 
781 	RETURN_BOOL((int)co->ucal->isEquivalentTo(*other_co->ucal));
782 }
783 
PHP_FUNCTION(intlcal_is_lenient)784 U_CFUNC PHP_FUNCTION(intlcal_is_lenient)
785 {
786 	CALENDAR_METHOD_INIT_VARS;
787 
788 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
789 			"O", &object, Calendar_ce_ptr) == FAILURE) {
790 		RETURN_THROWS();
791 	}
792 
793 	CALENDAR_METHOD_FETCH_OBJECT;
794 
795 	RETURN_BOOL((int)co->ucal->isLenient());
796 }
797 
PHP_FUNCTION(intlcal_is_set)798 U_CFUNC PHP_FUNCTION(intlcal_is_set)
799 {
800 	zend_long field;
801 	CALENDAR_METHOD_INIT_VARS;
802 
803 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
804 			"Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
805 		RETURN_THROWS();
806 	}
807 
808 	ZEND_VALUE_ERROR_INVALID_FIELD(field, 2);
809 
810 	CALENDAR_METHOD_FETCH_OBJECT;
811 
812 	RETURN_BOOL((int)co->ucal->isSet((UCalendarDateFields)field));
813 }
814 
PHP_FUNCTION(intlcal_is_weekend)815 U_CFUNC PHP_FUNCTION(intlcal_is_weekend)
816 {
817 	double date;
818 	bool date_is_null = 1;
819 	CALENDAR_METHOD_INIT_VARS;
820 
821 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
822 				"O|d!", &object, Calendar_ce_ptr, &date, &date_is_null) == FAILURE) {
823 		RETURN_THROWS();
824 	}
825 
826 	CALENDAR_METHOD_FETCH_OBJECT;
827 
828 	if (date_is_null) {
829 		RETURN_BOOL((int)co->ucal->isWeekend());
830 	} else {
831 		UBool ret = co->ucal->isWeekend((UDate)date, CALENDAR_ERROR_CODE(co));
832 		INTL_METHOD_CHECK_STATUS(co, "intlcal_is_weekend: "
833 			"Error calling ICU method");
834 		RETURN_BOOL((int)ret);
835 	}
836 }
837 
838 
PHP_FUNCTION(intlcal_set_first_day_of_week)839 U_CFUNC PHP_FUNCTION(intlcal_set_first_day_of_week)
840 {
841 	zend_long	dow;
842 	CALENDAR_METHOD_INIT_VARS;
843 
844 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
845 			"Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
846 		RETURN_THROWS();
847 	}
848 
849 	ZEND_VALUE_ERROR_INVALID_DAY_OF_WEEK(dow, 2);
850 
851 	CALENDAR_METHOD_FETCH_OBJECT;
852 
853 	co->ucal->setFirstDayOfWeek((UCalendarDaysOfWeek)dow);
854 
855 	RETURN_TRUE;
856 }
857 
PHP_FUNCTION(intlcal_set_lenient)858 U_CFUNC PHP_FUNCTION(intlcal_set_lenient)
859 {
860 	bool is_lenient;
861 	CALENDAR_METHOD_INIT_VARS;
862 
863 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
864 			"Ob", &object, Calendar_ce_ptr, &is_lenient) == FAILURE) {
865 		RETURN_THROWS();
866 	}
867 
868 	CALENDAR_METHOD_FETCH_OBJECT;
869 
870 	co->ucal->setLenient((UBool) is_lenient);
871 
872 	RETURN_TRUE;
873 }
874 
PHP_FUNCTION(intlcal_set_minimal_days_in_first_week)875 U_CFUNC PHP_FUNCTION(intlcal_set_minimal_days_in_first_week)
876 {
877 	zend_long	num_days;
878 	CALENDAR_METHOD_INIT_VARS;
879 
880 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
881 			"Ol", &object, Calendar_ce_ptr, &num_days) == FAILURE) {
882 		RETURN_THROWS();
883 	}
884 
885 	// Use ZEND_VALUE_ERROR_INVALID_DAY_OF_WEEK ?
886 	if (num_days < 1 || num_days > 7) {
887 		zend_argument_value_error(hasThis() ? 1 : 2, "must be between 1 and 7");
888 		RETURN_THROWS();
889 	}
890 
891 	CALENDAR_METHOD_FETCH_OBJECT;
892 
893 	co->ucal->setMinimalDaysInFirstWeek((uint8_t)num_days);
894 
895 	RETURN_TRUE;
896 }
897 
PHP_FUNCTION(intlcal_equals)898 U_CFUNC PHP_FUNCTION(intlcal_equals)
899 {
900 	zval			*other_object;
901 	Calendar_object	*other_co;
902 	CALENDAR_METHOD_INIT_VARS;
903 
904 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
905 			"OO", &object, Calendar_ce_ptr, &other_object, Calendar_ce_ptr)
906 			== FAILURE) {
907 		RETURN_THROWS();
908 	}
909 
910 	CALENDAR_METHOD_FETCH_OBJECT;
911 	other_co = Z_INTL_CALENDAR_P(other_object);
912 	if (other_co->ucal == NULL) {
913 		zend_argument_error(NULL, 2, "is uninitialized");
914 		RETURN_THROWS();
915 	}
916 
917 	UBool result = co->ucal->equals(*other_co->ucal, CALENDAR_ERROR_CODE(co));
918 	INTL_METHOD_CHECK_STATUS(co, "intlcal_equals: error calling ICU Calendar::equals");
919 
920 	RETURN_BOOL((int)result);
921 }
922 
PHP_FUNCTION(intlcal_get_repeated_wall_time_option)923 U_CFUNC PHP_FUNCTION(intlcal_get_repeated_wall_time_option)
924 {
925 	CALENDAR_METHOD_INIT_VARS;
926 
927 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
928 			"O", &object, Calendar_ce_ptr) == FAILURE) {
929 		RETURN_THROWS();
930 	}
931 
932 	CALENDAR_METHOD_FETCH_OBJECT;
933 
934 	RETURN_LONG(co->ucal->getRepeatedWallTimeOption());
935 }
936 
PHP_FUNCTION(intlcal_get_skipped_wall_time_option)937 U_CFUNC PHP_FUNCTION(intlcal_get_skipped_wall_time_option)
938 {
939 	CALENDAR_METHOD_INIT_VARS;
940 
941 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
942 			"O", &object, Calendar_ce_ptr) == FAILURE) {
943 		RETURN_THROWS();
944 	}
945 
946 	CALENDAR_METHOD_FETCH_OBJECT;
947 
948 	RETURN_LONG(co->ucal->getSkippedWallTimeOption());
949 }
950 
PHP_FUNCTION(intlcal_set_repeated_wall_time_option)951 U_CFUNC PHP_FUNCTION(intlcal_set_repeated_wall_time_option)
952 {
953 	zend_long	option;
954 	CALENDAR_METHOD_INIT_VARS;
955 
956 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
957 			"Ol", &object, Calendar_ce_ptr, &option) == FAILURE) {
958 		RETURN_THROWS();
959 	}
960 
961 	if (option != UCAL_WALLTIME_FIRST && option != UCAL_WALLTIME_LAST) {
962 		zend_argument_value_error(hasThis() ? 1 : 2, "must be either IntlCalendar::WALLTIME_FIRST or "
963 			"IntlCalendar::WALLTIME_LAST");
964 		RETURN_THROWS();
965 	}
966 
967 	CALENDAR_METHOD_FETCH_OBJECT;
968 
969 	co->ucal->setRepeatedWallTimeOption((UCalendarWallTimeOption)option);
970 
971 	RETURN_TRUE;
972 }
973 
PHP_FUNCTION(intlcal_set_skipped_wall_time_option)974 U_CFUNC PHP_FUNCTION(intlcal_set_skipped_wall_time_option)
975 {
976 	zend_long	option;
977 	CALENDAR_METHOD_INIT_VARS;
978 
979 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
980 			"Ol", &object, Calendar_ce_ptr, &option) == FAILURE) {
981 		RETURN_THROWS();
982 	}
983 
984 	if (option != UCAL_WALLTIME_FIRST && option != UCAL_WALLTIME_LAST
985 			&& option != UCAL_WALLTIME_NEXT_VALID) {
986 		zend_argument_value_error(hasThis() ? 1 : 2, "must be one of IntlCalendar::WALLTIME_FIRST, "
987 			"IntlCalendar::WALLTIME_LAST, or IntlCalendar::WALLTIME_NEXT_VALID");
988 		RETURN_THROWS();
989 	}
990 
991 	CALENDAR_METHOD_FETCH_OBJECT;
992 
993 	co->ucal->setSkippedWallTimeOption((UCalendarWallTimeOption)option);
994 
995 	RETURN_TRUE;
996 }
997 
PHP_FUNCTION(intlcal_from_date_time)998 U_CFUNC PHP_FUNCTION(intlcal_from_date_time)
999 {
1000 	zend_object     *date_obj;
1001 	zend_string     *date_str;
1002 	zval			zv_tmp, zv_arg, zv_timestamp;
1003 	php_date_obj	*datetime;
1004 	char			*locale_str = NULL;
1005 	size_t				locale_str_len;
1006 	TimeZone		*timeZone;
1007 	UErrorCode		status = U_ZERO_ERROR;
1008 	Calendar        *cal;
1009 	intl_error_reset(NULL);
1010 
1011 	ZEND_PARSE_PARAMETERS_START(1, 2)
1012 		Z_PARAM_OBJ_OF_CLASS_OR_STR(date_obj, php_date_get_date_ce(), date_str)
1013 		Z_PARAM_OPTIONAL
1014 		Z_PARAM_STRING_OR_NULL(locale_str, locale_str_len)
1015 	ZEND_PARSE_PARAMETERS_END();
1016 
1017 	if (date_str) {
1018 		object_init_ex(&zv_tmp, php_date_get_date_ce());
1019 		ZVAL_STR(&zv_arg, date_str);
1020 		zend_call_known_instance_method_with_1_params(Z_OBJCE(zv_tmp)->constructor, Z_OBJ(zv_tmp), NULL, &zv_arg);
1021 		date_obj = Z_OBJ(zv_tmp);
1022 		if (EG(exception)) {
1023 			zend_object_store_ctor_failed(Z_OBJ(zv_tmp));
1024 			goto error;
1025 		}
1026 	}
1027 
1028 	datetime = php_date_obj_from_obj(date_obj);
1029 	if (!datetime->time) {
1030 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1031 			"intlcal_from_date_time: DateTime object is unconstructed",
1032 			0);
1033 		goto error;
1034 	}
1035 
1036 	zend_call_method_with_0_params(date_obj, php_date_get_date_ce(), NULL, "gettimestamp", &zv_timestamp);
1037 	if (Z_TYPE(zv_timestamp) != IS_LONG) {
1038 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1039 			"intlcal_from_date_time: bad DateTime; call to "
1040 			"DateTime::getTimestamp() failed", 0);
1041 		zval_ptr_dtor(&zv_timestamp);
1042 		goto error;
1043 	}
1044 
1045 	if (!datetime->time->is_localtime) {
1046 		timeZone = TimeZone::getGMT()->clone();
1047 	} else {
1048 		timeZone = timezone_convert_datetimezone(datetime->time->zone_type,
1049 			datetime, 1, NULL, "intlcal_from_date_time");
1050 		if (timeZone == NULL) {
1051 			goto error;
1052 		}
1053 	}
1054 
1055 	if (!locale_str) {
1056 		locale_str = const_cast<char*>(intl_locale_get_default());
1057 	}
1058 
1059 	cal = Calendar::createInstance(timeZone,
1060 		Locale::createFromName(locale_str), status);
1061 	if (UNEXPECTED(cal == NULL)) {
1062 		delete timeZone;
1063 		intl_error_set(NULL, status, "intlcal_from_date_time: "
1064 				"error creating ICU Calendar object", 0);
1065 		goto error;
1066 	}
1067 	cal->setTime(((UDate)Z_LVAL(zv_timestamp)) * 1000., status);
1068     if (U_FAILURE(status)) {
1069 		/* time zone was adopted by cal; should not be deleted here */
1070 		delete cal;
1071 		intl_error_set(NULL, status, "intlcal_from_date_time: "
1072 				"error creating ICU Calendar::setTime()", 0);
1073         goto error;
1074     }
1075 
1076 	calendar_object_create(return_value, cal);
1077 
1078 error:
1079 	if (date_str) {
1080 		OBJ_RELEASE(date_obj);
1081 	}
1082 }
1083 
PHP_FUNCTION(intlcal_to_date_time)1084 U_CFUNC PHP_FUNCTION(intlcal_to_date_time)
1085 {
1086 	zval retval;
1087 	CALENDAR_METHOD_INIT_VARS;
1088 
1089 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1090 			&object, Calendar_ce_ptr) == FAILURE) {
1091 		RETURN_THROWS();
1092 	}
1093 
1094 	CALENDAR_METHOD_FETCH_OBJECT;
1095 
1096 	/* There are no exported functions in ext/date to this
1097 	 * in a more native fashion */
1098 	double	date = co->ucal->getTime(CALENDAR_ERROR_CODE(co)) / 1000.;
1099 	int64_t	ts;
1100 	char	ts_str[sizeof("@-9223372036854775808")];
1101 	int		ts_str_len;
1102 	zval	ts_zval, tmp;
1103 
1104 	INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
1105 
1106 	if (UNEXPECTED(date > (double)U_INT64_MAX || date < (double)U_INT64_MIN)) {
1107 		intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1108 			"intlcal_to_date_time: The calendar date is out of the "
1109 			"range for a 64-bit integer", 0);
1110 		RETURN_FALSE;
1111 	}
1112 
1113 	ZVAL_UNDEF(&retval);
1114 	ts = (int64_t)date;
1115 
1116 	ts_str_len = slprintf(ts_str, sizeof(ts_str), "@%" PRIi64, ts);
1117 	ZVAL_STRINGL(&ts_zval, ts_str, ts_str_len);
1118 
1119 	/* Now get the time zone */
1120 	const TimeZone& tz = co->ucal->getTimeZone();
1121 	zval *timezone_zval = timezone_convert_to_datetimezone(
1122 		&tz, CALENDAR_ERROR_P(co), "intlcal_to_date_time", &tmp);
1123 	if (timezone_zval == NULL) {
1124 		zval_ptr_dtor(&ts_zval);
1125 		RETURN_FALSE;
1126 	}
1127 
1128 	/* resources allocated from now on */
1129 
1130 	/* Finally, instantiate object and call constructor */
1131 	object_init_ex(return_value, php_date_get_date_ce());
1132 	zend_call_known_instance_method_with_2_params(
1133 		Z_OBJCE_P(return_value)->constructor, Z_OBJ_P(return_value), NULL, &ts_zval, timezone_zval);
1134 	if (EG(exception)) {
1135 		zend_object_store_ctor_failed(Z_OBJ_P(return_value));
1136 		zval_ptr_dtor(return_value);
1137 		zval_ptr_dtor(&ts_zval);
1138 
1139 		RETVAL_FALSE;
1140 		goto error;
1141 	}
1142 	zval_ptr_dtor(&ts_zval);
1143 
1144 	/* due to bug #40743, we have to set the time zone again */
1145 	zend_call_method_with_1_params(Z_OBJ_P(return_value), NULL, NULL, "settimezone",
1146 			&retval, timezone_zval);
1147 	if (Z_ISUNDEF(retval) || Z_TYPE(retval) == IS_FALSE) {
1148 		intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1149 			"intlcal_to_date_time: call to DateTime::setTimeZone has failed",
1150 			1);
1151 		zval_ptr_dtor(return_value);
1152 		RETVAL_FALSE;
1153 		goto error;
1154 	}
1155 
1156 error:
1157 	zval_ptr_dtor(timezone_zval);
1158 	zval_ptr_dtor(&retval);
1159 }
1160 
PHP_FUNCTION(intlcal_get_error_code)1161 U_CFUNC PHP_FUNCTION(intlcal_get_error_code)
1162 {
1163 	CALENDAR_METHOD_INIT_VARS;
1164 
1165 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1166 			&object, Calendar_ce_ptr) == FAILURE) {
1167 		RETURN_THROWS();
1168 	}
1169 
1170 	/* Fetch the object (without resetting its last error code ). */
1171 	co = Z_INTL_CALENDAR_P(object);
1172 	if (co == NULL)
1173 		RETURN_FALSE;
1174 
1175 	RETURN_LONG((zend_long)CALENDAR_ERROR_CODE(co));
1176 }
1177 
PHP_FUNCTION(intlcal_get_error_message)1178 U_CFUNC PHP_FUNCTION(intlcal_get_error_message)
1179 {
1180 	zend_string* message = NULL;
1181 	CALENDAR_METHOD_INIT_VARS;
1182 
1183 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1184 			&object, Calendar_ce_ptr) == FAILURE) {
1185 		RETURN_THROWS();
1186 	}
1187 
1188 
1189 	/* Fetch the object (without resetting its last error code ). */
1190 	co = Z_INTL_CALENDAR_P(object);
1191 	if (co == NULL)
1192 		RETURN_FALSE;
1193 
1194 	/* Return last error message. */
1195 	message = intl_error_get_message(CALENDAR_ERROR_P(co));
1196 	RETURN_STR(message);
1197 }
1198