]> granicus.if.org Git - php/commitdiff
Added IntlDateFormatter::formatObject(). Refactor
authorGustavo André dos Santos Lopes <cataphract@php.net>
Sun, 22 Jul 2012 01:54:03 +0000 (03:54 +0200)
committerGustavo André dos Santos Lopes <cataphract@php.net>
Sun, 22 Jul 2012 02:22:23 +0000 (04:22 +0200)
To better support IntlCalendar, added this function:

string IntlDateFormatter::formatObject(IntlCalendar|DateTime $obj [,
array|int|string $format = null [, string $locale = null).

$format is either of the constants IntlDateFormatter::FULL, etc., in
which case this format applies to both the date and the time, an array
in the form array($dateFormat, $timeFormat), or a string with the
SimpleDateFormat pattern.

This uses both the Calendar type and the timezone of the passed object
to configure the formatter (a GregorianCalendar is forced for
DateTime).

Some stuff was moved around and slighlt modified to allow for more code
reuse.

19 files changed:
ext/intl/calendar/calendar_methods.cpp
ext/intl/common/common_date.cpp
ext/intl/common/common_date.h
ext/intl/config.m4
ext/intl/config.w32
ext/intl/dateformat/dateformat_class.c
ext/intl/dateformat/dateformat_format.c
ext/intl/dateformat/dateformat_format_object.cpp [new file with mode: 0644]
ext/intl/dateformat/dateformat_format_object.h [new file with mode: 0644]
ext/intl/msgformat/msgformat_helpers.cpp
ext/intl/php_intl.c
ext/intl/tests/dateformat_format.phpt
ext/intl/tests/dateformat_formatObject_calendar.phpt [new file with mode: 0644]
ext/intl/tests/dateformat_formatObject_datetime.phpt [new file with mode: 0644]
ext/intl/tests/dateformat_formatObject_error.phpt [new file with mode: 0644]
ext/intl/tests/msgfmt_format_error5.phpt
ext/intl/timezone/timezone_class.cpp
ext/intl/timezone/timezone_class.h
ext/intl/timezone/timezone_methods.cpp

index 8562a2d69eaf451f151e8290f6f3baaef74c84ad..f59edaa25e3335bb91d1328fc61c3890df82f9f4 100644 (file)
 #include <unicode/locid.h>
 #include <unicode/calendar.h>
 #include <unicode/ustring.h>
+
 #include "../intl_convertcpp.h"
+#include "../common/common_date.h"
+
 extern "C" {
 #define USE_TIMEZONE_POINTER 1
 #include "../timezone/timezone_class.h"
index 812a196ed0fefb26d1e5a799a82d0da511a60b87..ee998818d989f497746e961fe70c92c17b7bacb5 100644 (file)
@@ -25,13 +25,162 @@ extern "C" {
 #include <ext/date/php_date.h>
 }
 
-U_CFUNC double intl_zval_to_millis(zval *z, UErrorCode *status TSRMLS_DC)
+#ifndef INFINITY
+#define INFINITY (DBL_MAX+DBL_MAX)
+#endif
+
+#ifndef NAN
+#define NAN (INFINITY-INFINITY)
+#endif
+
+/* {{{ timezone_convert_datetimezone
+ *      The timezone in DateTime and DateTimeZone is not unified. */
+U_CFUNC TimeZone *timezone_convert_datetimezone(int type,
+                                                                                               void *object,
+                                                                                               int is_datetime,
+                                                                                               intl_error *outside_error,
+                                                                                               const char *func TSRMLS_DC)
 {
-       double rv = NAN;
-       long lv;
-       int type;
+       char            *id = NULL,
+                               offset_id[] = "GMT+00:00";
+       int                     id_len = 0;
+       char            *message;
+       TimeZone        *timeZone;
+
+       switch (type) {
+               case TIMELIB_ZONETYPE_ID:
+                       id = is_datetime
+                               ? ((php_date_obj*)object)->time->tz_info->name
+                               : ((php_timezone_obj*)object)->tzi.tz->name;
+                       id_len = strlen(id);
+                       break;
+               case TIMELIB_ZONETYPE_OFFSET: {
+                       int offset_mins = is_datetime
+                               ? -((php_date_obj*)object)->time->z
+                               : -(int)((php_timezone_obj*)object)->tzi.utc_offset,
+                               hours = offset_mins / 60,
+                               minutes = offset_mins - hours * 60;
+                       minutes *= minutes > 0 ? 1 : -1;
+
+                       if (offset_mins <= -24 * 60 || offset_mins >= 24 * 60) {
+                               spprintf(&message, 0, "%s: object has an time zone offset "
+                                       "that's too large", func);
+                               intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
+                                       message, 1 TSRMLS_CC);
+                               efree(message);
+                               return NULL;
+                       }
 
-       if (U_FAILURE(*status)) {
+                       id = offset_id;
+                       id_len = slprintf(id, sizeof(offset_id), "GMT%+03d:%02d",
+                               hours, minutes);
+                       break;
+               }
+               case TIMELIB_ZONETYPE_ABBR:
+                       id = is_datetime
+                               ? ((php_date_obj*)object)->time->tz_abbr
+                               : ((php_timezone_obj*)object)->tzi.z.abbr;
+                       id_len = strlen(id);
+                       break;
+       }
+
+       UnicodeString s = UnicodeString(id, id_len, US_INV);
+       timeZone = TimeZone::createTimeZone(s);
+#if U_ICU_VERSION_MAJOR_NUM >= 49
+       if (*timeZone == TimeZone::getUnknown()) {
+#else
+       UnicodeString resultingId;
+       timeZone->getID(resultingId);
+       if (resultingId == UnicodeString("Etc/Unknown", -1, US_INV)
+                       || resultingId == UnicodeString("GMT", -1, US_INV)) {
+#endif
+               spprintf(&message, 0, "%s: time zone id '%s' "
+                       "extracted from ext/date DateTimeZone not recognized", func, id);
+               intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
+                       message, 1 TSRMLS_CC);
+               efree(message);
+               delete timeZone;
+               return NULL;
+       }
+       return timeZone;
+}
+/* }}} */
+
+U_CFUNC int intl_datetime_decompose(zval *z, double *millis, TimeZone **tz,
+               intl_error *err, const char *func TSRMLS_DC)
+{
+       zval    retval;
+       zval    *zfuncname;
+       char    *message;
+
+       if (err && U_FAILURE(err->code)) {
+               return FAILURE;
+       }
+
+       if (millis) {
+               *millis = NAN;
+       }
+       if (tz) {
+               *tz = NULL;
+       }
+
+       if (millis) {
+               INIT_ZVAL(retval);
+               MAKE_STD_ZVAL(zfuncname);
+               ZVAL_STRING(zfuncname, "getTimestamp", 1);
+               if (call_user_function(NULL, &(z), zfuncname, &retval, 0, NULL TSRMLS_CC)
+                               != SUCCESS || Z_TYPE(retval) != IS_LONG) {
+                       spprintf(&message, 0, "%s: error calling ::getTimeStamp() on the "
+                                       "object", func);
+                       intl_errors_set(err, U_INTERNAL_PROGRAM_ERROR,
+                               message, 1 TSRMLS_CC);
+                       efree(message);
+                       zval_ptr_dtor(&zfuncname);
+                       return FAILURE;
+               }
+
+               *millis = U_MILLIS_PER_SECOND * (double)Z_LVAL(retval);
+               zval_ptr_dtor(&zfuncname);
+       }
+
+       if (tz) {
+               php_date_obj *datetime;
+               datetime = (php_date_obj*)zend_object_store_get_object(z TSRMLS_CC);
+               if (!datetime->time) {
+                       spprintf(&message, 0, "%s: the DateTime object is not properly "
+                                       "initialized", func);
+                       intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+                               message, 1 TSRMLS_CC);
+                       efree(message);
+                       return FAILURE;
+               }
+               if (!datetime->time->is_localtime) {
+                       *tz = TimeZone::getGMT()->clone();
+               } else {
+                       *tz = timezone_convert_datetimezone(datetime->time->zone_type,
+                               datetime, 1, NULL, func TSRMLS_CC);
+                       if (*tz == NULL) {
+                               spprintf(&message, 0, "%s: could not convert DateTime's "
+                                               "time zone", func);
+                               intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+                                       message, 1 TSRMLS_CC);
+                               efree(message);
+                               return FAILURE;
+                       }
+               }
+       }
+
+       return SUCCESS;
+}
+
+U_CFUNC double intl_zval_to_millis(zval *z, intl_error *err, const char *func TSRMLS_DC)
+{
+       double  rv = NAN;
+       long    lv;
+       int             type;
+       char    *message;
+
+       if (err && U_FAILURE(err->code)) {
                return NAN;
        }
 
@@ -43,7 +192,12 @@ U_CFUNC double intl_zval_to_millis(zval *z, UErrorCode *status TSRMLS_DC)
                } else if (type == IS_LONG) {
                        rv = U_MILLIS_PER_SECOND * (double)lv;
                } else {
-                       *status = U_ILLEGAL_ARGUMENT_ERROR;
+                       spprintf(&message, 0, "%s: string '%s' is not numeric, "
+                                       "which would be required for it to be a valid date", func,
+                                       Z_STRVAL_P(z));
+                       intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+                               message, 1 TSRMLS_CC);
+                       efree(message);
                }
                break;
        case IS_LONG:
