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