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