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    zval    args_a[2] = {0},
526            *args       = &args_a[0];
527    zend_long   field;
528    int     variant;
529    CALENDAR_METHOD_INIT_VARS;
530
531    if (ZEND_NUM_ARGS() > (getThis() ? 1 : 2) ||
532            zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
533        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
534            "intlcal_clear: too many arguments", 0);
535        RETURN_FALSE;
536    }
537    if (!getThis()) {
538        args++;
539    }
540    if (Z_ISUNDEF(args[0]) || Z_TYPE(args[0]) == IS_NULL) {
541        zval *dummy; /* we know it's null */
542        if (zend_parse_method_parameters(ZEND_NUM_ARGS(),
543                getThis(), "O|z", &object, Calendar_ce_ptr, &dummy) == FAILURE) {
544            intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
545                "intlcal_clear: bad arguments", 0);
546            RETURN_FALSE;
547        }
548        variant = 0;
549    } else if (zend_parse_method_parameters(ZEND_NUM_ARGS(),
550            getThis(), "Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
551        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
552            "intlcal_clear: bad arguments", 0);
553        RETURN_FALSE;
554    } else if (field < 0 || field >= UCAL_FIELD_COUNT) {
555        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
556            "intlcal_clear: invalid field", 0);
557        RETURN_FALSE;
558    } else {
559        variant = 1;
560    }
561
562    CALENDAR_METHOD_FETCH_OBJECT;
563
564    if (variant == 0) {
565        co->ucal->clear();
566    } else {
567        co->ucal->clear((UCalendarDateFields)field);
568    }
569
570    RETURN_TRUE;
571}
572
573U_CFUNC PHP_FUNCTION(intlcal_field_difference)
574{
575    zend_long   field;
576    double  when;
577    CALENDAR_METHOD_INIT_VARS;
578
579    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
580            "Odl", &object, Calendar_ce_ptr, &when, &field) == FAILURE) {
581        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
582            "intlcal_field_difference: bad arguments", 0);
583        RETURN_FALSE;
584    }
585
586    if (field < 0 || field >= UCAL_FIELD_COUNT) {
587        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
588            "intlcal_field_difference: invalid field", 0);
589        RETURN_FALSE;
590    }
591
592    CALENDAR_METHOD_FETCH_OBJECT;
593
594    int32_t result = co->ucal->fieldDifference((UDate)when,
595        (UCalendarDateFields)field, CALENDAR_ERROR_CODE(co));
596    INTL_METHOD_CHECK_STATUS(co,
597        "intlcal_field_difference: Call to ICU method has failed");
598
599    RETURN_LONG((zend_long)result);
600}
601
602U_CFUNC PHP_FUNCTION(intlcal_get_actual_maximum)
603{
604    _php_intlcal_field_uec_ret_in32t_method(&Calendar::getActualMaximum,
605        "intlcal_get_actual_maximum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
606}
607
608U_CFUNC PHP_FUNCTION(intlcal_get_actual_minimum)
609{
610    _php_intlcal_field_uec_ret_in32t_method(&Calendar::getActualMinimum,
611        "intlcal_get_actual_minimum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
612}
613
614#if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM >= 44
615U_CFUNC PHP_FUNCTION(intlcal_get_day_of_week_type)
616{
617    zend_ulong  dow;
618    CALENDAR_METHOD_INIT_VARS;
619
620    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
621            "Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
622        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
623            "intlcal_get_day_of_week_type: bad arguments", 0);
624        RETURN_FALSE;
625    }
626
627    if (dow < UCAL_SUNDAY || dow > UCAL_SATURDAY) {
628        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
629            "intlcal_get_day_of_week_type: invalid day of week", 0);
630        RETURN_FALSE;
631    }
632
633    CALENDAR_METHOD_FETCH_OBJECT;
634
635    int32_t result = co->ucal->getDayOfWeekType(
636        (UCalendarDaysOfWeek)dow, CALENDAR_ERROR_CODE(co));
637    INTL_METHOD_CHECK_STATUS(co,
638        "intlcal_get_day_of_week_type: Call to ICU method has failed");
639
640    RETURN_LONG((zend_long)result);
641}
642#endif
643
644U_CFUNC PHP_FUNCTION(intlcal_get_first_day_of_week)
645{
646    CALENDAR_METHOD_INIT_VARS;
647
648    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
649            "O", &object, Calendar_ce_ptr) == FAILURE) {
650        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
651            "intlcal_get_first_day_of_week: bad arguments", 0);
652        RETURN_FALSE;
653    }
654
655    CALENDAR_METHOD_FETCH_OBJECT;
656
657    int32_t result = co->ucal->getFirstDayOfWeek(CALENDAR_ERROR_CODE(co));
658    INTL_METHOD_CHECK_STATUS(co,
659        "intlcal_get_first_day_of_week: Call to ICU method has failed");
660
661    RETURN_LONG((zend_long)result);
662}
663
664static void _php_intlcal_field_ret_in32t_method(
665        int32_t (Calendar::*func)(UCalendarDateFields) const,
666        const char *method_name,
667        INTERNAL_FUNCTION_PARAMETERS)
668{
669    zend_long   field;
670    char    *message;
671    CALENDAR_METHOD_INIT_VARS;
672
673    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
674            "Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
675        spprintf(&message, 0, "%s: bad arguments", method_name);
676        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, message, 1);
677        efree(message);
678        RETURN_FALSE;
679    }
680
681    if (field < 0 || field >= UCAL_FIELD_COUNT) {
682        spprintf(&message, 0, "%s: invalid field", method_name);
683        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, message, 1);
684        efree(message);
685        RETURN_FALSE;
686    }
687
688    CALENDAR_METHOD_FETCH_OBJECT;
689
690    int32_t result = (co->ucal->*func)((UCalendarDateFields)field);
691    INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
692
693    RETURN_LONG((zend_long)result);
694}
695
696U_CFUNC PHP_FUNCTION(intlcal_get_greatest_minimum)
697{
698    _php_intlcal_field_ret_in32t_method(&Calendar::getGreatestMinimum,
699        "intlcal_get_greatest_minimum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
700}
701
702U_CFUNC PHP_FUNCTION(intlcal_get_least_maximum)
703{
704    _php_intlcal_field_ret_in32t_method(&Calendar::getLeastMaximum,
705        "intlcal_get_least_maximum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
706}
707
708U_CFUNC PHP_FUNCTION(intlcal_get_locale)
709{
710    zend_long   locale_type;
711    CALENDAR_METHOD_INIT_VARS;
712
713    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
714            "Ol", &object, Calendar_ce_ptr, &locale_type) == FAILURE) {
715        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
716            "intlcal_get_locale: bad arguments", 0);
717        RETURN_FALSE;
718    }
719
720    if (locale_type != ULOC_ACTUAL_LOCALE && locale_type != ULOC_VALID_LOCALE) {
721        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
722            "intlcal_get_locale: invalid locale type", 0);
723        RETURN_FALSE;
724    }
725
726    CALENDAR_METHOD_FETCH_OBJECT;
727
728    Locale locale = co->ucal->getLocale((ULocDataLocaleType)locale_type,
729        CALENDAR_ERROR_CODE(co));
730    INTL_METHOD_CHECK_STATUS(co,
731        "intlcal_get_locale: Call to ICU method has failed");
732
733    RETURN_STRING(locale.getName());
734}
735
736U_CFUNC PHP_FUNCTION(intlcal_get_maximum)
737{
738    _php_intlcal_field_ret_in32t_method(&Calendar::getMaximum,
739        "intlcal_get_maximum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
740}
741
742U_CFUNC PHP_FUNCTION(intlcal_get_minimal_days_in_first_week)
743{
744    CALENDAR_METHOD_INIT_VARS;
745
746    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
747            "O", &object, Calendar_ce_ptr) == FAILURE) {
748        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
749            "intlcal_get_minimal_days_in_first_week: bad arguments", 0);
750        RETURN_FALSE;
751    }
752
753    CALENDAR_METHOD_FETCH_OBJECT;
754
755    uint8_t result = co->ucal->getMinimalDaysInFirstWeek();
756    INTL_METHOD_CHECK_STATUS(co,
757        "intlcal_get_first_day_of_week: Call to ICU method has failed");
758
759    RETURN_LONG((zend_long)result);
760}
761
762U_CFUNC PHP_FUNCTION(intlcal_get_minimum)
763{
764    _php_intlcal_field_ret_in32t_method(&Calendar::getMinimum,
765        "intlcal_get_minimum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
766}
767
768U_CFUNC PHP_FUNCTION(intlcal_get_time_zone)
769{
770    CALENDAR_METHOD_INIT_VARS;
771
772    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
773            "O", &object, Calendar_ce_ptr) == FAILURE) {
774        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
775            "intlcal_get_time_zone: bad arguments", 0);
776        RETURN_FALSE;
777    }
778
779    CALENDAR_METHOD_FETCH_OBJECT;
780
781    TimeZone *tz = co->ucal->getTimeZone().clone();
782    if (tz == NULL) {
783        intl_errors_set(CALENDAR_ERROR_P(co), U_MEMORY_ALLOCATION_ERROR,
784            "intlcal_get_time_zone: could not clone TimeZone", 0);
785        RETURN_FALSE;
786    }
787
788    timezone_object_construct(tz, return_value, 1);
789}
790
791U_CFUNC PHP_FUNCTION(intlcal_get_type)
792{
793    CALENDAR_METHOD_INIT_VARS;
794
795    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
796            "O", &object, Calendar_ce_ptr) == FAILURE) {
797        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
798            "intlcal_get_type: bad arguments", 0);
799        RETURN_FALSE;
800    }
801
802    CALENDAR_METHOD_FETCH_OBJECT;
803
804    RETURN_STRING(co->ucal->getType());
805}
806
807#if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM >= 44
808U_CFUNC PHP_FUNCTION(intlcal_get_weekend_transition)
809{
810    zend_long   dow;
811    CALENDAR_METHOD_INIT_VARS;
812
813    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
814            "Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
815        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
816            "intlcal_get_weekend_transition: bad arguments", 0);
817        RETURN_FALSE;
818    }
819
820    if (dow < UCAL_SUNDAY || dow > UCAL_SATURDAY) {
821        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
822            "intlcal_get_weekend_transition: invalid day of week", 0);
823        RETURN_FALSE;
824    }
825
826    CALENDAR_METHOD_FETCH_OBJECT;
827
828    int32_t res = co->ucal->getWeekendTransition((UCalendarDaysOfWeek)dow,
829        CALENDAR_ERROR_CODE(co));
830    INTL_METHOD_CHECK_STATUS(co, "intlcal_get_weekend_transition: "
831        "Error calling ICU method");
832
833    RETURN_LONG((zend_long)res);
834}
835#endif
836
837U_CFUNC PHP_FUNCTION(intlcal_in_daylight_time)
838{
839    CALENDAR_METHOD_INIT_VARS;
840
841    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
842            "O", &object, Calendar_ce_ptr) == FAILURE) {
843        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
844            "intlcal_in_daylight_time: bad arguments", 0);
845        RETURN_FALSE;
846    }
847
848    CALENDAR_METHOD_FETCH_OBJECT;
849
850    UBool ret = co->ucal->inDaylightTime(CALENDAR_ERROR_CODE(co));
851    INTL_METHOD_CHECK_STATUS(co, "intlcal_in_daylight_time: "
852        "Error calling ICU method");
853
854    RETURN_BOOL((int)ret);
855}
856
857U_CFUNC PHP_FUNCTION(intlcal_is_equivalent_to)
858{
859    zval            *other_object;
860    Calendar_object *other_co;
861    CALENDAR_METHOD_INIT_VARS;
862
863    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
864            "OO", &object, Calendar_ce_ptr, &other_object, Calendar_ce_ptr)
865            == FAILURE) {
866        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
867            "intlcal_is_equivalent_to: bad arguments", 0);
868        RETURN_FALSE;
869    }
870
871    other_co = Z_INTL_CALENDAR_P(other_object);
872    if (other_co->ucal == NULL) {
873        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "intlcal_is_equivalent_to:"
874            " Other IntlCalendar is unconstructed", 0);
875        RETURN_FALSE;
876    }
877
878    CALENDAR_METHOD_FETCH_OBJECT;
879
880    RETURN_BOOL((int)co->ucal->isEquivalentTo(*other_co->ucal));
881}
882
883U_CFUNC PHP_FUNCTION(intlcal_is_lenient)
884{
885    CALENDAR_METHOD_INIT_VARS;
886
887    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
888            "O", &object, Calendar_ce_ptr) == FAILURE) {
889        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
890            "intlcal_is_lenient: bad arguments", 0);
891        RETURN_FALSE;
892    }
893
894    CALENDAR_METHOD_FETCH_OBJECT;
895
896    RETURN_BOOL((int)co->ucal->isLenient());
897}
898
899U_CFUNC PHP_FUNCTION(intlcal_is_set)
900{
901    zend_long field;
902    CALENDAR_METHOD_INIT_VARS;
903
904    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
905            "Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
906        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
907            "intlcal_is_set: bad arguments", 0);
908        RETURN_FALSE;
909    }
910
911    if (field < 0 || field >= UCAL_FIELD_COUNT) {
912        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
913            "intlcal_is_set: invalid field", 0);
914        RETURN_FALSE;
915    }
916
917    CALENDAR_METHOD_FETCH_OBJECT;
918
919    RETURN_BOOL((int)co->ucal->isSet((UCalendarDateFields)field));
920}
921
922#if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM >= 44
923U_CFUNC PHP_FUNCTION(intlcal_is_weekend)
924{
925    double date;
926    zval *rawDate = NULL;
927    CALENDAR_METHOD_INIT_VARS;
928
929    if (zend_parse_method_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
930            ZEND_NUM_ARGS(), getThis(),
931            "O|z!", &object, Calendar_ce_ptr, &rawDate) == FAILURE
932            || (rawDate != NULL &&
933                zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
934                "O|d", &object, Calendar_ce_ptr, &date) == FAILURE)) {
935        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
936            "intlcal_is_weekend: bad arguments", 0);
937        RETURN_FALSE;
938    }
939
940    CALENDAR_METHOD_FETCH_OBJECT;
941
942    if (rawDate == NULL) {
943        RETURN_BOOL((int)co->ucal->isWeekend());
944    } else {
945        UBool ret = co->ucal->isWeekend((UDate)date, CALENDAR_ERROR_CODE(co));
946        INTL_METHOD_CHECK_STATUS(co, "intlcal_is_weekend: "
947            "Error calling ICU method");
948        RETURN_BOOL((int)ret);
949    }
950}
951#endif
952
953
954U_CFUNC PHP_FUNCTION(intlcal_set_first_day_of_week)
955{
956    zend_long   dow;
957    CALENDAR_METHOD_INIT_VARS;
958
959    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
960            "Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
961        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
962            "intlcal_set_first_day_of_week: bad arguments", 0);
963        RETURN_FALSE;
964    }
965
966    if (dow < UCAL_SUNDAY || dow > UCAL_SATURDAY) {
967        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
968            "intlcal_set_first_day_of_week: invalid day of week", 0);
969        RETURN_FALSE;
970    }
971
972    CALENDAR_METHOD_FETCH_OBJECT;
973
974    co->ucal->setFirstDayOfWeek((UCalendarDaysOfWeek)dow);
975
976    RETURN_TRUE;
977}
978
979U_CFUNC PHP_FUNCTION(intlcal_set_lenient)
980{
981    zend_bool is_lenient;
982    CALENDAR_METHOD_INIT_VARS;
983
984    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
985            "Ob", &object, Calendar_ce_ptr, &is_lenient) == FAILURE) {
986        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
987            "intlcal_set_lenient: bad arguments", 0);
988        RETURN_FALSE;
989    }
990
991    CALENDAR_METHOD_FETCH_OBJECT;
992
993    co->ucal->setLenient((UBool) is_lenient);
994
995    RETURN_TRUE;
996}
997
998U_CFUNC PHP_FUNCTION(intlcal_set_minimal_days_in_first_week)
999{
1000    zend_long   num_days;
1001    CALENDAR_METHOD_INIT_VARS;
1002
1003    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1004            "Ol", &object, Calendar_ce_ptr, &num_days) == FAILURE) {
1005        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1006            "intlcal_set_minimal_days_in_first_week: bad arguments", 0);
1007        RETURN_FALSE;
1008    }
1009
1010    if (num_days < 1 || num_days > 7) {
1011        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1012            "intlcal_set_minimal_days_in_first_week: invalid number of days; "
1013            "must be between 1 and 7", 0);
1014        RETURN_FALSE;
1015    }
1016
1017    CALENDAR_METHOD_FETCH_OBJECT;
1018
1019    co->ucal->setMinimalDaysInFirstWeek((uint8_t)num_days);
1020
1021    RETURN_TRUE;
1022}
1023
1024U_CFUNC PHP_FUNCTION(intlcal_equals)
1025{
1026    zval            *other_object;
1027    Calendar_object *other_co;
1028    CALENDAR_METHOD_INIT_VARS;
1029
1030    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1031            "OO", &object, Calendar_ce_ptr, &other_object, Calendar_ce_ptr)
1032            == FAILURE) {
1033        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1034            "intlcal_equals: bad arguments", 0);
1035        RETURN_FALSE;
1036    }
1037
1038    CALENDAR_METHOD_FETCH_OBJECT;
1039    other_co = Z_INTL_CALENDAR_P(other_object);
1040    if (other_co->ucal == NULL) {
1041        intl_errors_set(&co->err, U_ILLEGAL_ARGUMENT_ERROR,
1042            "intlcal_equals: The second IntlCalendar is unconstructed", 0);
1043        RETURN_FALSE;
1044    }
1045
1046    UBool result = co->ucal->equals(*other_co->ucal, CALENDAR_ERROR_CODE(co));
1047    INTL_METHOD_CHECK_STATUS(co, "intlcal_equals: error calling ICU Calendar::equals");
1048
1049    RETURN_BOOL((int)result);
1050}
1051
1052#if U_ICU_VERSION_MAJOR_NUM >= 49
1053
1054U_CFUNC PHP_FUNCTION(intlcal_get_repeated_wall_time_option)
1055{
1056    CALENDAR_METHOD_INIT_VARS;
1057
1058    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1059            "O", &object, Calendar_ce_ptr) == FAILURE) {
1060        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1061            "intlcal_get_repeated_wall_time_option: bad arguments", 0);
1062        RETURN_FALSE;
1063    }
1064
1065    CALENDAR_METHOD_FETCH_OBJECT;
1066
1067    RETURN_LONG(co->ucal->getRepeatedWallTimeOption());
1068}
1069
1070U_CFUNC PHP_FUNCTION(intlcal_get_skipped_wall_time_option)
1071{
1072    CALENDAR_METHOD_INIT_VARS;
1073
1074    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1075            "O", &object, Calendar_ce_ptr) == FAILURE) {
1076        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1077            "intlcal_get_skipped_wall_time_option: bad arguments", 0);
1078        RETURN_FALSE;
1079    }
1080
1081    CALENDAR_METHOD_FETCH_OBJECT;
1082
1083    RETURN_LONG(co->ucal->getSkippedWallTimeOption());
1084}
1085
1086U_CFUNC PHP_FUNCTION(intlcal_set_repeated_wall_time_option)
1087{
1088    zend_long   option;
1089    CALENDAR_METHOD_INIT_VARS;
1090
1091    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1092            "Ol", &object, Calendar_ce_ptr, &option) == FAILURE) {
1093        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1094            "intlcal_set_repeated_wall_time_option: bad arguments", 0);
1095        RETURN_FALSE;
1096    }
1097
1098    if (option != UCAL_WALLTIME_FIRST && option != UCAL_WALLTIME_LAST) {
1099        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1100            "intlcal_set_repeated_wall_time_option: invalid option", 0);
1101        RETURN_FALSE;
1102    }
1103
1104    CALENDAR_METHOD_FETCH_OBJECT;
1105
1106    co->ucal->setRepeatedWallTimeOption((UCalendarWallTimeOption)option);
1107
1108    RETURN_TRUE;
1109}
1110
1111U_CFUNC PHP_FUNCTION(intlcal_set_skipped_wall_time_option)
1112{
1113    zend_long   option;
1114    CALENDAR_METHOD_INIT_VARS;
1115
1116    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1117            "Ol", &object, Calendar_ce_ptr, &option) == FAILURE) {
1118        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1119            "intlcal_set_skipped_wall_time_option: bad arguments", 0);
1120        RETURN_FALSE;
1121    }
1122
1123    if (option != UCAL_WALLTIME_FIRST && option != UCAL_WALLTIME_LAST
1124            && option != UCAL_WALLTIME_NEXT_VALID) {
1125        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1126            "intlcal_set_skipped_wall_time_option: invalid option", 0);
1127        RETURN_FALSE;
1128    }
1129
1130    CALENDAR_METHOD_FETCH_OBJECT;
1131
1132    co->ucal->setSkippedWallTimeOption((UCalendarWallTimeOption)option);
1133
1134    RETURN_TRUE;
1135}
1136
1137#endif
1138
1139U_CFUNC PHP_FUNCTION(intlcal_from_date_time)
1140{
1141    zval            *zv_arg,
1142                    zv_tmp,
1143                    *zv_datetime        = NULL,
1144                    zv_timestamp;
1145    php_date_obj    *datetime;
1146    char            *locale_str         = NULL;
1147    size_t              locale_str_len;
1148    TimeZone        *timeZone;
1149    UErrorCode      status              = U_ZERO_ERROR;
1150    Calendar        *cal;
1151    intl_error_reset(NULL);
1152
1153    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s!",
1154            &zv_arg, &locale_str, &locale_str_len) == FAILURE) {
1155        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1156            "intlcal_from_date_time: bad arguments", 0);
1157        RETURN_NULL();
1158    }
1159
1160    if (!(Z_TYPE_P(zv_arg) == IS_OBJECT && instanceof_function(
1161            Z_OBJCE_P(zv_arg), php_date_get_date_ce()))) {
1162        object_init_ex(&zv_tmp, php_date_get_date_ce());
1163        zend_call_method_with_1_params(&zv_tmp, NULL, NULL, "__construct", NULL, zv_arg);
1164        if (EG(exception)) {
1165            zend_object_store_ctor_failed(Z_OBJ(zv_tmp));
1166            goto error;
1167        }
1168        zv_datetime = &zv_tmp;
1169    } else {
1170        zv_datetime = zv_arg;
1171    }
1172
1173    datetime = Z_PHPDATE_P(zv_datetime);
1174    if (!datetime->time) {
1175        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1176            "intlcal_from_date_time: DateTime object is unconstructed",
1177            0);
1178        goto error;
1179    }
1180
1181    zend_call_method_with_0_params(zv_datetime, php_date_get_date_ce(), NULL, "gettimestamp", &zv_timestamp);
1182    if (Z_TYPE(zv_timestamp) != IS_LONG) {
1183        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1184            "intlcal_from_date_time: bad DateTime; call to "
1185            "DateTime::getTimestamp() failed", 0);
1186        zval_ptr_dtor(&zv_timestamp);
1187        goto error;
1188    }
1189
1190    if (!datetime->time->is_localtime) {
1191        timeZone = TimeZone::getGMT()->clone();
1192    } else {
1193        timeZone = timezone_convert_datetimezone(datetime->time->zone_type,
1194            datetime, 1, NULL, "intlcal_from_date_time");
1195        if (timeZone == NULL) {
1196            goto error;
1197        }
1198    }
1199
1200    if (!locale_str) {
1201        locale_str = const_cast<char*>(intl_locale_get_default());
1202    }
1203
1204    cal = Calendar::createInstance(timeZone,
1205        Locale::createFromName(locale_str), status);
1206    if (cal == NULL) {
1207        delete timeZone;
1208        intl_error_set(NULL, status, "intlcal_from_date_time: "
1209                "error creating ICU Calendar object", 0);
1210        goto error;
1211    }
1212    cal->setTime(((UDate)Z_LVAL(zv_timestamp)) * 1000., status);
1213    if (U_FAILURE(status)) {
1214        /* time zone was adopted by cal; should not be deleted here */
1215        delete cal;
1216        intl_error_set(NULL, status, "intlcal_from_date_time: "
1217                "error creating ICU Calendar::setTime()", 0);
1218        goto error;
1219    }
1220
1221    calendar_object_create(return_value, cal);
1222
1223error:
1224    if (zv_datetime && zv_datetime != zv_arg) {
1225        zval_ptr_dtor(zv_datetime);
1226    }
1227}
1228
1229U_CFUNC PHP_FUNCTION(intlcal_to_date_time)
1230{
1231    zval retval;
1232    CALENDAR_METHOD_INIT_VARS;
1233
1234    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1235            &object, Calendar_ce_ptr) == FAILURE) {
1236        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1237            "intlcal_to_date_time: bad arguments", 0);
1238        RETURN_FALSE;
1239    }
1240
1241    CALENDAR_METHOD_FETCH_OBJECT;
1242
1243    /* There are no exported functions in ext/date to this
1244     * in a more native fashion */
1245    double  date = co->ucal->getTime(CALENDAR_ERROR_CODE(co)) / 1000.;
1246    int64_t ts;
1247    char    ts_str[sizeof("@-9223372036854775808")];
1248    int     ts_str_len;
1249    zval    ts_tmp, ts_zval, tmp;
1250
1251    INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
1252
1253    if (date > (double)U_INT64_MAX || date < (double)U_INT64_MIN) {
1254        intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1255            "intlcal_to_date_time: The calendar date is out of the "
1256            "range for a 64-bit integer", 0);
1257        RETURN_FALSE;
1258    }
1259
1260    ZVAL_UNDEF(&retval);
1261    ts = (int64_t)date;
1262
1263    ts_str_len = slprintf(ts_str, sizeof(ts_str), "@%I64d", ts);
1264    ZVAL_STRINGL(&ts_zval, ts_str, ts_str_len);
1265
1266    /* Now get the time zone */
1267    const TimeZone& tz = co->ucal->getTimeZone();
1268    zval *timezone_zval = timezone_convert_to_datetimezone(
1269        &tz, CALENDAR_ERROR_P(co), "intlcal_to_date_time", &tmp);
1270    if (timezone_zval == NULL) {
1271        RETURN_FALSE;
1272    }
1273
1274    /* resources allocated from now on */
1275
1276    /* Finally, instantiate object and call constructor */
1277    object_init_ex(return_value, php_date_get_date_ce());
1278    zend_call_method_with_2_params(return_value, NULL, NULL, "__construct", NULL, &ts_zval, timezone_zval);
1279    if (EG(exception)) {
1280        intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1281            "intlcal_to_date_time: DateTime constructor has thrown exception",
1282            1);
1283        zend_object_store_ctor_failed(Z_OBJ_P(return_value));
1284        zval_ptr_dtor(return_value);
1285        zval_ptr_dtor(&ts_zval);
1286
1287        RETVAL_FALSE;
1288        goto error;
1289    }
1290    zval_ptr_dtor(&ts_zval);
1291
1292    /* due to bug #40743, we have to set the time zone again */
1293    zend_call_method_with_1_params(return_value, NULL, NULL, "settimezone",
1294            &retval, timezone_zval);
1295    if (Z_ISUNDEF(retval) || Z_TYPE(retval) == IS_FALSE) {
1296        intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1297            "intlcal_to_date_time: call to DateTime::setTimeZone has failed",
1298            1);
1299        zval_ptr_dtor(return_value);
1300        RETVAL_FALSE;
1301        goto error;
1302    }
1303
1304error:
1305    zval_ptr_dtor(timezone_zval);
1306    zval_ptr_dtor(&retval);
1307}
1308
1309U_CFUNC PHP_FUNCTION(intlcal_get_error_code)
1310{
1311    CALENDAR_METHOD_INIT_VARS;
1312
1313    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1314            &object, Calendar_ce_ptr) == FAILURE) {
1315        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1316            "intlcal_get_error_code: bad arguments", 0);
1317        RETURN_FALSE;
1318    }
1319
1320    /* Fetch the object (without resetting its last error code ). */
1321    co = Z_INTL_CALENDAR_P(object);
1322    if (co == NULL)
1323        RETURN_FALSE;
1324
1325    RETURN_LONG((zend_long)CALENDAR_ERROR_CODE(co));
1326}
1327
1328U_CFUNC PHP_FUNCTION(intlcal_get_error_message)
1329{
1330    zend_string* message = NULL;
1331    CALENDAR_METHOD_INIT_VARS;
1332
1333    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1334            &object, Calendar_ce_ptr) == FAILURE) {
1335        intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
1336            "intlcal_get_error_message: bad arguments", 0 );
1337        RETURN_FALSE;
1338    }
1339
1340
1341    /* Fetch the object (without resetting its last error code ). */
1342    co = Z_INTL_CALENDAR_P(object);
1343    if (co == NULL)
1344        RETURN_FALSE;
1345
1346    /* Return last error message. */
1347    message = intl_error_get_message(CALENDAR_ERROR_P(co));
1348    RETURN_STR(message);
1349}
1350