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