1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
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 TSRMLS_CC );
48}
49
50U_CFUNC PHP_FUNCTION(intlcal_create_instance)
51{
52    zval        **zv_timezone   = NULL;
53    const char  *locale_str     = NULL;
54    int         dummy;
55    TimeZone    *timeZone;
56    UErrorCode  status          = U_ZERO_ERROR;
57    intl_error_reset(NULL TSRMLS_CC);
58
59    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|Zs!",
60            &zv_timezone, &locale_str, &dummy) == FAILURE) {
61        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
62            "intlcal_create_calendar: bad arguments", 0 TSRMLS_CC);
63        RETURN_NULL();
64    }
65
66    timeZone = timezone_process_timezone_argument(zv_timezone, NULL,
67        "intlcal_create_instance" TSRMLS_CC);
68    if (timeZone == NULL) {
69        RETURN_NULL();
70    }
71
72    if (!locale_str) {
73        locale_str = intl_locale_get_default(TSRMLS_C);
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 TSRMLS_CC);
81        RETURN_NULL();
82    }
83
84    calendar_object_create(return_value, cal TSRMLS_CC);
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    int         key_len,
147                locale_len;
148    zend_bool   commonly_used;
149    intl_error_reset(NULL TSRMLS_CC);
150
151    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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 TSRMLS_CC);
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 TSRMLS_CC);
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 TSRMLS_CC);
175        RETURN_FALSE;
176    }
177
178    StringEnumeration *se = new BugStringCharEnumeration(uenum);
179#endif
180
181    IntlIterator_from_StringEnumeration(se, return_value TSRMLS_CC);
182}
183#endif //ICU 4.2 only
184
185U_CFUNC PHP_FUNCTION(intlcal_get_now)
186{
187    intl_error_reset(NULL TSRMLS_CC);
188
189    if (zend_parse_parameters_none() == FAILURE) {
190        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
191            "intlcal_get_now: bad arguments", 0 TSRMLS_CC);
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 TSRMLS_CC);
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 TSRMLS_CC);
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(), 1);
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    long    field;
223    char    *message;
224    CALENDAR_METHOD_INIT_VARS;
225
226    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, 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 TSRMLS_CC);
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 TSRMLS_CC);
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((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() TSRMLS_CC, getThis(), "O",
261            &object, Calendar_ce_ptr) == FAILURE) {
262        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
263            "intlcal_get_time: bad arguments", 0 TSRMLS_CC);
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() TSRMLS_CC, 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 TSRMLS_CC);
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    long    field,
299            amount;
300    CALENDAR_METHOD_INIT_VARS;
301
302    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, 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 TSRMLS_CC);
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 TSRMLS_CC);
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 TSRMLS_CC);
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() TSRMLS_CC, 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 TSRMLS_CC);
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" TSRMLS_CC);
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() TSRMLS_CC, 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 TSRMLS_CC);
372        RETURN_FALSE;
373    }
374
375    CALENDAR_METHOD_FETCH_OBJECT;
376
377    when_co = static_cast<Calendar_object*>(
378        zend_object_store_get_object(when_object TSRMLS_CC));
379    if (when_co->ucal == NULL) {
380        intl_errors_set(&co->err, U_ILLEGAL_ARGUMENT_ERROR,
381            "intlcal_before/after: Other IntlCalendar was unconstructed", 0 TSRMLS_CC);
382        RETURN_FALSE;
383    }
384
385    UBool res = (co->ucal->*func)(*when_co->ucal, CALENDAR_ERROR_CODE(co));
386    INTL_METHOD_CHECK_STATUS(co, "intlcal_before/after: Error calling ICU method");
387
388    RETURN_BOOL((int)res);
389}
390
391U_CFUNC PHP_FUNCTION(intlcal_after)
392{
393    _php_intlcal_before_after(&Calendar::after, INTERNAL_FUNCTION_PARAM_PASSTHRU);
394}
395
396U_CFUNC PHP_FUNCTION(intlcal_before)
397{
398    _php_intlcal_before_after(&Calendar::before, INTERNAL_FUNCTION_PARAM_PASSTHRU);
399}
400
401U_CFUNC PHP_FUNCTION(intlcal_set)
402{
403    long    arg1, arg2, arg3, arg4, arg5, arg6;
404    zval    **args_a[7] = {0},
405            ***args     = &args_a[0];
406    int     i;
407    int     variant; /* number of args of the set() overload */
408    CALENDAR_METHOD_INIT_VARS;
409
410    /* must come before zpp because zpp would convert the args in the stack to 0 */
411    if (ZEND_NUM_ARGS() > (getThis() ? 6 : 7) ||
412                zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
413        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
414            "intlcal_set: too many arguments", 0 TSRMLS_CC);
415        RETURN_FALSE;
416    }
417    if (!getThis()) {
418        args++;
419    }
420    variant = ZEND_NUM_ARGS() - (getThis() ? 0 : 1);
421    while (variant > 2 && Z_TYPE_PP(args[variant - 1]) == IS_NULL) {
422        variant--;
423    }
424
425    if (variant == 4 ||
426            zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
427            "Oll|llll", &object, Calendar_ce_ptr, &arg1, &arg2, &arg3, &arg4,
428            &arg5, &arg6) == FAILURE) {
429        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
430            "intlcal_set: bad arguments", 0 TSRMLS_CC);
431        RETURN_FALSE;
432    }
433
434    for (i = 0; i < variant; i++) {
435        if (Z_LVAL_PP(args[i]) < INT32_MIN || Z_LVAL_PP(args[i]) > INT32_MAX) {
436            intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
437                "intlcal_set: at least one of the arguments has an absolute "
438                "value that is too large", 0 TSRMLS_CC);
439            RETURN_FALSE;
440        }
441    }
442
443    if (variant == 2 && (arg1 < 0 || arg1 >= UCAL_FIELD_COUNT)) {
444        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
445            "intlcal_set: invalid field", 0 TSRMLS_CC);
446        RETURN_FALSE;
447    }
448
449    CALENDAR_METHOD_FETCH_OBJECT;
450
451    if (variant == 2) {
452        co->ucal->set((UCalendarDateFields)arg1, (int32_t)arg2);
453    } else if (variant == 3) {
454        co->ucal->set((int32_t)arg1, (int32_t)arg2, (int32_t)arg3);
455    } else if (variant == 5) {
456        co->ucal->set((int32_t)arg1, (int32_t)arg2, (int32_t)arg3, (int32_t)arg4, (int32_t)arg5);
457    } else if (variant == 6) {
458        co->ucal->set((int32_t)arg1, (int32_t)arg2, (int32_t)arg3, (int32_t)arg4, (int32_t)arg5, (int32_t)arg6);
459    }
460
461    RETURN_TRUE;
462}
463
464U_CFUNC PHP_FUNCTION(intlcal_roll)
465{
466    long        field,
467                value;
468    zval        **args_a[3]      = {0},
469                ***args          = &args_a[0];
470    zend_bool   bool_variant_val = (zend_bool)-1;
471    CALENDAR_METHOD_INIT_VARS;
472
473    if (ZEND_NUM_ARGS() > (getThis() ? 2 :3) ||
474            zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
475        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
476            "intlcal_set: too many arguments", 0 TSRMLS_CC);
477        RETURN_FALSE;
478    }
479    if (!getThis()) {
480        args++;
481    }
482    if (args[1] != NULL && Z_TYPE_PP(args[1]) == IS_BOOL) {
483        if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
484                "Olb", &object, Calendar_ce_ptr, &field, &bool_variant_val)
485                == FAILURE) {
486            intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
487                "intlcal_roll: bad arguments", 0 TSRMLS_CC);
488            RETURN_FALSE;
489        }
490        bool_variant_val = Z_BVAL_PP(args[1]);
491    } else if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
492            "Oll", &object, Calendar_ce_ptr, &field, &value) == FAILURE) {
493        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
494            "intlcal_roll: bad arguments", 0 TSRMLS_CC);
495        RETURN_FALSE;
496    }
497
498    if (field < 0 || field >= UCAL_FIELD_COUNT) {
499        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
500            "intlcal_roll: invalid field", 0 TSRMLS_CC);
501        RETURN_FALSE;
502    }
503    if (bool_variant_val == (zend_bool)-1 &&
504            (value < INT32_MIN || value > INT32_MAX)) {
505        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
506            "intlcal_roll: value out of bounds", 0 TSRMLS_CC);
507        RETURN_FALSE;
508    }
509
510    CALENDAR_METHOD_FETCH_OBJECT;
511
512    if (bool_variant_val != (zend_bool)-1) {
513        co->ucal->roll((UCalendarDateFields)field, (UBool)bool_variant_val,
514            CALENDAR_ERROR_CODE(co));
515    } else {
516        co->ucal->roll((UCalendarDateFields)field, (int32_t)value,
517            CALENDAR_ERROR_CODE(co));
518    }
519    INTL_METHOD_CHECK_STATUS(co, "intlcal_roll: Error calling ICU Calendar::roll");
520
521    RETURN_TRUE;
522}
523
524U_CFUNC PHP_FUNCTION(intlcal_clear)
525{
526    zval    **args_a[2] = {0},
527            ***args     = &args_a[0];
528    long    field;
529    int     variant;
530    CALENDAR_METHOD_INIT_VARS;
531
532    if (ZEND_NUM_ARGS() > (getThis() ? 1 : 2) ||
533            zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
534        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
535            "intlcal_clear: too many arguments", 0 TSRMLS_CC);
536        RETURN_FALSE;
537    }
538    if (!getThis()) {
539        args++;
540    }
541    if (args[0] == NULL || Z_TYPE_PP(args[0]) == IS_NULL) {
542        zval *dummy; /* we know it's null */
543        if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
544                getThis(), "O|z", &object, Calendar_ce_ptr, &dummy) == FAILURE) {
545            intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
546                "intlcal_clear: bad arguments", 0 TSRMLS_CC);
547            RETURN_FALSE;
548        }
549        variant = 0;
550    } else if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
551            getThis(), "Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
552        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
553            "intlcal_clear: bad arguments", 0 TSRMLS_CC);
554        RETURN_FALSE;
555    } else if (field < 0 || field >= UCAL_FIELD_COUNT) {
556        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
557            "intlcal_clear: invalid field", 0 TSRMLS_CC);
558        RETURN_FALSE;
559    } else {
560        variant = 1;
561    }
562
563    CALENDAR_METHOD_FETCH_OBJECT;
564
565    if (variant == 0) {
566        co->ucal->clear();
567    } else {
568        co->ucal->clear((UCalendarDateFields)field);
569    }
570
571    RETURN_TRUE;
572}
573
574U_CFUNC PHP_FUNCTION(intlcal_field_difference)
575{
576    long    field;
577    double  when;
578    CALENDAR_METHOD_INIT_VARS;
579
580    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
581            "Odl", &object, Calendar_ce_ptr, &when, &field) == FAILURE) {
582        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
583            "intlcal_field_difference: bad arguments", 0 TSRMLS_CC);
584        RETURN_FALSE;
585    }
586
587    if (field < 0 || field >= UCAL_FIELD_COUNT) {
588        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
589            "intlcal_field_difference: invalid field", 0 TSRMLS_CC);
590        RETURN_FALSE;
591    }
592
593    CALENDAR_METHOD_FETCH_OBJECT;
594
595    int32_t result = co->ucal->fieldDifference((UDate)when,
596        (UCalendarDateFields)field, CALENDAR_ERROR_CODE(co));
597    INTL_METHOD_CHECK_STATUS(co,
598        "intlcal_field_difference: Call to ICU method has failed");
599
600    RETURN_LONG((long)result);
601}
602
603U_CFUNC PHP_FUNCTION(intlcal_get_actual_maximum)
604{
605    _php_intlcal_field_uec_ret_in32t_method(&Calendar::getActualMaximum,
606        "intlcal_get_actual_maximum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
607}
608
609U_CFUNC PHP_FUNCTION(intlcal_get_actual_minimum)
610{
611    _php_intlcal_field_uec_ret_in32t_method(&Calendar::getActualMinimum,
612        "intlcal_get_actual_minimum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
613}
614
615#if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM >= 44
616U_CFUNC PHP_FUNCTION(intlcal_get_day_of_week_type)
617{
618    long    dow;
619    CALENDAR_METHOD_INIT_VARS;
620
621    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
622            "Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
623        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
624            "intlcal_get_day_of_week_type: bad arguments", 0 TSRMLS_CC);
625        RETURN_FALSE;
626    }
627
628    if (dow < UCAL_SUNDAY || dow > UCAL_SATURDAY) {
629        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
630            "intlcal_get_day_of_week_type: invalid day of week", 0 TSRMLS_CC);
631        RETURN_FALSE;
632    }
633
634    CALENDAR_METHOD_FETCH_OBJECT;
635
636    int32_t result = co->ucal->getDayOfWeekType(
637        (UCalendarDaysOfWeek)dow, CALENDAR_ERROR_CODE(co));
638    INTL_METHOD_CHECK_STATUS(co,
639        "intlcal_get_day_of_week_type: Call to ICU method has failed");
640
641    RETURN_LONG((long)result);
642}
643#endif
644
645U_CFUNC PHP_FUNCTION(intlcal_get_first_day_of_week)
646{
647    CALENDAR_METHOD_INIT_VARS;
648
649    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
650            "O", &object, Calendar_ce_ptr) == FAILURE) {
651        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
652            "intlcal_get_first_day_of_week: bad arguments", 0 TSRMLS_CC);
653        RETURN_FALSE;
654    }
655
656    CALENDAR_METHOD_FETCH_OBJECT;
657
658    int32_t result = co->ucal->getFirstDayOfWeek(CALENDAR_ERROR_CODE(co));
659    INTL_METHOD_CHECK_STATUS(co,
660        "intlcal_get_first_day_of_week: Call to ICU method has failed");
661
662    RETURN_LONG((long)result);
663}
664
665static void _php_intlcal_field_ret_in32t_method(
666        int32_t (Calendar::*func)(UCalendarDateFields) const,
667        const char *method_name,
668        INTERNAL_FUNCTION_PARAMETERS)
669{
670    long    field;
671    char    *message;
672    CALENDAR_METHOD_INIT_VARS;
673
674    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
675            "Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
676        spprintf(&message, 0, "%s: bad arguments", method_name);
677        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, message, 1 TSRMLS_CC);
678        efree(message);
679        RETURN_FALSE;
680    }
681
682    if (field < 0 || field >= UCAL_FIELD_COUNT) {
683        spprintf(&message, 0, "%s: invalid field", method_name);
684        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, message, 1 TSRMLS_CC);
685        efree(message);
686        RETURN_FALSE;
687    }
688
689    CALENDAR_METHOD_FETCH_OBJECT;
690
691    int32_t result = (co->ucal->*func)((UCalendarDateFields)field);
692    INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
693
694    RETURN_LONG((long)result);
695}
696
697U_CFUNC PHP_FUNCTION(intlcal_get_greatest_minimum)
698{
699    _php_intlcal_field_ret_in32t_method(&Calendar::getGreatestMinimum,
700        "intlcal_get_greatest_minimum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
701}
702
703U_CFUNC PHP_FUNCTION(intlcal_get_least_maximum)
704{
705    _php_intlcal_field_ret_in32t_method(&Calendar::getLeastMaximum,
706        "intlcal_get_least_maximum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
707}
708
709U_CFUNC PHP_FUNCTION(intlcal_get_locale)
710{
711    long    locale_type;
712    CALENDAR_METHOD_INIT_VARS;
713
714    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
715            "Ol", &object, Calendar_ce_ptr, &locale_type) == FAILURE) {
716        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
717            "intlcal_get_locale: bad arguments", 0 TSRMLS_CC);
718        RETURN_FALSE;
719    }
720
721    if (locale_type != ULOC_ACTUAL_LOCALE && locale_type != ULOC_VALID_LOCALE) {
722        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
723            "intlcal_get_locale: invalid locale type", 0 TSRMLS_CC);
724        RETURN_FALSE;
725    }
726
727    CALENDAR_METHOD_FETCH_OBJECT;
728
729    Locale locale = co->ucal->getLocale((ULocDataLocaleType)locale_type,
730        CALENDAR_ERROR_CODE(co));
731    INTL_METHOD_CHECK_STATUS(co,
732        "intlcal_get_locale: Call to ICU method has failed");
733
734    RETURN_STRING(locale.getName(), 1);
735}
736
737U_CFUNC PHP_FUNCTION(intlcal_get_maximum)
738{
739    _php_intlcal_field_ret_in32t_method(&Calendar::getMaximum,
740        "intlcal_get_maximum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
741}
742
743U_CFUNC PHP_FUNCTION(intlcal_get_minimal_days_in_first_week)
744{
745    CALENDAR_METHOD_INIT_VARS;
746
747    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
748            "O", &object, Calendar_ce_ptr) == FAILURE) {
749        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
750            "intlcal_get_minimal_days_in_first_week: bad arguments", 0 TSRMLS_CC);
751        RETURN_FALSE;
752    }
753
754    CALENDAR_METHOD_FETCH_OBJECT;
755
756    uint8_t result = co->ucal->getMinimalDaysInFirstWeek();
757    INTL_METHOD_CHECK_STATUS(co,
758        "intlcal_get_first_day_of_week: Call to ICU method has failed");
759
760    RETURN_LONG((long)result);
761}
762
763U_CFUNC PHP_FUNCTION(intlcal_get_minimum)
764{
765    _php_intlcal_field_ret_in32t_method(&Calendar::getMinimum,
766        "intlcal_get_minimum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
767}
768
769U_CFUNC PHP_FUNCTION(intlcal_get_time_zone)
770{
771    CALENDAR_METHOD_INIT_VARS;
772
773    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
774            "O", &object, Calendar_ce_ptr) == FAILURE) {
775        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
776            "intlcal_get_time_zone: bad arguments", 0 TSRMLS_CC);
777        RETURN_FALSE;
778    }
779
780    CALENDAR_METHOD_FETCH_OBJECT;
781
782    TimeZone *tz = co->ucal->getTimeZone().clone();
783    if (tz == NULL) {
784        intl_errors_set(CALENDAR_ERROR_P(co), U_MEMORY_ALLOCATION_ERROR,
785            "intlcal_get_time_zone: could not clone TimeZone", 0 TSRMLS_CC);
786        RETURN_FALSE;
787    }
788
789    timezone_object_construct(tz, return_value, 1 TSRMLS_CC);
790}
791
792U_CFUNC PHP_FUNCTION(intlcal_get_type)
793{
794    CALENDAR_METHOD_INIT_VARS;
795
796    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
797            "O", &object, Calendar_ce_ptr) == FAILURE) {
798        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
799            "intlcal_get_type: bad arguments", 0 TSRMLS_CC);
800        RETURN_FALSE;
801    }
802
803    CALENDAR_METHOD_FETCH_OBJECT;
804
805    RETURN_STRING(co->ucal->getType(), 1);
806}
807
808#if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM >= 44
809U_CFUNC PHP_FUNCTION(intlcal_get_weekend_transition)
810{
811    long    dow;
812    CALENDAR_METHOD_INIT_VARS;
813
814    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
815            "Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
816        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
817            "intlcal_get_weekend_transition: bad arguments", 0 TSRMLS_CC);
818        RETURN_FALSE;
819    }
820
821    if (dow < UCAL_SUNDAY || dow > UCAL_SATURDAY) {
822        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
823            "intlcal_get_weekend_transition: invalid day of week", 0 TSRMLS_CC);
824        RETURN_FALSE;
825    }
826
827    CALENDAR_METHOD_FETCH_OBJECT;
828
829    int32_t res = co->ucal->getWeekendTransition((UCalendarDaysOfWeek)dow,
830        CALENDAR_ERROR_CODE(co));
831    INTL_METHOD_CHECK_STATUS(co, "intlcal_get_weekend_transition: "
832        "Error calling ICU method");
833
834    RETURN_LONG((long)res);
835}
836#endif
837
838U_CFUNC PHP_FUNCTION(intlcal_in_daylight_time)
839{
840    CALENDAR_METHOD_INIT_VARS;
841
842    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
843            "O", &object, Calendar_ce_ptr) == FAILURE) {
844        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
845            "intlcal_in_daylight_time: bad arguments", 0 TSRMLS_CC);
846        RETURN_FALSE;
847    }
848
849    CALENDAR_METHOD_FETCH_OBJECT;
850
851    UBool ret = co->ucal->inDaylightTime(CALENDAR_ERROR_CODE(co));
852    INTL_METHOD_CHECK_STATUS(co, "intlcal_in_daylight_time: "
853        "Error calling ICU method");
854
855    RETURN_BOOL((int)ret);
856}
857
858U_CFUNC PHP_FUNCTION(intlcal_is_equivalent_to)
859{
860    zval            *other_object;
861    Calendar_object *other_co;
862    CALENDAR_METHOD_INIT_VARS;
863
864    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
865            "OO", &object, Calendar_ce_ptr, &other_object, Calendar_ce_ptr)
866            == FAILURE) {
867        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
868            "intlcal_is_equivalent_to: bad arguments", 0 TSRMLS_CC);
869        RETURN_FALSE;
870    }
871
872    other_co = (Calendar_object*)zend_object_store_get_object(other_object TSRMLS_CC);
873    if (other_co->ucal == NULL) {
874        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "intlcal_is_equivalent_to:"
875            " Other IntlCalendar is unconstructed", 0 TSRMLS_CC);
876        RETURN_FALSE;
877    }
878
879    CALENDAR_METHOD_FETCH_OBJECT;
880
881    RETURN_BOOL((int)co->ucal->isEquivalentTo(*other_co->ucal));
882}
883
884U_CFUNC PHP_FUNCTION(intlcal_is_lenient)
885{
886    CALENDAR_METHOD_INIT_VARS;
887
888    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
889            "O", &object, Calendar_ce_ptr) == FAILURE) {
890        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
891            "intlcal_is_lenient: bad arguments", 0 TSRMLS_CC);
892        RETURN_FALSE;
893    }
894
895    CALENDAR_METHOD_FETCH_OBJECT;
896
897    RETURN_BOOL((int)co->ucal->isLenient());
898}
899
900U_CFUNC PHP_FUNCTION(intlcal_is_set)
901{
902    long field;
903    CALENDAR_METHOD_INIT_VARS;
904
905    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
906            "Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
907        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
908            "intlcal_is_set: bad arguments", 0 TSRMLS_CC);
909        RETURN_FALSE;
910    }
911
912    if (field < 0 || field >= UCAL_FIELD_COUNT) {
913        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
914            "intlcal_is_set: invalid field", 0 TSRMLS_CC);
915        RETURN_FALSE;
916    }
917
918    CALENDAR_METHOD_FETCH_OBJECT;
919
920    RETURN_BOOL((int)co->ucal->isSet((UCalendarDateFields)field));
921}
922
923#if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM >= 44
924U_CFUNC PHP_FUNCTION(intlcal_is_weekend)
925{
926    double date;
927    zval *rawDate = NULL;
928    CALENDAR_METHOD_INIT_VARS;
929
930    if (zend_parse_method_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
931            ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
932            "O|z!", &object, Calendar_ce_ptr, &rawDate) == FAILURE
933            || (rawDate != NULL &&
934                zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
935                "O|d", &object, Calendar_ce_ptr, &date) == FAILURE)) {
936        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
937            "intlcal_is_weekend: bad arguments", 0 TSRMLS_CC);
938        RETURN_FALSE;
939    }
940
941    CALENDAR_METHOD_FETCH_OBJECT;
942
943    if (rawDate == NULL) {
944        RETURN_BOOL((int)co->ucal->isWeekend());
945    } else {
946        UBool ret = co->ucal->isWeekend((UDate)date, CALENDAR_ERROR_CODE(co));
947        INTL_METHOD_CHECK_STATUS(co, "intlcal_is_weekend: "
948            "Error calling ICU method");
949        RETURN_BOOL((int)ret);
950    }
951}
952#endif
953
954
955U_CFUNC PHP_FUNCTION(intlcal_set_first_day_of_week)
956{
957    long    dow;
958    CALENDAR_METHOD_INIT_VARS;
959
960    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
961            "Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
962        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
963            "intlcal_set_first_day_of_week: bad arguments", 0 TSRMLS_CC);
964        RETURN_FALSE;
965    }
966
967    if (dow < UCAL_SUNDAY || dow > UCAL_SATURDAY) {
968        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
969            "intlcal_set_first_day_of_week: invalid day of week", 0 TSRMLS_CC);
970        RETURN_FALSE;
971    }
972
973    CALENDAR_METHOD_FETCH_OBJECT;
974
975    co->ucal->setFirstDayOfWeek((UCalendarDaysOfWeek)dow);
976
977    RETURN_TRUE;
978}
979
980U_CFUNC PHP_FUNCTION(intlcal_set_lenient)
981{
982    zend_bool is_lenient;
983    CALENDAR_METHOD_INIT_VARS;
984
985    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
986            "Ob", &object, Calendar_ce_ptr, &is_lenient) == FAILURE) {
987        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
988            "intlcal_set_lenient: bad arguments", 0 TSRMLS_CC);
989        RETURN_FALSE;
990    }
991
992    CALENDAR_METHOD_FETCH_OBJECT;
993
994    co->ucal->setLenient((UBool) is_lenient);
995
996    RETURN_TRUE;
997}
998
999U_CFUNC PHP_FUNCTION(intlcal_set_minimal_days_in_first_week)
1000{
1001    long    num_days;
1002    CALENDAR_METHOD_INIT_VARS;
1003
1004    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
1005            "Ol", &object, Calendar_ce_ptr, &num_days) == FAILURE) {
1006        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1007            "intlcal_set_minimal_days_in_first_week: bad arguments", 0 TSRMLS_CC);
1008        RETURN_FALSE;
1009    }
1010
1011    if (num_days < 1 || num_days > 7) {
1012        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1013            "intlcal_set_minimal_days_in_first_week: invalid number of days; "
1014            "must be between 1 and 7", 0 TSRMLS_CC);
1015        RETURN_FALSE;
1016    }
1017
1018    CALENDAR_METHOD_FETCH_OBJECT;
1019
1020    co->ucal->setMinimalDaysInFirstWeek((uint8_t)num_days);
1021
1022    RETURN_TRUE;
1023}
1024
1025U_CFUNC PHP_FUNCTION(intlcal_equals)
1026{
1027    zval            *other_object;
1028    Calendar_object *other_co;
1029    CALENDAR_METHOD_INIT_VARS;
1030
1031    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
1032            "OO", &object, Calendar_ce_ptr, &other_object, Calendar_ce_ptr)
1033            == FAILURE) {
1034        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1035            "intlcal_equals: bad arguments", 0 TSRMLS_CC);
1036        RETURN_FALSE;
1037    }
1038
1039    CALENDAR_METHOD_FETCH_OBJECT;
1040    other_co = (Calendar_object *) zend_object_store_get_object(other_object TSRMLS_CC);
1041    if (other_co->ucal == NULL) {
1042        intl_errors_set(&co->err, U_ILLEGAL_ARGUMENT_ERROR,
1043            "intlcal_equals: The second IntlCalendar is unconstructed", 0 TSRMLS_CC);
1044        RETURN_FALSE;
1045    }
1046
1047    UBool result = co->ucal->equals(*other_co->ucal, CALENDAR_ERROR_CODE(co));
1048    INTL_METHOD_CHECK_STATUS(co, "intlcal_equals: error calling ICU Calendar::equals");
1049
1050    RETURN_BOOL((int)result);
1051}
1052
1053#if U_ICU_VERSION_MAJOR_NUM >= 49
1054
1055U_CFUNC PHP_FUNCTION(intlcal_get_repeated_wall_time_option)
1056{
1057    CALENDAR_METHOD_INIT_VARS;
1058
1059    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
1060            "O", &object, Calendar_ce_ptr) == FAILURE) {
1061        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1062            "intlcal_get_repeated_wall_time_option: bad arguments", 0 TSRMLS_CC);
1063        RETURN_FALSE;
1064    }
1065
1066    CALENDAR_METHOD_FETCH_OBJECT;
1067
1068    RETURN_LONG(co->ucal->getRepeatedWallTimeOption());
1069}
1070
1071U_CFUNC PHP_FUNCTION(intlcal_get_skipped_wall_time_option)
1072{
1073    CALENDAR_METHOD_INIT_VARS;
1074
1075    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
1076            "O", &object, Calendar_ce_ptr) == FAILURE) {
1077        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1078            "intlcal_get_skipped_wall_time_option: bad arguments", 0 TSRMLS_CC);
1079        RETURN_FALSE;
1080    }
1081
1082    CALENDAR_METHOD_FETCH_OBJECT;
1083
1084    RETURN_LONG(co->ucal->getSkippedWallTimeOption());
1085}
1086
1087U_CFUNC PHP_FUNCTION(intlcal_set_repeated_wall_time_option)
1088{
1089    long    option;
1090    CALENDAR_METHOD_INIT_VARS;
1091
1092    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
1093            "Ol", &object, Calendar_ce_ptr, &option) == FAILURE) {
1094        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1095            "intlcal_set_repeated_wall_time_option: bad arguments", 0 TSRMLS_CC);
1096        RETURN_FALSE;
1097    }
1098
1099    if (option != UCAL_WALLTIME_FIRST && option != UCAL_WALLTIME_LAST) {
1100        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1101            "intlcal_set_repeated_wall_time_option: invalid option", 0 TSRMLS_CC);
1102        RETURN_FALSE;
1103    }
1104
1105    CALENDAR_METHOD_FETCH_OBJECT;
1106
1107    co->ucal->setRepeatedWallTimeOption((UCalendarWallTimeOption)option);
1108
1109    RETURN_TRUE;
1110}
1111
1112U_CFUNC PHP_FUNCTION(intlcal_set_skipped_wall_time_option)
1113{
1114    long    option;
1115    CALENDAR_METHOD_INIT_VARS;
1116
1117    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
1118            "Ol", &object, Calendar_ce_ptr, &option) == FAILURE) {
1119        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1120            "intlcal_set_skipped_wall_time_option: bad arguments", 0 TSRMLS_CC);
1121        RETURN_FALSE;
1122    }
1123
1124    if (option != UCAL_WALLTIME_FIRST && option != UCAL_WALLTIME_LAST
1125            && option != UCAL_WALLTIME_NEXT_VALID) {
1126        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1127            "intlcal_set_skipped_wall_time_option: invalid option", 0 TSRMLS_CC);
1128        RETURN_FALSE;
1129    }
1130
1131    CALENDAR_METHOD_FETCH_OBJECT;
1132
1133    co->ucal->setSkippedWallTimeOption((UCalendarWallTimeOption)option);
1134
1135    RETURN_TRUE;
1136}
1137
1138#endif
1139
1140U_CFUNC PHP_FUNCTION(intlcal_from_date_time)
1141{
1142    zval            **zv_arg,
1143                    *zv_datetime        = NULL,
1144                    *zv_timestamp       = NULL;
1145    php_date_obj    *datetime;
1146    char            *locale_str         = NULL;
1147    int             locale_str_len;
1148    TimeZone        *timeZone;
1149    UErrorCode      status              = U_ZERO_ERROR;
1150    Calendar        *cal;
1151    intl_error_reset(NULL TSRMLS_CC);
1152
1153    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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 TSRMLS_CC);
1157        RETURN_NULL();
1158    }
1159
1160    if (!(Z_TYPE_PP(zv_arg) == IS_OBJECT && instanceof_function(
1161            Z_OBJCE_PP(zv_arg), php_date_get_date_ce() TSRMLS_CC))) {
1162        ALLOC_INIT_ZVAL(zv_datetime);
1163        object_init_ex(zv_datetime, php_date_get_date_ce());
1164        zend_call_method_with_1_params(&zv_datetime, NULL, NULL, "__construct",
1165            NULL, *zv_arg);
1166        if (EG(exception)) {
1167            zend_object_store_ctor_failed(zv_datetime TSRMLS_CC);
1168            goto error;
1169        }
1170    } else {
1171        zv_datetime = *zv_arg;
1172    }
1173
1174    datetime = (php_date_obj*)zend_object_store_get_object(zv_datetime TSRMLS_CC);
1175    if (!datetime->time) {
1176        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1177            "intlcal_from_date_time: DateTime object is unconstructed",
1178            0 TSRMLS_CC);
1179        goto error;
1180    }
1181
1182    zend_call_method_with_0_params(&zv_datetime, php_date_get_date_ce(),
1183        NULL, "gettimestamp", &zv_timestamp);
1184    if (!zv_timestamp || Z_TYPE_P(zv_timestamp) != IS_LONG) {
1185        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1186            "intlcal_from_date_time: bad DateTime; call to "
1187            "DateTime::getTimestamp() failed", 0 TSRMLS_CC);
1188        goto error;
1189    }
1190
1191    if (!datetime->time->is_localtime) {
1192        timeZone = TimeZone::getGMT()->clone();
1193    } else {
1194        timeZone = timezone_convert_datetimezone(datetime->time->zone_type,
1195            datetime, 1, NULL, "intlcal_from_date_time" TSRMLS_CC);
1196        if (timeZone == NULL) {
1197            goto error;
1198        }
1199    }
1200
1201    if (!locale_str) {
1202        locale_str = const_cast<char*>(intl_locale_get_default(TSRMLS_C));
1203    }
1204
1205    cal = Calendar::createInstance(timeZone,
1206        Locale::createFromName(locale_str), status);
1207    if (cal == NULL) {
1208        delete timeZone;
1209        intl_error_set(NULL, status, "intlcal_from_date_time: "
1210                "error creating ICU Calendar object", 0 TSRMLS_CC);
1211        goto error;
1212    }
1213    cal->setTime(((UDate)Z_LVAL_P(zv_timestamp)) * 1000., status);
1214    if (U_FAILURE(status)) {
1215        /* time zone was adopted by cal; should not be deleted here */
1216        delete cal;
1217        intl_error_set(NULL, status, "intlcal_from_date_time: "
1218                "error creating ICU Calendar::setTime()", 0 TSRMLS_CC);
1219        goto error;
1220    }
1221
1222    calendar_object_create(return_value, cal TSRMLS_CC);
1223
1224error:
1225    if (zv_datetime != *zv_arg) {
1226        zval_ptr_dtor(&zv_datetime);
1227    }
1228    if (zv_timestamp) {
1229        zval_ptr_dtor(&zv_timestamp);
1230    }
1231}
1232
1233U_CFUNC PHP_FUNCTION(intlcal_to_date_time)
1234{
1235    zval *retval = NULL;
1236    CALENDAR_METHOD_INIT_VARS;
1237
1238    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
1239            &object, Calendar_ce_ptr) == FAILURE) {
1240        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1241            "intlcal_to_date_time: bad arguments", 0 TSRMLS_CC);
1242        RETURN_FALSE;
1243    }
1244
1245    CALENDAR_METHOD_FETCH_OBJECT;
1246
1247    /* There are no exported functions in ext/date to this
1248     * in a more native fashion */
1249    double  date = co->ucal->getTime(CALENDAR_ERROR_CODE(co)) / 1000.;
1250    int64_t ts;
1251    char    ts_str[sizeof("@-9223372036854775808")];
1252    int     ts_str_len;
1253    zval    ts_zval = zval_used_for_init;
1254
1255    INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
1256
1257    if (date > (double)U_INT64_MAX || date < (double)U_INT64_MIN) {
1258        intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1259            "intlcal_to_date_time: The calendar date is out of the "
1260            "range for a 64-bit integer", 0 TSRMLS_CC);
1261        RETURN_FALSE;
1262    }
1263
1264    ts = (int64_t)date;
1265
1266    ts_str_len = slprintf(ts_str, sizeof(ts_str), "@%I64d", ts);
1267    ZVAL_STRINGL(&ts_zval, ts_str, ts_str_len, 0);
1268
1269    /* Now get the time zone */
1270    const TimeZone& tz = co->ucal->getTimeZone();
1271    zval *timezone_zval = timezone_convert_to_datetimezone(
1272        &tz, CALENDAR_ERROR_P(co), "intlcal_to_date_time" TSRMLS_CC);
1273    if (timezone_zval == NULL) {
1274        RETURN_FALSE;
1275    }
1276
1277    /* resources allocated from now on */
1278
1279    /* Finally, instantiate object and call constructor */
1280    object_init_ex(return_value, php_date_get_date_ce());
1281    zend_call_method_with_2_params(&return_value, NULL, NULL, "__construct",
1282            NULL, &ts_zval, timezone_zval);
1283    if (EG(exception)) {
1284        intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1285            "intlcal_to_date_time: DateTime constructor has thrown exception",
1286            1 TSRMLS_CC);
1287        zend_object_store_ctor_failed(return_value TSRMLS_CC);
1288        zval_ptr_dtor(&return_value);
1289
1290        RETVAL_FALSE;
1291        goto error;
1292    }
1293
1294    /* due to bug #40743, we have to set the time zone again */
1295    zend_call_method_with_1_params(&return_value, NULL, NULL, "settimezone",
1296            &retval, timezone_zval);
1297    if (retval == NULL || Z_TYPE_P(retval) == IS_BOOL) {
1298        intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1299            "intlcal_to_date_time: call to DateTime::setTimeZone has failed",
1300            1 TSRMLS_CC);
1301        zval_ptr_dtor(&return_value);
1302        RETVAL_FALSE;
1303        goto error;
1304    }
1305
1306error:
1307    zval_ptr_dtor(&timezone_zval);
1308    if (retval != NULL) {
1309        zval_ptr_dtor(&retval);
1310    }
1311}
1312
1313U_CFUNC PHP_FUNCTION(intlcal_get_error_code)
1314{
1315    CALENDAR_METHOD_INIT_VARS;
1316
1317    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
1318            &object, Calendar_ce_ptr) == FAILURE) {
1319        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1320            "intlcal_get_error_code: bad arguments", 0 TSRMLS_CC);
1321        RETURN_FALSE;
1322    }
1323
1324    /* Fetch the object (without resetting its last error code ). */
1325    co = (Calendar_object*)zend_object_store_get_object(object TSRMLS_CC);
1326    if (co == NULL)
1327        RETURN_FALSE;
1328
1329    RETURN_LONG((long)CALENDAR_ERROR_CODE(co));
1330}
1331
1332U_CFUNC PHP_FUNCTION(intlcal_get_error_message)
1333{
1334    const char* message = NULL;
1335    CALENDAR_METHOD_INIT_VARS;
1336
1337    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
1338            &object, Calendar_ce_ptr) == FAILURE) {
1339        intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
1340            "intlcal_get_error_message: bad arguments", 0 TSRMLS_CC );
1341        RETURN_FALSE;
1342    }
1343
1344
1345    /* Fetch the object (without resetting its last error code ). */
1346    co = (Calendar_object*)zend_object_store_get_object(object TSRMLS_CC);
1347    if (co == NULL)
1348        RETURN_FALSE;
1349
1350    /* Return last error message. */
1351    message = intl_error_get_message(CALENDAR_ERROR_P(co) TSRMLS_CC);
1352    RETURN_STRING(message, 0);
1353}
1354