@@ -54,33 +208,41 @@ U_CFUNC double intl_zval_to_millis(zval *z, UErrorCode *status TSRMLS_DC)
                break;
        case IS_OBJECT:
                if (instanceof_function(Z_OBJCE_P(z), php_date_get_date_ce() TSRMLS_CC)) {
-                       zval retval;
-                       zval *zfuncname;
-                       INIT_ZVAL(retval);
-                       MAKE_STD_ZVAL(zfuncname);
-                       ZVAL_STRING(zfuncname, "getTimestamp", 1);
-                       if (call_user_function(NULL, &(z), zfuncname, &retval, 0, NULL TSRMLS_CC)
-                                       != SUCCESS || Z_TYPE(retval) != IS_LONG) {
-                               *status = U_INTERNAL_PROGRAM_ERROR;
-                       } else {
-                               rv = U_MILLIS_PER_SECOND * (double)Z_LVAL(retval);
-                       }
-                       zval_ptr_dtor(&zfuncname);
+                       intl_datetime_decompose(z, &rv, NULL, err, func TSRMLS_CC);
                } else if (instanceof_function(Z_OBJCE_P(z), Calendar_ce_ptr TSRMLS_CC)) {
                        Calendar_object *co = (Calendar_object *)
                                zend_object_store_get_object(z TSRMLS_CC );
                        if (co->ucal == NULL) {
-                               *status = U_ILLEGAL_ARGUMENT_ERROR;
+                               spprintf(&message, 0, "%s: IntlCalendar object is not properly "
+                                               "constructed", func);
+                               intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+                                       message, 1 TSRMLS_CC);
+                               efree(message);
                        } else {
-                               rv = (double)co->ucal->getTime(*status);
+                               UErrorCode status = UErrorCode();
+                               rv = (double)co->ucal->getTime(status);
+                               if (U_FAILURE(status)) {
+                                       spprintf(&message, 0, "%s: call to internal "
+                                                       "Calendar::getTime() has failed", func);
+                                       intl_errors_set(err, status, message, 1 TSRMLS_CC);
+                                       efree(message);
+                               }
                        }
                } else {
                        /* TODO: try with cast(), get() to obtain a number */
-                       *status = U_ILLEGAL_ARGUMENT_ERROR;
+                       spprintf(&message, 0, "%s: invalid object type for date/time "
+                                       "(only IntlCalendar and DateTime permitted)", func);
+                       intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+                               message, 1 TSRMLS_CC);
+                       efree(message);
                }
                break;
        default:
