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(getThis() ? ((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(getThis() ? ((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(getThis() ? ((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 (zend_parse_method_parameters(
381 		ZEND_NUM_ARGS(), object, "Oll|llll",
382 		&object, Calendar_ce_ptr, &args[0], &args[1], &args[2], &args[3], &args[4], &args[5]
383 	) == FAILURE) {
384 		RETURN_THROWS();
385 	}
386 
387 	for (int i = 0; i < arg_num; i++) {
388 		/* Arguments start at 1 */
389 		ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(args[i], i + 1);
390 	}
391 
392 	CALENDAR_METHOD_FETCH_OBJECT;
393 
394 	if (arg_num == 2) {
395 		ZEND_VALUE_ERROR_INVALID_FIELD(args[0], 2);
396 		co->ucal->set((UCalendarDateFields)args[0], (int32_t)args[1]);
397 	} else if (arg_num == 3) {
398 		co->ucal->set((int32_t)args[0], (int32_t)args[1], (int32_t)args[2]);
399 	} else if (arg_num == 4) {
400 		zend_argument_count_error("IntlCalendar::set() has no variant with exactly 4 parameters");
401 		RETURN_THROWS();
402 	} else if (arg_num == 5) {
403 		co->ucal->set((int32_t)args[0], (int32_t)args[1], (int32_t)args[2], (int32_t)args[3], (int32_t)args[4]);
404 	} else {
405 		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]);
406 	}
407 
408 	RETURN_TRUE;
409 }
410 
PHP_METHOD(IntlCalendar,setDate)411 U_CFUNC PHP_METHOD(IntlCalendar, setDate)
412 {
413 	zend_long year, month, day;
414 
415 	CALENDAR_METHOD_INIT_VARS;
416 
417 	object = getThis();
418 
419 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), object, "Olll",
420 		&object, Calendar_ce_ptr, &year, &month, &day) == FAILURE) {
421 		RETURN_THROWS();
422 	}
423 
424 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 1);
425 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 2);
426 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 3);
427 
428 	CALENDAR_METHOD_FETCH_OBJECT;
429 
430 	co->ucal->set((int32_t) year, (int32_t) month, (int32_t) day);
431 }
432 
PHP_METHOD(IntlCalendar,setDateTime)433 U_CFUNC PHP_METHOD(IntlCalendar, setDateTime)
434 {
435 	zend_long year, month, day, hour, minute, second = 0;
436 	bool second_is_null = true;
437 
438 	CALENDAR_METHOD_INIT_VARS;
439 
440 	object = getThis();
441 
442 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), object, "Olllll|l!",
443 		&object, Calendar_ce_ptr, &year, &month, &day, &hour, &minute, &second, &second_is_null) == FAILURE) {
444 		RETURN_THROWS();
445 	}
446 
447 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 1);
448 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 2);
449 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 3);
450 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(hour, 4);
451 	ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(minute, 5);
452 
453 	CALENDAR_METHOD_FETCH_OBJECT;
454 
455 	if (second_is_null) {
456 		co->ucal->set((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute);
457 	} else {
458 		ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(second, 6);
459 		co->ucal->set((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute, (int32_t) second);
460 	}
461 }
462 
PHP_FUNCTION(intlcal_roll)463 U_CFUNC PHP_FUNCTION(intlcal_roll)
464 {
465 	zval *zvalue;
466 	zend_long field, value;
467 	CALENDAR_METHOD_INIT_VARS;
468 
469 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Olz", &object, Calendar_ce_ptr, &field, &zvalue) == FAILURE) {
470 		RETURN_THROWS();
471 	}
472 
473 	CALENDAR_METHOD_FETCH_OBJECT;
474 
475 	ZEND_VALUE_ERROR_INVALID_FIELD(field, 2);
476 
477 	if (Z_TYPE_P(zvalue) == IS_FALSE || Z_TYPE_P(zvalue) == IS_TRUE) {
478 		value = Z_TYPE_P(zvalue) == IS_TRUE ? 1 : -1;
479 		php_error_docref(NULL, E_DEPRECATED, "Passing bool is deprecated, use 1 or -1 instead");
480 	} else {
481 		value = zval_get_long(zvalue);
482 		ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(value, 3);
483 	}
484 
485 	co->ucal->roll((UCalendarDateFields)field, (int32_t)value, CALENDAR_ERROR_CODE(co));
486 
487 	INTL_METHOD_CHECK_STATUS(co, "intlcal_roll: Error calling ICU Calendar::roll");
488 
489 	RETURN_TRUE;
490 }
491 
PHP_FUNCTION(intlcal_clear)492 U_CFUNC PHP_FUNCTION(intlcal_clear)
493 {
494 	zend_long field;
495 	bool field_is_null = 1;
496 	CALENDAR_METHOD_INIT_VARS;
497 
498 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(),
499 			getThis(), "O|l!", &object, Calendar_ce_ptr, &field, &field_is_null) == FAILURE) {
500 		RETURN_THROWS();
501 	}
502 
503 	CALENDAR_METHOD_FETCH_OBJECT;
504 
505 	if (field_is_null) {
506 		co->ucal->clear();
507 	} else {
508 		ZEND_VALUE_ERROR_INVALID_FIELD(field, 2);
509 
510 		co->ucal->clear((UCalendarDateFields)field);
511 	}
512 
513 	RETURN_TRUE;
514 }
515 
PHP_FUNCTION(intlcal_field_difference)516 U_CFUNC PHP_FUNCTION(intlcal_field_difference)
517 {
518 	zend_long	field;
519 	double	when;
520 	CALENDAR_METHOD_INIT_VARS;
521 
522 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
523 			"Odl", &object, Calendar_ce_ptr, &when, &field)	== FAILURE) {
524 		RETURN_THROWS();
525 	}
526 
527 	ZEND_VALUE_ERROR_INVALID_FIELD(field, 3);
528 
529 	CALENDAR_METHOD_FETCH_OBJECT;
530 
531 	int32_t result = co->ucal->fieldDifference((UDate)when,
532 		(UCalendarDateFields)field, CALENDAR_ERROR_CODE(co));
533 	INTL_METHOD_CHECK_STATUS(co,
534 		"intlcal_field_difference: Call to ICU method has failed");
535 
536 	RETURN_LONG((zend_long)result);
537 }
538 
PHP_FUNCTION(intlcal_get_actual_maximum)539 U_CFUNC PHP_FUNCTION(intlcal_get_actual_maximum)
540 {
541 	_php_intlcal_field_uec_ret_in32t_method(&Calendar::getActualMaximum,
542 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
543 }
544 
PHP_FUNCTION(intlcal_get_actual_minimum)545 U_CFUNC PHP_FUNCTION(intlcal_get_actual_minimum)
546 {
547 	_php_intlcal_field_uec_ret_in32t_method(&Calendar::getActualMinimum,
548 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
549 }
550 
PHP_FUNCTION(intlcal_get_day_of_week_type)551 U_CFUNC PHP_FUNCTION(intlcal_get_day_of_week_type)
552 {
553 	zend_long	dow;
554 	CALENDAR_METHOD_INIT_VARS;
555 
556 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
557 			"Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
558 		RETURN_THROWS();
559 	}
560 
561 	ZEND_VALUE_ERROR_INVALID_DAY_OF_WEEK(dow, 2);
562 
563 	CALENDAR_METHOD_FETCH_OBJECT;
564 
565 	int32_t result = co->ucal->getDayOfWeekType(
566 		(UCalendarDaysOfWeek)dow, CALENDAR_ERROR_CODE(co));
567 	INTL_METHOD_CHECK_STATUS(co,
568 		"intlcal_get_day_of_week_type: Call to ICU method has failed");
569 
570 	RETURN_LONG((zend_long)result);
571 }
572 
PHP_FUNCTION(intlcal_get_first_day_of_week)573 U_CFUNC PHP_FUNCTION(intlcal_get_first_day_of_week)
574 {
575 	CALENDAR_METHOD_INIT_VARS;
576 
577 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
578 			"O", &object, Calendar_ce_ptr) == FAILURE) {
579 		RETURN_THROWS();
580 	}
581 
582 	CALENDAR_METHOD_FETCH_OBJECT;
583 
584 	int32_t result = co->ucal->getFirstDayOfWeek(CALENDAR_ERROR_CODE(co));
585 	INTL_METHOD_CHECK_STATUS(co,
586 		"intlcal_get_first_day_of_week: Call to ICU method has failed");
587 
588 	RETURN_LONG((zend_long)result);
589 }
590 
_php_intlcal_field_ret_in32t_method(int32_t (Calendar::* func)(UCalendarDateFields)const,INTERNAL_FUNCTION_PARAMETERS)591 static void _php_intlcal_field_ret_in32t_method(
592 		int32_t (Calendar::*func)(UCalendarDateFields) const,
593 		INTERNAL_FUNCTION_PARAMETERS)
594 {
595 	zend_long	field;
596 	CALENDAR_METHOD_INIT_VARS;
597 
598 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
599 			"Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
600 		RETURN_THROWS();
601 	}
602 
603 	ZEND_VALUE_ERROR_INVALID_FIELD(field, 2);
604 
605 	CALENDAR_METHOD_FETCH_OBJECT;
606 
607 	int32_t result = (co->ucal->*func)((UCalendarDateFields)field);
608 	INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
609 
610 	RETURN_LONG((zend_long)result);
611 }
612 
PHP_FUNCTION(intlcal_get_greatest_minimum)613 U_CFUNC PHP_FUNCTION(intlcal_get_greatest_minimum)
614 {
615 	_php_intlcal_field_ret_in32t_method(&Calendar::getGreatestMinimum,
616 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
617 }
618 
PHP_FUNCTION(intlcal_get_least_maximum)619 U_CFUNC PHP_FUNCTION(intlcal_get_least_maximum)
620 {
621 	_php_intlcal_field_ret_in32t_method(&Calendar::getLeastMaximum,
622 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
623 }
624 
PHP_FUNCTION(intlcal_get_locale)625 U_CFUNC PHP_FUNCTION(intlcal_get_locale)
626 {
627 	zend_long	locale_type;
628 	CALENDAR_METHOD_INIT_VARS;
629 
630 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
631 			"Ol", &object, Calendar_ce_ptr, &locale_type) == FAILURE) {
632 		RETURN_THROWS();
633 	}
634 
635 	if (locale_type != ULOC_ACTUAL_LOCALE && locale_type != ULOC_VALID_LOCALE) {
636 		zend_argument_value_error(getThis() ? 1 : 2, "must be either Locale::ACTUAL_LOCALE or Locale::VALID_LOCALE");
637 		RETURN_THROWS();
638 	}
639 
640 	CALENDAR_METHOD_FETCH_OBJECT;
641 
642 	Locale locale = co->ucal->getLocale((ULocDataLocaleType)locale_type,
643 		CALENDAR_ERROR_CODE(co));
644 	INTL_METHOD_CHECK_STATUS(co,
645 		"intlcal_get_locale: Call to ICU method has failed");
646 
647 	RETURN_STRING(locale.getName());
648 }
649 
PHP_FUNCTION(intlcal_get_maximum)650 U_CFUNC PHP_FUNCTION(intlcal_get_maximum)
651 {
652 	_php_intlcal_field_ret_in32t_method(&Calendar::getMaximum,
653 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
654 }
655 
PHP_FUNCTION(intlcal_get_minimal_days_in_first_week)656 U_CFUNC PHP_FUNCTION(intlcal_get_minimal_days_in_first_week)
657 {
658 	CALENDAR_METHOD_INIT_VARS;
659 
660 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
661 			"O", &object, Calendar_ce_ptr) == FAILURE) {
662 		RETURN_THROWS();
663 	}
664 
665 	CALENDAR_METHOD_FETCH_OBJECT;
666 
667 	uint8_t result = co->ucal->getMinimalDaysInFirstWeek();
668 	INTL_METHOD_CHECK_STATUS(co,
669 		"intlcal_get_first_day_of_week: Call to ICU method has failed"); /* TODO Is it really a failure? */
670 
671 	RETURN_LONG((zend_long)result);
672 }
673 
PHP_FUNCTION(intlcal_get_minimum)674 U_CFUNC PHP_FUNCTION(intlcal_get_minimum)
675 {
676 	_php_intlcal_field_ret_in32t_method(&Calendar::getMinimum,
677 		INTERNAL_FUNCTION_PARAM_PASSTHRU);
678 }
679 
PHP_FUNCTION(intlcal_get_time_zone)680 U_CFUNC PHP_FUNCTION(intlcal_get_time_zone)
681 {
682 	CALENDAR_METHOD_INIT_VARS;
683 
684 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
685 			"O", &object, Calendar_ce_ptr) == FAILURE) {
686 		RETURN_THROWS();
687 	}
688 
689 	CALENDAR_METHOD_FETCH_OBJECT;
690 
691 	TimeZone *tz = co->ucal->getTimeZone().clone();
692 	if (UNEXPECTED(tz == NULL)) {
693 		intl_errors_set(CALENDAR_ERROR_P(co), U_MEMORY_ALLOCATION_ERROR,
694 			"intlcal_get_time_zone: could not clone TimeZone", 0);
695 		RETURN_FALSE;
696 	}
697 
698 	timezone_object_construct(tz, return_value, 1);
699 }
700 
PHP_FUNCTION(intlcal_get_type)701 U_CFUNC PHP_FUNCTION(intlcal_get_type)
702 {
703 	CALENDAR_METHOD_INIT_VARS;
704 
705 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
706 			"O", &object, Calendar_ce_ptr) == FAILURE) {
707 		RETURN_THROWS();
708 	}
709 
710 	CALENDAR_METHOD_FETCH_OBJECT;
711 
712 	RETURN_STRING(co->ucal->getType());
713 }
714 
PHP_FUNCTION(intlcal_get_weekend_transition)715 U_CFUNC PHP_FUNCTION(intlcal_get_weekend_transition)
716 {
717 	zend_long	dow;
718 	CALENDAR_METHOD_INIT_VARS;
719 
720 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
721 			"Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
722 		RETURN_THROWS();
723 	}
724 
725 	ZEND_VALUE_ERROR_INVALID_DAY_OF_WEEK(dow, 2);
726 
727 	CALENDAR_METHOD_FETCH_OBJECT;
728 
729 	int32_t res = co->ucal->getWeekendTransition((UCalendarDaysOfWeek)dow,
730 		CALENDAR_ERROR_CODE(co));
731 	INTL_METHOD_CHECK_STATUS(co, "intlcal_get_weekend_transition: "
732 		"Error calling ICU method");
733 
734 	RETURN_LONG((zend_long)res);
735 }
736 
PHP_FUNCTION(intlcal_in_daylight_time)737 U_CFUNC PHP_FUNCTION(intlcal_in_daylight_time)
738 {
739 	CALENDAR_METHOD_INIT_VARS;
740 
741 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
742 			"O", &object, Calendar_ce_ptr) == FAILURE) {
743 		RETURN_THROWS();
744 	}
745 
746 	CALENDAR_METHOD_FETCH_OBJECT;
747 
748 	UBool ret = co->ucal->inDaylightTime(CALENDAR_ERROR_CODE(co));
749 	INTL_METHOD_CHECK_STATUS(co, "intlcal_in_daylight_time: "
750 		"Error calling ICU method");
751 
752 	RETURN_BOOL((int)ret);
753 }
754 
PHP_FUNCTION(intlcal_is_equivalent_to)755 U_CFUNC PHP_FUNCTION(intlcal_is_equivalent_to)
756 {
757 	zval			*other_object;
758 	Calendar_object *other_co;
759 	CALENDAR_METHOD_INIT_VARS;
760 
761 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
762 			"OO", &object, Calendar_ce_ptr, &other_object, Calendar_ce_ptr)
763 			== FAILURE) {
764 		RETURN_THROWS();
765 	}
766 
767 	other_co = Z_INTL_CALENDAR_P(other_object);
768 	if (other_co->ucal == NULL) {
769 		zend_argument_error(NULL, 2, "is uninitialized");
770 		RETURN_THROWS();
771 	}
772 
773 	CALENDAR_METHOD_FETCH_OBJECT;
774 
775 	RETURN_BOOL((int)co->ucal->isEquivalentTo(*other_co->ucal));
776 }
777 
PHP_FUNCTION(intlcal_is_lenient)778 U_CFUNC PHP_FUNCTION(intlcal_is_lenient)
779 {
780 	CALENDAR_METHOD_INIT_VARS;
781 
782 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
783 			"O", &object, Calendar_ce_ptr) == FAILURE) {
784 		RETURN_THROWS();
785 	}
786 
787 	CALENDAR_METHOD_FETCH_OBJECT;
788 
789 	RETURN_BOOL((int)co->ucal->isLenient());
790 }
791 
PHP_FUNCTION(intlcal_is_set)792 U_CFUNC PHP_FUNCTION(intlcal_is_set)
793 {
794 	zend_long field;
795 	CALENDAR_METHOD_INIT_VARS;
796 
797 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
798 			"Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
799 		RETURN_THROWS();
800 	}
801 
802 	ZEND_VALUE_ERROR_INVALID_FIELD(field, 2);
803 
804 	CALENDAR_METHOD_FETCH_OBJECT;
805 
806 	RETURN_BOOL((int)co->ucal->isSet((UCalendarDateFields)field));
807 }
808 
PHP_FUNCTION(intlcal_is_weekend)809 U_CFUNC PHP_FUNCTION(intlcal_is_weekend)
810 {
811 	double date;
812 	bool date_is_null = 1;
813 	CALENDAR_METHOD_INIT_VARS;
814 
815 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
816 				"O|d!", &object, Calendar_ce_ptr, &date, &date_is_null) == FAILURE) {
817 		RETURN_THROWS();
818 	}
819 
820 	CALENDAR_METHOD_FETCH_OBJECT;
821 
822 	if (date_is_null) {
823 		RETURN_BOOL((int)co->ucal->isWeekend());
824 	} else {
825 		UBool ret = co->ucal->isWeekend((UDate)date, CALENDAR_ERROR_CODE(co));
826 		INTL_METHOD_CHECK_STATUS(co, "intlcal_is_weekend: "
827 			"Error calling ICU method");
828 		RETURN_BOOL((int)ret);
829 	}
830 }
831 
832 
PHP_FUNCTION(intlcal_set_first_day_of_week)833 U_CFUNC PHP_FUNCTION(intlcal_set_first_day_of_week)
834 {
835 	zend_long	dow;
836 	CALENDAR_METHOD_INIT_VARS;
837 
838 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
839 			"Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
840 		RETURN_THROWS();
841 	}
842 
843 	ZEND_VALUE_ERROR_INVALID_DAY_OF_WEEK(dow, 2);
844 
845 	CALENDAR_METHOD_FETCH_OBJECT;
846 
847 	co->ucal->setFirstDayOfWeek((UCalendarDaysOfWeek)dow);
848 
849 	RETURN_TRUE;
850 }
851 
PHP_FUNCTION(intlcal_set_lenient)852 U_CFUNC PHP_FUNCTION(intlcal_set_lenient)
853 {
854 	bool is_lenient;
855 	CALENDAR_METHOD_INIT_VARS;
856 
857 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
858 			"Ob", &object, Calendar_ce_ptr, &is_lenient) == FAILURE) {
859 		RETURN_THROWS();
860 	}
861 
862 	CALENDAR_METHOD_FETCH_OBJECT;
863 
864 	co->ucal->setLenient((UBool) is_lenient);
865 
866 	RETURN_TRUE;
867 }
868 
PHP_FUNCTION(intlcal_set_minimal_days_in_first_week)869 U_CFUNC PHP_FUNCTION(intlcal_set_minimal_days_in_first_week)
870 {
871 	zend_long	num_days;
872 	CALENDAR_METHOD_INIT_VARS;
873 
874 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
875 			"Ol", &object, Calendar_ce_ptr, &num_days) == FAILURE) {
876 		RETURN_THROWS();
877 	}
878 
879 	// Use ZEND_VALUE_ERROR_INVALID_DAY_OF_WEEK ?
880 	if (num_days < 1 || num_days > 7) {
881 		zend_argument_value_error(getThis() ? 1 : 2, "must be between 1 and 7");
882 		RETURN_THROWS();
883 	}
884 
885 	CALENDAR_METHOD_FETCH_OBJECT;
886 
887 	co->ucal->setMinimalDaysInFirstWeek((uint8_t)num_days);
888 
889 	RETURN_TRUE;
890 }
891 
PHP_FUNCTION(intlcal_equals)892 U_CFUNC PHP_FUNCTION(intlcal_equals)
893 {
894 	zval			*other_object;
895 	Calendar_object	*other_co;
896 	CALENDAR_METHOD_INIT_VARS;
897 
898 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
899 			"OO", &object, Calendar_ce_ptr, &other_object, Calendar_ce_ptr)
900 			== FAILURE) {
901 		RETURN_THROWS();
902 	}
903 
904 	CALENDAR_METHOD_FETCH_OBJECT;
905 	other_co = Z_INTL_CALENDAR_P(other_object);
906 	if (other_co->ucal == NULL) {
907 		zend_argument_error(NULL, 2, "is uninitialized");
908 		RETURN_THROWS();
909 	}
910 
911 	UBool result = co->ucal->equals(*other_co->ucal, CALENDAR_ERROR_CODE(co));
912 	INTL_METHOD_CHECK_STATUS(co, "intlcal_equals: error calling ICU Calendar::equals");
913 
914 	RETURN_BOOL((int)result);
915 }
916 
PHP_FUNCTION(intlcal_get_repeated_wall_time_option)917 U_CFUNC PHP_FUNCTION(intlcal_get_repeated_wall_time_option)
918 {
919 	CALENDAR_METHOD_INIT_VARS;
920 
921 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
922 			"O", &object, Calendar_ce_ptr) == FAILURE) {
923 		RETURN_THROWS();
924 	}
925 
926 	CALENDAR_METHOD_FETCH_OBJECT;
927 
928 	RETURN_LONG(co->ucal->getRepeatedWallTimeOption());
929 }
930 
PHP_FUNCTION(intlcal_get_skipped_wall_time_option)931 U_CFUNC PHP_FUNCTION(intlcal_get_skipped_wall_time_option)
932 {
933 	CALENDAR_METHOD_INIT_VARS;
934 
935 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
936 			"O", &object, Calendar_ce_ptr) == FAILURE) {
937 		RETURN_THROWS();
938 	}
939 
940 	CALENDAR_METHOD_FETCH_OBJECT;
941 
942 	RETURN_LONG(co->ucal->getSkippedWallTimeOption());
943 }
944 
PHP_FUNCTION(intlcal_set_repeated_wall_time_option)945 U_CFUNC PHP_FUNCTION(intlcal_set_repeated_wall_time_option)
946 {
947 	zend_long	option;
948 	CALENDAR_METHOD_INIT_VARS;
949 
950 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
951 			"Ol", &object, Calendar_ce_ptr, &option) == FAILURE) {
952 		RETURN_THROWS();
953 	}
954 
955 	if (option != UCAL_WALLTIME_FIRST && option != UCAL_WALLTIME_LAST) {
956 		zend_argument_value_error(getThis() ? 1 : 2, "must be either IntlCalendar::WALLTIME_FIRST or "
957 			"IntlCalendar::WALLTIME_LAST");
958 		RETURN_THROWS();
959 	}
960 
961 	CALENDAR_METHOD_FETCH_OBJECT;
962 
963 	co->ucal->setRepeatedWallTimeOption((UCalendarWallTimeOption)option);
964 
965 	RETURN_TRUE;
966 }
967 
PHP_FUNCTION(intlcal_set_skipped_wall_time_option)968 U_CFUNC PHP_FUNCTION(intlcal_set_skipped_wall_time_option)
969 {
970 	zend_long	option;
971 	CALENDAR_METHOD_INIT_VARS;
972 
973 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
974 			"Ol", &object, Calendar_ce_ptr, &option) == FAILURE) {
975 		RETURN_THROWS();
976 	}
977 
978 	if (option != UCAL_WALLTIME_FIRST && option != UCAL_WALLTIME_LAST
979 			&& option != UCAL_WALLTIME_NEXT_VALID) {
980 		zend_argument_value_error(getThis() ? 1 : 2, "must be one of IntlCalendar::WALLTIME_FIRST, "
981 			"IntlCalendar::WALLTIME_LAST, or IntlCalendar::WALLTIME_NEXT_VALID");
982 		RETURN_THROWS();
983 	}
984 
985 	CALENDAR_METHOD_FETCH_OBJECT;
986 
987 	co->ucal->setSkippedWallTimeOption((UCalendarWallTimeOption)option);
988 
989 	RETURN_TRUE;
990 }
991 
PHP_FUNCTION(intlcal_from_date_time)992 U_CFUNC PHP_FUNCTION(intlcal_from_date_time)
993 {
994 	zend_object     *date_obj;
995 	zend_string     *date_str;
996 	zval			zv_tmp, zv_arg, zv_timestamp;
997 	php_date_obj	*datetime;
998 	char			*locale_str = NULL;
999 	size_t				locale_str_len;
1000 	TimeZone		*timeZone;
1001 	UErrorCode		status = U_ZERO_ERROR;
1002 	Calendar        *cal;
1003 	intl_error_reset(NULL);
1004 
1005 	ZEND_PARSE_PARAMETERS_START(1, 2)
1006 		Z_PARAM_OBJ_OF_CLASS_OR_STR(date_obj, php_date_get_date_ce(), date_str)
1007 		Z_PARAM_OPTIONAL
1008 		Z_PARAM_STRING_OR_NULL(locale_str, locale_str_len)
1009 	ZEND_PARSE_PARAMETERS_END();
1010 
1011 	if (date_str) {
1012 		object_init_ex(&zv_tmp, php_date_get_date_ce());
1013 		ZVAL_STR(&zv_arg, date_str);
1014 		zend_call_known_instance_method_with_1_params(Z_OBJCE(zv_tmp)->constructor, Z_OBJ(zv_tmp), NULL, &zv_arg);
1015 		date_obj = Z_OBJ(zv_tmp);
1016 		if (EG(exception)) {
1017 			zend_object_store_ctor_failed(Z_OBJ(zv_tmp));
1018 			goto error;
1019 		}
1020 	}
1021 
1022 	datetime = php_date_obj_from_obj(date_obj);
1023 	if (!datetime->time) {
1024 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1025 			"intlcal_from_date_time: DateTime object is unconstructed",
1026 			0);
1027 		goto error;
1028 	}
1029 
1030 	zend_call_method_with_0_params(date_obj, php_date_get_date_ce(), NULL, "gettimestamp", &zv_timestamp);
1031 	if (Z_TYPE(zv_timestamp) != IS_LONG) {
1032 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1033 			"intlcal_from_date_time: bad DateTime; call to "
1034 			"DateTime::getTimestamp() failed", 0);
1035 		zval_ptr_dtor(&zv_timestamp);
1036 		goto error;
1037 	}
1038 
1039 	if (!datetime->time->is_localtime) {
1040 		timeZone = TimeZone::getGMT()->clone();
1041 	} else {
1042 		timeZone = timezone_convert_datetimezone(datetime->time->zone_type,
1043 			datetime, 1, NULL, "intlcal_from_date_time");
1044 		if (timeZone == NULL) {
1045 			goto error;
1046 		}
1047 	}
1048 
1049 	if (!locale_str) {
1050 		locale_str = const_cast<char*>(intl_locale_get_default());
1051 	}
1052 
1053 	cal = Calendar::createInstance(timeZone,
1054 		Locale::createFromName(locale_str), status);
1055 	if (UNEXPECTED(cal == NULL)) {
1056 		delete timeZone;
1057 		intl_error_set(NULL, status, "intlcal_from_date_time: "
1058 				"error creating ICU Calendar object", 0);
1059 		goto error;
1060 	}
1061 	cal->setTime(((UDate)Z_LVAL(zv_timestamp)) * 1000., status);
1062     if (U_FAILURE(status)) {
1063 		/* time zone was adopted by cal; should not be deleted here */
1064 		delete cal;
1065 		intl_error_set(NULL, status, "intlcal_from_date_time: "
1066 				"error creating ICU Calendar::setTime()", 0);
1067         goto error;
1068     }
1069 
1070 	calendar_object_create(return_value, cal);
1071 
1072 error:
1073 	if (date_str) {
1074 		OBJ_RELEASE(date_obj);
1075 	}
1076 }
1077 
PHP_FUNCTION(intlcal_to_date_time)1078 U_CFUNC PHP_FUNCTION(intlcal_to_date_time)
1079 {
1080 	zval retval;
1081 	CALENDAR_METHOD_INIT_VARS;
1082 
1083 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1084 			&object, Calendar_ce_ptr) == FAILURE) {
1085 		RETURN_THROWS();
1086 	}
1087 
1088 	CALENDAR_METHOD_FETCH_OBJECT;
1089 
1090 	/* There are no exported functions in ext/date to this
1091 	 * in a more native fashion */
1092 	double	date = co->ucal->getTime(CALENDAR_ERROR_CODE(co)) / 1000.;
1093 	int64_t	ts;
1094 	char	ts_str[sizeof("@-9223372036854775808")];
1095 	int		ts_str_len;
1096 	zval	ts_zval, tmp;
1097 
1098 	INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
1099 
1100 	if (UNEXPECTED(date > (double)U_INT64_MAX || date < (double)U_INT64_MIN)) {
1101 		intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1102 			"intlcal_to_date_time: The calendar date is out of the "
1103 			"range for a 64-bit integer", 0);
1104 		RETURN_FALSE;
1105 	}
1106 
1107 	ZVAL_UNDEF(&retval);
1108 	ts = (int64_t)date;
1109 
1110 	ts_str_len = slprintf(ts_str, sizeof(ts_str), "@%" PRIi64, ts);
1111 	ZVAL_STRINGL(&ts_zval, ts_str, ts_str_len);
1112 
1113 	/* Now get the time zone */
1114 	const TimeZone& tz = co->ucal->getTimeZone();
1115 	zval *timezone_zval = timezone_convert_to_datetimezone(
1116 		&tz, CALENDAR_ERROR_P(co), "intlcal_to_date_time", &tmp);
1117 	if (timezone_zval == NULL) {
1118 		zval_ptr_dtor(&ts_zval);
1119 		RETURN_FALSE;
1120 	}
1121 
1122 	/* resources allocated from now on */
1123 
1124 	/* Finally, instantiate object and call constructor */
1125 	object_init_ex(return_value, php_date_get_date_ce());
1126 	zend_call_known_instance_method_with_2_params(
1127 		Z_OBJCE_P(return_value)->constructor, Z_OBJ_P(return_value), NULL, &ts_zval, timezone_zval);
1128 	if (EG(exception)) {
1129 		zend_object_store_ctor_failed(Z_OBJ_P(return_value));
1130 		zval_ptr_dtor(return_value);
1131 		zval_ptr_dtor(&ts_zval);
1132 
1133 		RETVAL_FALSE;
1134 		goto error;
1135 	}
1136 	zval_ptr_dtor(&ts_zval);
1137 
1138 	/* due to bug #40743, we have to set the time zone again */
1139 	zend_call_method_with_1_params(Z_OBJ_P(return_value), NULL, NULL, "settimezone",
1140 			&retval, timezone_zval);
1141 	if (Z_ISUNDEF(retval) || Z_TYPE(retval) == IS_FALSE) {
1142 		intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1143 			"intlcal_to_date_time: call to DateTime::setTimeZone has failed",
1144 			1);
1145 		zval_ptr_dtor(return_value);
1146 		RETVAL_FALSE;
1147 		goto error;
1148 	}
1149 
1150 error:
1151 	zval_ptr_dtor(timezone_zval);
1152 	zval_ptr_dtor(&retval);
1153 }
1154 
PHP_FUNCTION(intlcal_get_error_code)1155 U_CFUNC PHP_FUNCTION(intlcal_get_error_code)
1156 {
1157 	CALENDAR_METHOD_INIT_VARS;
1158 
1159 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1160 			&object, Calendar_ce_ptr) == FAILURE) {
1161 		RETURN_THROWS();
1162 	}
1163 
1164 	/* Fetch the object (without resetting its last error code ). */
1165 	co = Z_INTL_CALENDAR_P(object);
1166 	if (co == NULL)
1167 		RETURN_FALSE;
1168 
1169 	RETURN_LONG((zend_long)CALENDAR_ERROR_CODE(co));
1170 }
1171 
PHP_FUNCTION(intlcal_get_error_message)1172 U_CFUNC PHP_FUNCTION(intlcal_get_error_message)
1173 {
1174 	zend_string* message = NULL;
1175 	CALENDAR_METHOD_INIT_VARS;
1176 
1177 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1178 			&object, Calendar_ce_ptr) == FAILURE) {
1179 		RETURN_THROWS();
1180 	}
1181 
1182 
1183 	/* Fetch the object (without resetting its last error code ). */
1184 	co = Z_INTL_CALENDAR_P(object);
1185 	if (co == NULL)
1186 		RETURN_FALSE;
1187 
1188 	/* Return last error message. */
1189 	message = intl_error_get_message(CALENDAR_ERROR_P(co));
1190 	RETURN_STR(message);
1191 }
1192