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