-               *status = U_ILLEGAL_ARGUMENT_ERROR;
+               spprintf(&message, 0, "%s: invalid PHP type for date", func);
+               intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+                       message, 1 TSRMLS_CC);
+               efree(message);
+               break;
        }
 
        return rv;
index cfa14d1a58384398e12ebcfdcc8b68b4a49252e2..d2396cbf5afa616255499a51baf6b992be1b2c06 100644 (file)
 
 U_CDECL_BEGIN
 #include <php.h>
+#include "../intl_error.h"
 U_CDECL_END
 
-U_CFUNC double intl_zval_to_millis(zval *z, UErrorCode *status TSRMLS_DC);
+#ifdef __cplusplus
+
+#include <unicode/timezone.h>
+
+U_CFUNC TimeZone *timezone_convert_datetimezone(int type, void *object, int is_datetime, intl_error *outside_error, const char *func TSRMLS_DC);
+U_CFUNC int intl_datetime_decompose(zval *z, double *millis, TimeZone **tz,
+               intl_error *err, const char *func TSRMLS_DC);
+
+#endif
+
+U_CFUNC double intl_zval_to_millis(zval *z, intl_error *err, const char *func TSRMLS_DC);
 
 #endif /* COMMON_DATE_H */
 
index 8598161e2b88751ea0544ac104e9b9666cfcfe8e..c33f47fbdc0a33458893c2c250fb9672476f3142 100755 (executable)
@@ -52,6 +52,7 @@ if test "$PHP_INTL" != "no"; then
     dateformat/dateformat_attr.c \
     dateformat/dateformat_data.c \
     dateformat/dateformat_format.c \
+    dateformat/dateformat_format_object.cpp \
     dateformat/dateformat_parse.c \
     dateformat/dateformat_create.cpp \
     dateformat/dateformat_attrcpp.cpp \
index d57c7f3a334e907a6acd44e8362c899547226933..851436480d935283d1af8b9aadc7d4f6fb0e57f4 100755 (executable)
@@ -62,6 +62,7 @@ if (PHP_INTL != "no") {
                                dateformat_class.c \
                                dateformat_attr.c \
                                dateformat_format.c \
+                               dateformat_format_object.cpp \
                                dateformat_parse.c \
                                dateformat_data.c \
                                dateformat_attrcpp.cpp \
index fda67f1b7000308873610f38679ac95570569499..809a1c60c9b3e9a4add1ee900b1c194237e3ece0 100755 (executable)
@@ -19,6 +19,7 @@
 #include "php_intl.h"
 #include "dateformat_data.h"
 #include "dateformat_format.h"
+#include "dateformat_format_object.h"
 #include "dateformat_parse.h"
 #include "dateformat.h"
 #include "dateformat_attr.h"
@@ -120,6 +121,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_intldateformatter_format, 0, 0, 0)
        ZEND_ARG_INFO(0, array)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_intldateformatter_format_object, 0, 0, 1)
+       ZEND_ARG_INFO(0, object)
+       ZEND_ARG_INFO(0, format)
+       ZEND_ARG_INFO(0, locale)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO(arginfo_intldateformatter_getdatetype, 0)
 ZEND_END_ARG_INFO()
 
@@ -170,6 +177,7 @@ static zend_function_entry IntlDateFormatter_class_functions[] = {
        PHP_NAMED_FE( setLenient, ZEND_FN( datefmt_set_lenient ), arginfo_intldateformatter_setlenient )
        PHP_NAMED_FE( isLenient, ZEND_FN( datefmt_is_lenient ), arginfo_intldateformatter_getdatetype )
        PHP_NAMED_FE( format, ZEND_FN( datefmt_format ), arginfo_intldateformatter_format )
+       PHP_ME_MAPPING( formatObject, datefmt_format_object, arginfo_intldateformatter_format_object, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
        PHP_NAMED_FE( parse, ZEND_FN( datefmt_parse), datefmt_parse_args )
        PHP_NAMED_FE( localtime, ZEND_FN( datefmt_localtime ), datefmt_parse_args )
        PHP_NAMED_FE( getErrorCode, ZEND_FN( datefmt_get_error_code ), arginfo_intldateformatter_getdatetype )
index 468a3d7748d3b4269bb3588a1981582be348a7ad..d5a17f91cd3997e69d19388df916ff95a8e96e39 100755 (executable)
@@ -175,10 +175,11 @@ PHP_FUNCTION(datefmt_format)
                timestamp = internal_get_timestamp(dfo, hash_arr TSRMLS_CC);
                INTL_METHOD_CHECK_STATUS(dfo, "datefmt_format: date formatting failed")
        } else {
-               timestamp = intl_zval_to_millis(zarg,
-                               &INTL_DATA_ERROR_CODE(dfo) TSRMLS_CC);
-               INTL_METHOD_CHECK_STATUS(dfo, "datefmt_format: could not convert input "
-                               "into a date")
+               timestamp = intl_zval_to_millis(zarg, INTL_DATA_ERROR_P(dfo),
+                               "datefmt_format" TSRMLS_CC);
+               if (U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
+                       RETURN_FALSE;
+               }
        }
        
        internal_format( dfo, timestamp, return_value TSRMLS_CC);
diff --git a/ext/intl/dateformat/dateformat_format_object.cpp b/ext/intl/dateformat/dateformat_format_object.cpp
new file mode 100644 (file)
index 0000000..e8981fa
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors: Gustavo Lopes <cataphract@php.net>                          |
+   +----------------------------------------------------------------------+
+*/
+
+#include "../intl_cppshims.h"
+
+#include <unicode/calendar.h>
+#include <unicode/gregocal.h>
+#include <unicode/datefmt.h>
+#include <unicode/smpdtfmt.h>
+#include <unicode/locid.h>
+
+#include "../intl_convertcpp.h"
+
+extern "C" {
+#include "../php_intl.h"
+#include "../locale/locale.h"
+#define USE_CALENDAR_POINTER 1
+#include "../calendar/calendar_class.h"
+#include <ext/date/php_date.h>
+#include "../common/common_date.h"
+}
+
+static const DateFormat::EStyle valid_styles[] = {
+               DateFormat::kNone,
+               DateFormat::kFull,
+               DateFormat::kLong,
+               DateFormat::kMedium,
+               DateFormat::kShort,
+               DateFormat::kFullRelative,
+               DateFormat::kLongRelative,
+               DateFormat::kMediumRelative,
+               DateFormat::kShortRelative,
+};
+
+static bool valid_format(zval **z) {
+       if (Z_TYPE_PP(z) == IS_LONG) {
+               long lval = Z_LVAL_PP(z);
+               for (int i = 0; i < sizeof(valid_styles) / sizeof(*valid_styles); i++) {
+                       if ((long)valid_styles[i] == lval) {
+                               return true;
+                       }
+               }
+       }
+
+       return false;
+}
+
+U_CFUNC PHP_FUNCTION(datefmt_format_object)
+{
+       zval                            *object,
+                                               **format = NULL;
+       const char                      *locale_str     = NULL;
+       int                                     locale_len;
+       bool                            pattern         = false;
+       UDate                           date;
+       TimeZone                        *timeZone       = NULL;
+       UErrorCode                      status          = U_ZERO_ERROR;
+       DateFormat                      *df                     = NULL;
+       Calendar                        *cal            = NULL;
+       DateFormat::EStyle      dateStyle = DateFormat::kDefault,
+                                               timeStyle = DateFormat::kDefault;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|Zs!",
+                       &object, &format, &locale_str, &locale_len) == FAILURE) {
+               RETURN_FALSE;
+       }
+
+       if (!locale_str) {
+               locale_str = intl_locale_get_default(TSRMLS_C);
+       }
+
+       if (format == NULL || Z_TYPE_PP(format) == IS_NULL) {
+               //nothing
+       } else if (Z_TYPE_PP(format) == IS_ARRAY) {
+               HashTable               *ht     = Z_ARRVAL_PP(format);
+               HashPosition    pos     = {0};
+               zval                    **z;
+               if (zend_hash_num_elements(ht) != 2) {
+                       intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                                       "datefmt_format_object: bad format; if array, it must have "
+                                       "two elements", 0 TSRMLS_CC);
+                       RETURN_FALSE;
+               }
+
+               zend_hash_internal_pointer_reset_ex(ht, &pos);
+               zend_hash_get_current_data_ex(ht, (void**)&z, &pos);
+               if (!valid_format(z)) {
+                       intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                                       "datefmt_format_object: bad format; the date format (first "
+                                       "element of the array) is not valid", 0 TSRMLS_CC);
+                       RETURN_FALSE;
+               }
+               dateStyle = (DateFormat::EStyle)Z_LVAL_PP(z);
+
+               zend_hash_move_forward_ex(ht, &pos);
+               zend_hash_get_current_data_ex(ht, (void**)&z, &pos);
+               if (!valid_format(z)) {
+                       intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                                       "datefmt_format_object: bad format; the time format ("
+                                       "second element of the array) is not valid", 0 TSRMLS_CC);
+                       RETURN_FALSE;
+               }
+               timeStyle = (DateFormat::EStyle)Z_LVAL_PP(z);
+       } else if (Z_TYPE_PP(format) == IS_LONG) {
+               if (!valid_format(format)) {
+                       intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                                       "datefmt_format_object: the date/time format type is invalid",
+                                       0 TSRMLS_CC);
+                       RETURN_FALSE;
+               }
+               dateStyle = timeStyle = (DateFormat::EStyle)Z_LVAL_PP(format);
+       } else {
+               convert_to_string_ex(format);
+               if (Z_STRLEN_PP(format) == 0) {
+                       intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                                       "datefmt_format_object: the format is empty", 0 TSRMLS_CC);
+                       RETURN_FALSE;
+               }
+               pattern = true;
+       }
+
+       //there's no support for relative time in ICU yet
+       timeStyle = (DateFormat::EStyle)(timeStyle & ~DateFormat::kRelative);
+
+       zend_class_entry *instance_ce = Z_OBJCE_P(object);
+       if (instanceof_function(instance_ce, Calendar_ce_ptr TSRMLS_CC)) {
+               Calendar *obj_cal = calendar_fetch_native_calendar(object TSRMLS_CC);
+               if (obj_cal == NULL) {
+                       intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                                       "datefmt_format_object: bad IntlCalendar instance: "
+                                       "not initialized properly", 0 TSRMLS_CC);
+                       RETURN_FALSE;
+               }
+               timeZone = obj_cal->getTimeZone().clone();
+               date = obj_cal->getTime(status);
+               if (U_FAILURE(status)) {
+                       intl_error_set(NULL, status,
+                                       "datefmt_format_object: error obtaining instant from "
+                                       "IntlCalendar", 0 TSRMLS_CC);
+                       RETVAL_FALSE;
+                       goto cleanup;
+               }
+               cal = obj_cal->clone();
+       } else if (instanceof_function(instance_ce, php_date_get_date_ce() TSRMLS_CC)) {
+               if (intl_datetime_decompose(object, &date, &timeZone, NULL,
+                               "datefmt_format_object" TSRMLS_CC) == FAILURE) {
+                       RETURN_FALSE;
+               }
+               cal = new GregorianCalendar(Locale::createFromName(locale_str), status);
+               if (U_FAILURE(status)) {
+                       intl_error_set(NULL, status,
+                                       "datefmt_format_object: could not create GregorianCalendar",
+                                       0 TSRMLS_CC);
+                       RETVAL_FALSE;
+                       goto cleanup;
+               }
+       } else {
+               intl_error_set(NULL, status, "datefmt_format_object: the passed object "
+                               "must be an instance of either IntlCalendar or DateTime",
+                               0 TSRMLS_CC);
+               RETURN_FALSE;
+       }
+
+       if (pattern) {
+                df = new SimpleDateFormat(
+                               UnicodeString(Z_STRVAL_PP(format), Z_STRLEN_PP(format),
+                                               UnicodeString::kInvariant),
+                               Locale::createFromName(locale_str),
+                               status);
+
+               if (U_FAILURE(status)) {
+                       intl_error_set(NULL, status,
+                                       "datefmt_format_object: could not create SimpleDateFormat",
+                                       0 TSRMLS_CC);
+                       RETVAL_FALSE;
+                       goto cleanup;
+               }
+       } else {
+               df = DateFormat::createDateTimeInstance(dateStyle, timeStyle,
+                               Locale::createFromName(locale_str));
+
+               if (df == NULL) { /* according to ICU sources, this should never happen */
+                       intl_error_set(NULL, status,
+                                       "datefmt_format_object: could not create DateFormat",
+                                       0 TSRMLS_CC);
+                       RETVAL_FALSE;
+                       goto cleanup;
+               }
+       }
+
+       //must be in this order (or have the cal adopt the tz)
+       df->adoptCalendar(cal);
+       cal = NULL;
+       df->adoptTimeZone(timeZone);
+       timeZone = NULL;
+
+       {
+               UnicodeString result = UnicodeString();
+               df->format(date, result);
+
+               Z_TYPE_P(return_value) = IS_STRING;
+               if (intl_charFromString(result, &Z_STRVAL_P(return_value),
+                               &Z_STRLEN_P(return_value), &status) == FAILURE) {
+                       intl_error_set(NULL, status,
+                                       "datefmt_format_object: error converting result to UTF-8",
+                                       0 TSRMLS_CC);
+                       RETVAL_FALSE;
+                       goto cleanup;
+               }
+       }
+
+
+cleanup:
+       delete df;
+       delete timeZone;
+       delete cal;
+}
diff --git a/ext/intl/dateformat/dateformat_format_object.h b/ext/intl/dateformat/dateformat_format_object.h
new file mode 100644 (file)
index 0000000..d80ea87
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors: Gustavo Lopes <cataphract@php.net>                          |
+   +----------------------------------------------------------------------+
+ */
+
+#include <php.h>
+
+PHP_FUNCTION(datefmt_format_object);
index c8223190784a09255cfdb627defe739c3a538b6b..9ee1cdcfb0d174f72ddcffc5206c079d986f1e4a 100755 (executable)
@@ -43,14 +43,6 @@ extern "C" {
 #include "../timezone/timezone_class.h"
 }
 
-#ifndef INFINITY
-#define INFINITY (DBL_MAX+DBL_MAX)
-#endif
-
-#ifndef NAN
-#define NAN (INFINITY-INFINITY)
-#endif
-
 #if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM >= 48
 #define HAS_MESSAGE_PATTERN 1
 #endif
@@ -549,7 +541,7 @@ retry_kint64:
                                }
                        case Formattable::kDate:
                                {
-                                       double dd = intl_zval_to_millis(*elem, &err.code TSRMLS_CC);
+                                       double dd = intl_zval_to_millis(*elem, &err, "msgfmt_format" TSRMLS_CC);
                                        if (U_FAILURE(err.code)) {
                                                char *message, *key_char;
                                                int key_len;
index 59272db71271dc45f6f6bc626b588d2b3287df89..f314870ac6f56e66cc5a3562f8ccc51a94bd7165 100755 (executable)
@@ -62,6 +62,7 @@
 #include "dateformat/dateformat_attr.h"
 #include "dateformat/dateformat_attrcpp.h"
 #include "dateformat/dateformat_format.h"
+#include "dateformat/dateformat_format_object.h"
 #include "dateformat/dateformat_parse.h"
 #include "dateformat/dateformat_data.h"
 
@@ -339,6 +340,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_datefmt_format, 0, 0, 0)
        ZEND_ARG_INFO(0, array)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_datefmt_format_object, 0, 0, 1)
+       ZEND_ARG_INFO(0, object)
+       ZEND_ARG_INFO(0, format)
+       ZEND_ARG_INFO(0, locale)
+ZEND_END_ARG_INFO()
+
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_datefmt_create, 0, 0, 3)
        ZEND_ARG_INFO(0, locale)
        ZEND_ARG_INFO(0, date_type)
@@ -695,6 +703,7 @@ zend_function_entry intl_functions[] = {
        PHP_FE( datefmt_is_lenient, arginfo_msgfmt_get_locale )
        PHP_FE( datefmt_set_lenient, arginfo_msgfmt_get_locale )
        PHP_FE( datefmt_format, arginfo_datefmt_format )
+       PHP_FE( datefmt_format_object, arginfo_datefmt_format_object )
        PHP_FE( datefmt_parse, datefmt_parse_args )
        PHP_FE( datefmt_localtime , datefmt_parse_args )
        PHP_FE( datefmt_get_error_code, arginfo_msgfmt_get_error_code )
index d09de0e439614db84e58d8f5c446b6a85249b30b..8664eea319888fb8ac26d6e56a554e55abb169f8 100755 (executable)
@@ -399,24 +399,24 @@ Formatted DateTime is : 20001230 05:04 PM
 Date is: stdClass::__set_state(array(
 ))
 ------------
-Error while formatting as: 'datefmt_format: could not convert input into a date: U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
 ------------
 Date is: stdClass::__set_state(array(
 ))
 ------------
-Error while formatting as: 'datefmt_format: could not convert input into a date: U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
 ------------
 Date is: stdClass::__set_state(array(
 ))
 ------------
-Error while formatting as: 'datefmt_format: could not convert input into a date: U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
 ------------
 Date is: stdClass::__set_state(array(
 ))
 ------------
-Error while formatting as: 'datefmt_format: could not convert input into a date: U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
 ------------
 Date is: stdClass::__set_state(array(
 ))
 ------------
-Error while formatting as: 'datefmt_format: could not convert input into a date: U_ILLEGAL_ARGUMENT_ERROR'
+Error while formatting as: 'datefmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted): U_ILLEGAL_ARGUMENT_ERROR'
diff --git a/ext/intl/tests/dateformat_formatObject_calendar.phpt b/ext/intl/tests/dateformat_formatObject_calendar.phpt
new file mode 100644 (file)
index 0000000..03371a9
--- /dev/null
@@ -0,0 +1,41 @@
+--TEST--\r
+IntlDateFormatter::formatObject(): IntlCalendar tests\r
+--SKIPIF--\r
+<?php\r
+if (!extension_loaded('intl'))\r
+       die('skip intl extension not enabled');\r
+--FILE--\r
+<?php
+ini_set("intl.error_level", E_WARNING);
+ini_set("intl.default_locale", "pt_PT");
+ini_set("date.timezone", "Europe/Lisbon");
+
+$cal = IntlCalendar::fromDateTime('2012-01-01 00:00:00'); //Europe/Lisbon
+echo IntlDateFormatter::formatObject($cal), "\n";
+echo IntlDateFormatter::formatObject($cal, IntlDateFormatter::FULL), "\n";
+echo IntlDateFormatter::formatObject($cal, null, "en-US"), "\n";
+echo IntlDateFormatter::formatObject($cal, array(IntlDateFormatter::SHORT, IntlDateFormatter::FULL), "en-US"), "\n";
+echo IntlDateFormatter::formatObject($cal, 'E y-MM-d HH,mm,ss.SSS v', "en-US"), "\n";
+
+$cal = IntlCalendar::fromDateTime('2012-01-01 05:00:00+03:00');
+echo datefmt_format_object($cal, IntlDateFormatter::FULL), "\n";
+
+$cal = IntlCalendar::createInstance(null,'en-US@calendar=islamic-civil');
+$cal->setTime(strtotime('2012-01-01 00:00:00')*1000.);
+echo IntlDateFormatter::formatObject($cal), "\n";
+echo IntlDateFormatter::formatObject($cal, IntlDateFormatter::FULL, "en-US"), "\n";
+
+?>
+==DONE==
+\r
+--EXPECT--\r
+01/01/2012 00:00:00
+Domingo, 1 de Janeiro de 2012 0:00:00 Hora Padrão da Europa Ocidental
+Jan 1, 2012 12:00:00 AM
+1/1/12 12:00:00 AM Western European Standard Time
+Sun 2012-01-1 00,00,00.000 Portugal Time (Lisbon)
+Domingo, 1 de Janeiro de 2012 5:00:00 GMT+03:00
+06/02/1433 00:00:00
+Sunday, Safar 6, 1433 12:00:00 AM Western European Standard Time
+==DONE==
+\r
diff --git a/ext/intl/tests/dateformat_formatObject_datetime.phpt b/ext/intl/tests/dateformat_formatObject_datetime.phpt
new file mode 100644 (file)
index 0000000..bfc26cb
--- /dev/null
@@ -0,0 +1,34 @@
+--TEST--\r
+IntlDateFormatter::formatObject(): DateTime tests\r
+--SKIPIF--\r
+<?php\r
+if (!extension_loaded('intl'))\r
+       die('skip intl extension not enabled');\r
+--FILE--\r
+<?php
+ini_set("intl.error_level", E_WARNING);
+ini_set("intl.default_locale", "pt_PT");
+ini_set("date.timezone", "Europe/Lisbon");
+
+$dt = new DateTime('2012-01-01 00:00:00'); //Europe/Lisbon
+echo IntlDateFormatter::formatObject($dt), "\n";
+echo IntlDateFormatter::formatObject($dt, IntlDateFormatter::FULL), "\n";
+echo IntlDateFormatter::formatObject($dt, null, "en-US"), "\n";
+echo IntlDateFormatter::formatObject($dt, array(IntlDateFormatter::SHORT, IntlDateFormatter::FULL), "en-US"), "\n";
+echo IntlDateFormatter::formatObject($dt, 'E y-MM-d HH,mm,ss.SSS v', "en-US"), "\n";
+
+$dt = new DateTime('2012-01-01 05:00:00+03:00');
+echo IntlDateFormatter::formatObject($dt, IntlDateFormatter::FULL), "\n";
+
+?>
+==DONE==
+\r
+--EXPECT--\r
+01/01/2012 00:00:00
+Domingo, 1 de Janeiro de 2012 0:00:00 Hora Padrão da Europa Ocidental
+Jan 1, 2012 12:00:00 AM
+1/1/12 12:00:00 AM Western European Standard Time
+Sun 2012-01-1 00,00,00.000 Portugal Time (Lisbon)
+Domingo, 1 de Janeiro de 2012 5:00:00 GMT+03:00
+==DONE==
+\r
diff --git a/ext/intl/tests/dateformat_formatObject_error.phpt b/ext/intl/tests/dateformat_formatObject_error.phpt
new file mode 100644 (file)
index 0000000..7aaf69e
--- /dev/null
@@ -0,0 +1,74 @@
+--TEST--\r
+IntlDateFormatter::formatObject(): error conditions\r
+--SKIPIF--\r
+<?php\r
+if (!extension_loaded('intl'))\r
+       die('skip intl extension not enabled');\r
+--FILE--\r
+<?php
+ini_set("intl.error_level", E_WARNING);
+ini_set("intl.default_locale", "pt_PT");
+ini_set("date.timezone", "Europe/Lisbon");
+
+var_dump(IntlDateFormatter::formatObject());
+var_dump(IntlDateFormatter::formatObject(1));
+var_dump(IntlDateFormatter::formatObject(new stdclass));
+
+class A extends IntlCalendar {function __construct(){}}
+var_dump(IntlDateFormatter::formatObject(new A));
+class B extends DateTime {function __construct(){}}
+var_dump(IntlDateFormatter::formatObject(new B));
+
+$cal = IntlCalendar::createInstance();
+var_dump(IntlDateFormatter::formatObject($cal, -2));
+var_dump(IntlDateFormatter::formatObject($cal, array()));
+var_dump(IntlDateFormatter::formatObject($cal, array(1,2,3)));
+var_dump(IntlDateFormatter::formatObject($cal, array(array(), 1)));
+var_dump(IntlDateFormatter::formatObject($cal, array(1, -2)));
+var_dump(IntlDateFormatter::formatObject($cal, ""));
+var_dump(IntlDateFormatter::formatObject($cal, "YYYY", array()));
+
+?>
+==DONE==
+\r
+--EXPECTF--\r
+
+Warning: IntlDateFormatter::formatObject() expects at least 1 parameter, 0 given in %s on line %d
+bool(false)
+
+Warning: IntlDateFormatter::formatObject() expects parameter 1 to be object, integer given in %s on line %d
+bool(false)
+
+Warning: IntlDateFormatter::formatObject(): datefmt_format_object: the passed object must be an instance of either IntlCalendar or DateTime in %s on line %d
+bool(false)
+
+Warning: IntlDateFormatter::formatObject(): datefmt_format_object: bad IntlCalendar instance: not initialized properly in %s on line %d
+bool(false)
+
+Warning: DateTime::getTimestamp(): The DateTime object has not been correctly initialized by its constructor in %s on line %d
+
+Warning: IntlDateFormatter::formatObject(): datefmt_format_object: error calling ::getTimeStamp() on the object in %s on line %d
+bool(false)
+
+Warning: IntlDateFormatter::formatObject(): datefmt_format_object: the date/time format type is invalid in %s on line %d
+bool(false)
+
+Warning: IntlDateFormatter::formatObject(): datefmt_format_object: bad format; if array, it must have two elements in %s on line %d
+bool(false)
+
+Warning: IntlDateFormatter::formatObject(): datefmt_format_object: bad format; if array, it must have two elements in %s on line %d
+bool(false)
+
+Warning: IntlDateFormatter::formatObject(): datefmt_format_object: bad format; the date format (first element of the array) is not valid in %s on line %d
+bool(false)
+
+Warning: IntlDateFormatter::formatObject(): datefmt_format_object: bad format; the time format (second element of the array) is not valid in %s on line %d
+bool(false)
+
+Warning: IntlDateFormatter::formatObject(): datefmt_format_object: the format is empty in %s on line %d
+bool(false)
+
+Warning: IntlDateFormatter::formatObject() expects parameter 3 to be string, array given in %s on line %d
+bool(false)
+==DONE==
+\r
index 052d0efd116ff5b6783e9b8ff7465d7746d0b936..ebbd4550e8f462331d757cc534b8ca454baed9c9 100644 (file)
@@ -20,6 +20,7 @@ $mf = new MessageFormatter('en_US', $fmt);
 var_dump($mf->format(array("foo" => new stdclass())));
 
 --EXPECTF--
+Warning: MessageFormatter::format(): msgfmt_format: invalid object type for date/time (only IntlCalendar and DateTime permitted) in %s on line %d
 
 Warning: MessageFormatter::format(): The argument for key 'foo' cannot be used as a date or time in %s on line %d
 bool(false)
index 6e62c34f6d3d76012c0d30b8e890854116af34c7..27cf41a4cf4e9e6597ccc51d52e73d2bca517488 100644 (file)
@@ -25,6 +25,8 @@
 #include <unicode/calendar.h>
 #include "../intl_convertcpp.h"
 
+#include "../common/common_date.h"
+
 extern "C" {
 #include "../intl_convert.h"
 #define USE_TIMEZONE_POINTER 1
@@ -54,79 +56,6 @@ U_CFUNC void timezone_object_construct(const TimeZone *zone, zval *object, int o
 }
 /* }}} */
 
-/* {{{ timezone_convert_datetimezone
- *      The timezone in DateTime and DateTimeZone is not unified. */
-U_CFUNC TimeZone *timezone_convert_datetimezone(int type,
-                                                                                               void *object,
-                                                                                               int is_datetime,
-                                                                                               intl_error *outside_error,
-                                                                                               const char *func TSRMLS_DC)
-{
-       char            *id = NULL,
-                               offset_id[] = "GMT+00:00";
-       int                     id_len = 0;
-       char            *message;
-       TimeZone        *timeZone;
-
-       switch (type) {
-               case TIMELIB_ZONETYPE_ID:
-                       id = is_datetime
-                               ? ((php_date_obj*)object)->time->tz_info->name
-                               : ((php_timezone_obj*)object)->tzi.tz->name;
-                       id_len = strlen(id);
-                       break;
-               case TIMELIB_ZONETYPE_OFFSET: {
-                       int offset_mins = is_datetime
-                               ? -((php_date_obj*)object)->time->z
-                               : -(int)((php_timezone_obj*)object)->tzi.utc_offset,
-                               hours = offset_mins / 60,
-                               minutes = offset_mins - hours * 60;
-                       minutes *= minutes > 0 ? 1 : -1;
-
-                       if (offset_mins <= -24 * 60 || offset_mins >= 24 * 60) {
-                               spprintf(&message, 0, "%s: object has an time zone offset "
-                                       "that's too large", func);
-                               intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
-                                       message, 1 TSRMLS_CC);
-                               efree(message);
-                               return NULL;
-                       }
-
-                       id = offset_id;
-                       id_len = slprintf(id, sizeof(offset_id), "GMT%+03d:%02d",
-                               hours, minutes);
-                       break;
-               }
-               case TIMELIB_ZONETYPE_ABBR:
-                       id = is_datetime
-                               ? ((php_date_obj*)object)->time->tz_abbr
-                               : ((php_timezone_obj*)object)->tzi.z.abbr;
-                       id_len = strlen(id);
-                       break;
-       }
-
-       UnicodeString s = UnicodeString(id, id_len, US_INV);
-       timeZone = TimeZone::createTimeZone(s);
-#if U_ICU_VERSION_MAJOR_NUM >= 49
-       if (*timeZone == TimeZone::getUnknown()) {
-#else
-       UnicodeString resultingId;
-       timeZone->getID(resultingId);
-       if (resultingId == UnicodeString("Etc/Unknown", -1, US_INV)
-                       || resultingId == UnicodeString("GMT", -1, US_INV)) {
-#endif
-               spprintf(&message, 0, "%s: time zone id '%s' "
-                       "extracted from ext/date DateTimeZone not recognized", func, id);
-               intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
-                       message, 1 TSRMLS_CC);
-               efree(message);
-               delete timeZone;
-               return NULL;
-       }
-       return timeZone;
-}
-/* }}} */
-
 /* {{{ timezone_convert_to_datetimezone
  *        Convert from TimeZone to DateTimeZone object */
 U_CFUNC zval *timezone_convert_to_datetimezone(const TimeZone *timeZone,
index 0d3c0edde4721dfbd312138063b08b524d6dab60..a638f6dbf49efc14167e224632591fcc37bab1c2 100644 (file)
@@ -59,7 +59,6 @@ typedef struct {
                RETURN_FALSE; \
        }
 
-TimeZone *timezone_convert_datetimezone(int type, void *object, int is_datetime, intl_error *outside_error, const char *func TSRMLS_DC);
 zval *timezone_convert_to_datetimezone(const TimeZone *timeZone, intl_error *outside_error, const char *func TSRMLS_DC);
 TimeZone *timezone_process_timezone_argument(zval **zv_timezone, intl_error *error, const char *func TSRMLS_DC);
 
index 1435679fe743c49a6d014ae93e1bc6741c9970dc..c35f0b87217fa10c1a86af6b233d1973a14fd834 100644 (file)
@@ -24,6 +24,9 @@
 #include <unicode/timezone.h>
 #include <unicode/ustring.h>
 #include "intl_convertcpp.h"
+
+#include "../common/common_date.h"
+
 extern "C" {
 #define USE_TIMEZONE_POINTER 1
 #include "timezone_class.h"