From 2416719fb184f84fcd521be2c8a5feabf65270e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Mon, 2 Jul 2012 00:24:54 +0200 Subject: Unified zval -> UDate conversions Now IntlDateFormatter::format() also accepts IntlCalendar objects. Code is shared in MessageFormatter and IntlDateFormatter. --- ext/intl/common/common_date.cpp | 88 ++++++++++++++++++++++++++++++++ ext/intl/common/common_date.h | 29 +++++++++++ ext/intl/config.m4 | 1 + ext/intl/config.w32 | 1 + ext/intl/dateformat/dateformat.h | 1 - ext/intl/dateformat/dateformat_format.c | 80 +++++++++-------------------- ext/intl/msgformat/msgformat_helpers.cpp | 68 +----------------------- ext/intl/tests/dateformat_format.phpt | 10 ++-- 8 files changed, 149 insertions(+), 129 deletions(-) create mode 100644 ext/intl/common/common_date.cpp create mode 100644 ext/intl/common/common_date.h (limited to 'ext/intl') diff --git a/ext/intl/common/common_date.cpp b/ext/intl/common/common_date.cpp new file mode 100644 index 0000000000..812a196ed0 --- /dev/null +++ b/ext/intl/common/common_date.cpp @@ -0,0 +1,88 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + +----------------------------------------------------------------------+ +*/ + +#include "../intl_cppshims.h" + +#include + +extern "C" { +#include "../php_intl.h" +#define USE_CALENDAR_POINTER 1 +#include "../calendar/calendar_class.h" +#include +} + +U_CFUNC double intl_zval_to_millis(zval *z, UErrorCode *status TSRMLS_DC) +{ + double rv = NAN; + long lv; + int type; + + if (U_FAILURE(*status)) { + return NAN; + } + + switch (Z_TYPE_P(z)) { + case IS_STRING: + type = is_numeric_string(Z_STRVAL_P(z), Z_STRLEN_P(z), &lv, &rv, 0); + if (type == IS_DOUBLE) { + rv *= U_MILLIS_PER_SECOND; + } else if (type == IS_LONG) { + rv = U_MILLIS_PER_SECOND * (double)lv; + } else { + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + break; + case IS_LONG: + rv = U_MILLIS_PER_SECOND * (double)Z_LVAL_P(z); + break; + case IS_DOUBLE: + rv = U_MILLIS_PER_SECOND * Z_DVAL_P(z); + 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); + } 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; + } else { + rv = (double)co->ucal->getTime(*status); + } + } else { + /* TODO: try with cast(), get() to obtain a number */ + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + break; + default: + *status = U_ILLEGAL_ARGUMENT_ERROR; + } + + return rv; +} + diff --git a/ext/intl/common/common_date.h b/ext/intl/common/common_date.h new file mode 100644 index 0000000000..cfa14d1a58 --- /dev/null +++ b/ext/intl/common/common_date.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + +----------------------------------------------------------------------+ +*/ + +#ifndef COMMON_DATE_H +#define COMMON_DATE_H + +#include + +U_CDECL_BEGIN +#include +U_CDECL_END + +U_CFUNC double intl_zval_to_millis(zval *z, UErrorCode *status TSRMLS_DC); + +#endif /* COMMON_DATE_H */ + diff --git a/ext/intl/config.m4 b/ext/intl/config.m4 index 431deeb7d2..8598161e2b 100755 --- a/ext/intl/config.m4 +++ b/ext/intl/config.m4 @@ -33,6 +33,7 @@ if test "$PHP_INTL" != "no"; then collator/collator_error.c \ common/common_error.c \ common/common_enum.cpp \ + common/common_date.cpp \ formatter/formatter.c \ formatter/formatter_main.c \ formatter/formatter_class.c \ diff --git a/ext/intl/config.w32 b/ext/intl/config.w32 index 735749ab43..d57c7f3a33 100755 --- a/ext/intl/config.w32 +++ b/ext/intl/config.w32 @@ -24,6 +24,7 @@ if (PHP_INTL != "no") { ADD_SOURCES(configure_module_dirname + "/common", "\ common_error.c \ common_enum.cpp \ + common_date.cpp \ ", "intl"); ADD_SOURCES(configure_module_dirname + "/formatter", "\ formatter.c \ diff --git a/ext/intl/dateformat/dateformat.h b/ext/intl/dateformat/dateformat.h index f11918b79f..a5a747328f 100755 --- a/ext/intl/dateformat/dateformat.h +++ b/ext/intl/dateformat/dateformat.h @@ -40,6 +40,5 @@ These are not necessary at this point of time #define CALENDAR_YEAR "tm_year" #define CALENDAR_WDAY "tm_wday" #define CALENDAR_YDAY "tm_yday" -#define CALENDAR_ISDST "tm_isdst" #endif // DATE_FORMATTER_H diff --git a/ext/intl/dateformat/dateformat_format.c b/ext/intl/dateformat/dateformat_format.c index 82f825f140..65fe68eaf5 100755 --- a/ext/intl/dateformat/dateformat_format.c +++ b/ext/intl/dateformat/dateformat_format.c @@ -21,15 +21,13 @@ #include #include -#include "php_intl.h" -#include "intl_convert.h" +#include "../php_intl.h" +#include "../intl_convert.h" +#include "../common/common_date.h" #include "dateformat.h" #include "dateformat_class.h" #include "dateformat_format.h" #include "dateformat_data.h" -/* avoid redefinition of int8_t, already defined in unicode/pwin32.h */ -#define _MSC_STDINT_H_ 1 -#include "ext/date/php_date.h" /* {{{ * Internal function which calls the udat_format @@ -126,70 +124,38 @@ static UDate internal_get_timestamp(IntlDateFormatter_object *dfo, HashTable* ha * Format the time value as a string. }}}*/ PHP_FUNCTION(datefmt_format) { - UDate timestamp =0; - UDate p_timestamp =0; - HashTable* hash_arr = NULL; - zval* zarg = NULL; + UDate timestamp = 0; + HashTable *hash_arr = NULL; + zval *zarg = NULL; DATE_FORMAT_METHOD_INIT_VARS; /* Parse parameters. */ - if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz", &object, IntlDateFormatter_ce_ptr,&zarg ) == FAILURE ) - { - intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "datefmt_format: unable to parse input params", 0 TSRMLS_CC ); + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz", + &object, IntlDateFormatter_ce_ptr, &zarg) == FAILURE) { + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "datefmt_format: unable " + "to parse input params", 0 TSRMLS_CC ); RETURN_FALSE; } - /* Fetch the object. */ DATE_FORMAT_METHOD_FETCH_OBJECT; - switch(Z_TYPE_P(zarg) ){ - case IS_LONG: - p_timestamp = Z_LVAL_P(zarg) ; - timestamp = p_timestamp * 1000; - break; - case IS_DOUBLE: - /* timestamp*1000 since ICU expects it in milliseconds */ - p_timestamp = Z_DVAL_P(zarg) ; - timestamp = p_timestamp * 1000; - break; - case IS_ARRAY: - hash_arr = Z_ARRVAL_P(zarg); - if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 ) - RETURN_FALSE; - - timestamp = internal_get_timestamp(dfo, hash_arr TSRMLS_CC); - INTL_METHOD_CHECK_STATUS( dfo, "datefmt_format: Date formatting failed" ) - break; - case IS_OBJECT: { - zend_class_entry *date_ce = php_date_get_date_ce(); - zval retval; - zval *zfuncname; - if(!instanceof_function(Z_OBJCE_P(zarg), date_ce TSRMLS_CC)) { - intl_errors_set(INTL_DATA_ERROR_P(dfo), U_ILLEGAL_ARGUMENT_ERROR, "datefmt_format: object must be an instance of DateTime", 0 TSRMLS_CC ); - RETURN_FALSE; - } - INIT_ZVAL(retval); - MAKE_STD_ZVAL(zfuncname); - ZVAL_STRING(zfuncname, "getTimestamp", 1); - if(call_user_function(NULL, &zarg, zfuncname, &retval, 0, NULL TSRMLS_CC) != SUCCESS || Z_TYPE(retval) != IS_LONG) { - intl_errors_set(INTL_DATA_ERROR_P(dfo), U_ILLEGAL_ARGUMENT_ERROR, "datefmt_format: cannot get timestamp", 0 TSRMLS_CC ); - zval_ptr_dtor(&zfuncname); - RETURN_FALSE; - } - zval_ptr_dtor(&zfuncname); - p_timestamp = Z_LVAL(retval); - timestamp = p_timestamp*1000; - } - break; - default: - intl_errors_set( INTL_DATA_ERROR_P(dfo), U_ILLEGAL_ARGUMENT_ERROR, - "datefmt_format: takes either an array or an integer timestamp value or a DateTime object", 0 TSRMLS_CC ); + if (Z_TYPE_P(zarg) == IS_ARRAY) { + hash_arr = Z_ARRVAL_P(zarg); + if (!hash_arr || zend_hash_num_elements(hash_arr) == 0) { RETURN_FALSE; - } + } - internal_format( dfo, timestamp, return_value TSRMLS_CC); + 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") + } + internal_format( dfo, timestamp, return_value TSRMLS_CC); } /* }}} */ diff --git a/ext/intl/msgformat/msgformat_helpers.cpp b/ext/intl/msgformat/msgformat_helpers.cpp index fd8df1a39c..c822319078 100755 --- a/ext/intl/msgformat/msgformat_helpers.cpp +++ b/ext/intl/msgformat/msgformat_helpers.cpp @@ -31,6 +31,7 @@ #include #include "../intl_convertcpp.h" +#include "../common/common_date.h" extern "C" { #include "php_intl.h" @@ -38,11 +39,6 @@ extern "C" { #include "msgformat_format.h" #include "msgformat_helpers.h" #include "intl_convert.h" -#define USE_CALENDAR_POINTER 1 -#include "../calendar/calendar_class.h" -/* avoid redefinition of int8_t, already defined in unicode/pwin32.h */ -#define _MSC_STDINT_H_ 1 -#include "ext/date/php_date.h" #define USE_TIMEZONE_POINTER #include "../timezone/timezone_class.h" } @@ -95,66 +91,6 @@ U_CFUNC int32_t umsg_format_arg_count(UMessageFormat *fmt) return fmt_count; } -static double umsg_helper_zval_to_millis(zval *z, UErrorCode *status TSRMLS_DC) { - double rv = NAN; - long lv; - int type; - - if (U_FAILURE(*status)) { - return NAN; - } - - switch (Z_TYPE_P(z)) { - case IS_STRING: - type = is_numeric_string(Z_STRVAL_P(z), Z_STRLEN_P(z), &lv, &rv, 0); - if (type == IS_DOUBLE) { - rv *= U_MILLIS_PER_SECOND; - } else if (type == IS_LONG) { - rv = U_MILLIS_PER_SECOND * (double)lv; - } else { - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - break; - case IS_LONG: - rv = U_MILLIS_PER_SECOND * (double)Z_LVAL_P(z); - break; - case IS_DOUBLE: - rv = U_MILLIS_PER_SECOND * Z_DVAL_P(z); - 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); - } 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; - } else { - rv = (double)co->ucal->getTime(*status); - } - } else { - /* TODO: try with cast(), get() to obtain a number */ - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - break; - default: - *status = U_ILLEGAL_ARGUMENT_ERROR; - } - - return rv; -} - static HashTable *umsg_get_numeric_types(MessageFormatter_object *mfo, intl_error& err TSRMLS_DC) { @@ -613,7 +549,7 @@ retry_kint64: } case Formattable::kDate: { - double dd = umsg_helper_zval_to_millis(*elem, &err.code TSRMLS_CC); + double dd = intl_zval_to_millis(*elem, &err.code TSRMLS_CC); if (U_FAILURE(err.code)) { char *message, *key_char; int key_len; diff --git a/ext/intl/tests/dateformat_format.phpt b/ext/intl/tests/dateformat_format.phpt index 98f9d34c03..d09de0e439 100755 --- a/ext/intl/tests/dateformat_format.phpt +++ b/ext/intl/tests/dateformat_format.phpt @@ -399,24 +399,24 @@ Formatted DateTime is : 20001230 05:04 PM Date is: stdClass::__set_state(array( )) ------------ -Error while formatting as: 'datefmt_format: object must be an instance of DateTime: U_ILLEGAL_ARGUMENT_ERROR' +Error while formatting as: 'datefmt_format: could not convert input into a date: U_ILLEGAL_ARGUMENT_ERROR' ------------ Date is: stdClass::__set_state(array( )) ------------ -Error while formatting as: 'datefmt_format: object must be an instance of DateTime: U_ILLEGAL_ARGUMENT_ERROR' +Error while formatting as: 'datefmt_format: could not convert input into a date: U_ILLEGAL_ARGUMENT_ERROR' ------------ Date is: stdClass::__set_state(array( )) ------------ -Error while formatting as: 'datefmt_format: object must be an instance of DateTime: U_ILLEGAL_ARGUMENT_ERROR' +Error while formatting as: 'datefmt_format: could not convert input into a date: U_ILLEGAL_ARGUMENT_ERROR' ------------ Date is: stdClass::__set_state(array( )) ------------ -Error while formatting as: 'datefmt_format: object must be an instance of DateTime: U_ILLEGAL_ARGUMENT_ERROR' +Error while formatting as: 'datefmt_format: could not convert input into a date: U_ILLEGAL_ARGUMENT_ERROR' ------------ Date is: stdClass::__set_state(array( )) ------------ -Error while formatting as: 'datefmt_format: object must be an instance of DateTime: U_ILLEGAL_ARGUMENT_ERROR' +Error while formatting as: 'datefmt_format: could not convert input into a date: U_ILLEGAL_ARGUMENT_ERROR' -- cgit v1.2.1 From 46629e35ffadae77114088c59faf301328159d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Mon, 2 Jul 2012 00:26:38 +0200 Subject: Refactored internal_get_timestamp() Added bounds checking for 32-bit ints. Do not fetch array elements that ::parse() generates but that ::format() does not actually care about.y --- ext/intl/dateformat/dateformat_format.c | 98 +++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 36 deletions(-) (limited to 'ext/intl') diff --git a/ext/intl/dateformat/dateformat_format.c b/ext/intl/dateformat/dateformat_format.c index 65fe68eaf5..468a3d7748 100755 --- a/ext/intl/dateformat/dateformat_format.c +++ b/ext/intl/dateformat/dateformat_format.c @@ -59,20 +59,38 @@ static void internal_format(IntlDateFormatter_object *dfo, UDate timestamp, zval /* {{{ * Internal function which fetches an element from the passed array for the key_name passed */ -static double internal_get_arr_ele(IntlDateFormatter_object *dfo, HashTable* hash_arr, char* key_name TSRMLS_DC) +static int32_t internal_get_arr_ele(IntlDateFormatter_object *dfo, + HashTable* hash_arr, char* key_name, intl_error *err TSRMLS_DC) { - zval** ele_value = NULL; - UDate result = -1; - - if( zend_hash_find( hash_arr, key_name, strlen(key_name) + 1, (void **)&ele_value ) == SUCCESS ){ - if( Z_TYPE_PP(ele_value)!= IS_LONG ){ - intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, - "datefmt_format: parameter array does not contain a long element.", 0 TSRMLS_CC ); - }else{ - result = Z_LVAL_PP(ele_value); + zval **ele_value = NULL; + int32_t result = 0; + char *message; + + if (U_FAILURE(err->code)) { + return result; + } + + if (zend_hash_find(hash_arr, key_name, strlen(key_name) + 1, + (void **)&ele_value) == SUCCESS) { + if(Z_TYPE_PP(ele_value) != IS_LONG) { + spprintf(&message, 0, "datefmt_format: parameter array contains " + "a non-integer element for key '%s'", key_name); + intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR, message, 1 TSRMLS_CC); + efree(message); + } else { + if (Z_LVAL_PP(ele_value) > INT32_MAX || + Z_LVAL_PP(ele_value) < INT32_MIN) { + spprintf(&message, 0, "datefmt_format: value %ld is out of " + "bounds for a 32-bit integer in key '%s'", + Z_LVAL_PP(ele_value), key_name); + intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR, message, 1 TSRMLS_CC); + efree(message); + } else { + result = Z_LVAL_PP(ele_value); + } } } - /* printf("\n Inside internal_get_arr_ele key_name= %s, result = %g \n", key_name, result); */ + return result; } /* }}} */ @@ -80,41 +98,49 @@ static double internal_get_arr_ele(IntlDateFormatter_object *dfo, HashTable* has /* {{{ * Internal function which sets UCalendar from the passed array and retrieves timestamp */ -static UDate internal_get_timestamp(IntlDateFormatter_object *dfo, HashTable* hash_arr TSRMLS_DC) +static UDate internal_get_timestamp(IntlDateFormatter_object *dfo, + HashTable *hash_arr TSRMLS_DC) { - long year =0; - long month =0; - long hour =0; - long minute =0; - long second =0; - long wday =0; - long yday =0; - long mday =0; - UBool isInDST = FALSE; - const UCalendar *pcal; + int32_t year, + month, + hour, + minute, + second, + mday; + UCalendar *pcal; + intl_error *err = &dfo->datef_data.error; + +#define INTL_GET_ELEM(elem) \ + internal_get_arr_ele(dfo, hash_arr, (elem), err TSRMLS_CC) /* Fetch values from the incoming array */ - year = internal_get_arr_ele( dfo, hash_arr, CALENDAR_YEAR TSRMLS_CC) + 1900; /* tm_year is years since 1900 */ + year = INTL_GET_ELEM(CALENDAR_YEAR) + 1900; /* tm_year is years since 1900 */ /* Month in ICU and PHP starts from January =0 */ - month = internal_get_arr_ele( dfo, hash_arr, CALENDAR_MON TSRMLS_CC); - hour = internal_get_arr_ele( dfo, hash_arr, CALENDAR_HOUR TSRMLS_CC); - minute = internal_get_arr_ele( dfo, hash_arr, CALENDAR_MIN TSRMLS_CC); - second = internal_get_arr_ele( dfo, hash_arr, CALENDAR_SEC TSRMLS_CC); - wday = internal_get_arr_ele( dfo, hash_arr, CALENDAR_WDAY TSRMLS_CC); - yday = internal_get_arr_ele( dfo, hash_arr, CALENDAR_YDAY TSRMLS_CC); - isInDST = internal_get_arr_ele( dfo, hash_arr, CALENDAR_ISDST TSRMLS_CC); + month = INTL_GET_ELEM(CALENDAR_MON); + hour = INTL_GET_ELEM(CALENDAR_HOUR); + minute = INTL_GET_ELEM(CALENDAR_MIN); + second = INTL_GET_ELEM(CALENDAR_SEC); /* For the ucal_setDateTime() function, this is the 'date' value */ - mday = internal_get_arr_ele( dfo, hash_arr, CALENDAR_MDAY TSRMLS_CC); + mday = INTL_GET_ELEM(CALENDAR_MDAY); - pcal = udat_getCalendar(DATE_FORMAT_OBJECT(dfo)); - /* set the incoming values for the calendar */ - ucal_setDateTime( pcal, year, month, mday, hour, minute, second, &INTL_DATA_ERROR_CODE(dfo)); - if( INTL_DATA_ERROR_CODE(dfo) != U_ZERO_ERROR){ +#undef INTL_GET_ELEM + + pcal = ucal_clone(udat_getCalendar(DATE_FORMAT_OBJECT(dfo)), + &INTL_DATA_ERROR_CODE(dfo)); + + if (INTL_DATA_ERROR_CODE(dfo) != U_ZERO_ERROR) { + intl_errors_set(err, INTL_DATA_ERROR_CODE(dfo), "datefmt_format: " + "error cloning calendar", 0 TSRMLS_CC); return 0; } + + /* set the incoming values for the calendar */ + ucal_setDateTime(pcal, year, month, mday, hour, minute, second, &INTL_DATA_ERROR_CODE(dfo)); + /* actually, ucal_setDateTime cannot fail */ /* Fetch the timestamp from the UCalendar */ - return ucal_getMillis(pcal, &INTL_DATA_ERROR_CODE(dfo) ); + return ucal_getMillis(pcal, &INTL_DATA_ERROR_CODE(dfo)); + udat_close(pcal); } -- cgit v1.2.1 From 2f0775b999e7859275c614a3d2d8edd1506e0d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Sun, 22 Jul 2012 03:54:03 +0200 Subject: Added IntlDateFormatter::formatObject(). Refactor 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. --- ext/intl/calendar/calendar_methods.cpp | 3 + ext/intl/common/common_date.cpp | 206 ++++++++++++++++-- ext/intl/common/common_date.h | 13 +- ext/intl/config.m4 | 1 + ext/intl/config.w32 | 1 + ext/intl/dateformat/dateformat_class.c | 8 + ext/intl/dateformat/dateformat_format.c | 9 +- ext/intl/dateformat/dateformat_format_object.cpp | 230 +++++++++++++++++++++ ext/intl/dateformat/dateformat_format_object.h | 19 ++ ext/intl/msgformat/msgformat_helpers.cpp | 10 +- ext/intl/php_intl.c | 9 + ext/intl/tests/dateformat_format.phpt | 10 +- .../tests/dateformat_formatObject_calendar.phpt | 41 ++++ .../tests/dateformat_formatObject_datetime.phpt | 34 +++ ext/intl/tests/dateformat_formatObject_error.phpt | 74 +++++++ ext/intl/tests/msgfmt_format_error5.phpt | 1 + ext/intl/timezone/timezone_class.cpp | 75 +------ ext/intl/timezone/timezone_class.h | 1 - ext/intl/timezone/timezone_methods.cpp | 3 + 19 files changed, 633 insertions(+), 115 deletions(-) create mode 100644 ext/intl/dateformat/dateformat_format_object.cpp create mode 100644 ext/intl/dateformat/dateformat_format_object.h create mode 100644 ext/intl/tests/dateformat_formatObject_calendar.phpt create mode 100644 ext/intl/tests/dateformat_formatObject_datetime.phpt create mode 100644 ext/intl/tests/dateformat_formatObject_error.phpt (limited to 'ext/intl') diff --git a/ext/intl/calendar/calendar_methods.cpp b/ext/intl/calendar/calendar_methods.cpp index 8562a2d69e..f59edaa25e 100644 --- a/ext/intl/calendar/calendar_methods.cpp +++ b/ext/intl/calendar/calendar_methods.cpp @@ -23,7 +23,10 @@ #include #include #include + #include "../intl_convertcpp.h" +#include "../common/common_date.h" + extern "C" { #define USE_TIMEZONE_POINTER 1 #include "../timezone/timezone_class.h" diff --git a/ext/intl/common/common_date.cpp b/ext/intl/common/common_date.cpp index 812a196ed0..ee998818d9 100644 --- a/ext/intl/common/common_date.cpp +++ b/ext/intl/common/common_date.cpp @@ -25,13 +25,162 @@ extern "C" { #include } -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; diff --git a/ext/intl/common/common_date.h b/ext/intl/common/common_date.h index cfa14d1a58..d2396cbf5a 100644 --- a/ext/intl/common/common_date.h +++ b/ext/intl/common/common_date.h @@ -21,9 +21,20 @@ U_CDECL_BEGIN #include +#include "../intl_error.h" U_CDECL_END -U_CFUNC double intl_zval_to_millis(zval *z, UErrorCode *status TSRMLS_DC); +#ifdef __cplusplus + +#include + +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 */ diff --git a/ext/intl/config.m4 b/ext/intl/config.m4 index 8598161e2b..c33f47fbdc 100755 --- a/ext/intl/config.m4 +++ b/ext/intl/config.m4 @@ -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 \ diff --git a/ext/intl/config.w32 b/ext/intl/config.w32 index d57c7f3a33..851436480d 100755 --- a/ext/intl/config.w32 +++ b/ext/intl/config.w32 @@ -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 \ diff --git a/ext/intl/dateformat/dateformat_class.c b/ext/intl/dateformat/dateformat_class.c index fda67f1b70..809a1c60c9 100755 --- a/ext/intl/dateformat/dateformat_class.c +++ b/ext/intl/dateformat/dateformat_class.c @@ -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 ) diff --git a/ext/intl/dateformat/dateformat_format.c b/ext/intl/dateformat/dateformat_format.c index 468a3d7748..d5a17f91cd 100755 --- a/ext/intl/dateformat/dateformat_format.c +++ b/ext/intl/dateformat/dateformat_format.c @@ -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 index 0000000000..e8981faa26 --- /dev/null +++ b/ext/intl/dateformat/dateformat_format_object.cpp @@ -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 | + +----------------------------------------------------------------------+ +*/ + +#include "../intl_cppshims.h" + +#include +#include +#include +#include +#include + +#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 +#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 index 0000000000..d80ea87e0f --- /dev/null +++ b/ext/intl/dateformat/dateformat_format_object.h @@ -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 | + +----------------------------------------------------------------------+ + */ + +#include + +PHP_FUNCTION(datefmt_format_object); diff --git a/ext/intl/msgformat/msgformat_helpers.cpp b/ext/intl/msgformat/msgformat_helpers.cpp index c822319078..9ee1cdcfb0 100755 --- a/ext/intl/msgformat/msgformat_helpers.cpp +++ b/ext/intl/msgformat/msgformat_helpers.cpp @@ -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; diff --git a/ext/intl/php_intl.c b/ext/intl/php_intl.c index 59272db712..f314870ac6 100755 --- a/ext/intl/php_intl.c +++ b/ext/intl/php_intl.c @@ -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 ) diff --git a/ext/intl/tests/dateformat_format.phpt b/ext/intl/tests/dateformat_format.phpt index d09de0e439..8664eea319 100755 --- a/ext/intl/tests/dateformat_format.phpt +++ b/ext/intl/tests/dateformat_format.phpt @@ -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 index 0000000000..03371a91ab --- /dev/null +++ b/ext/intl/tests/dateformat_formatObject_calendar.phpt @@ -0,0 +1,41 @@ +--TEST-- +IntlDateFormatter::formatObject(): IntlCalendar tests +--SKIPIF-- +setTime(strtotime('2012-01-01 00:00:00')*1000.); +echo IntlDateFormatter::formatObject($cal), "\n"; +echo IntlDateFormatter::formatObject($cal, IntlDateFormatter::FULL, "en-US"), "\n"; + +?> +==DONE== + +--EXPECT-- +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== + diff --git a/ext/intl/tests/dateformat_formatObject_datetime.phpt b/ext/intl/tests/dateformat_formatObject_datetime.phpt new file mode 100644 index 0000000000..bfc26cb80c --- /dev/null +++ b/ext/intl/tests/dateformat_formatObject_datetime.phpt @@ -0,0 +1,34 @@ +--TEST-- +IntlDateFormatter::formatObject(): DateTime tests +--SKIPIF-- + +==DONE== + +--EXPECT-- +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== + diff --git a/ext/intl/tests/dateformat_formatObject_error.phpt b/ext/intl/tests/dateformat_formatObject_error.phpt new file mode 100644 index 0000000000..7aaf69e54e --- /dev/null +++ b/ext/intl/tests/dateformat_formatObject_error.phpt @@ -0,0 +1,74 @@ +--TEST-- +IntlDateFormatter::formatObject(): error conditions +--SKIPIF-- + +==DONE== + +--EXPECTF-- + +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== + diff --git a/ext/intl/tests/msgfmt_format_error5.phpt b/ext/intl/tests/msgfmt_format_error5.phpt index 052d0efd11..ebbd4550e8 100644 --- a/ext/intl/tests/msgfmt_format_error5.phpt +++ b/ext/intl/tests/msgfmt_format_error5.phpt @@ -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) diff --git a/ext/intl/timezone/timezone_class.cpp b/ext/intl/timezone/timezone_class.cpp index 6e62c34f6d..27cf41a4cf 100644 --- a/ext/intl/timezone/timezone_class.cpp +++ b/ext/intl/timezone/timezone_class.cpp @@ -25,6 +25,8 @@ #include #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, diff --git a/ext/intl/timezone/timezone_class.h b/ext/intl/timezone/timezone_class.h index 0d3c0edde4..a638f6dbf4 100644 --- a/ext/intl/timezone/timezone_class.h +++ b/ext/intl/timezone/timezone_class.h @@ -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); diff --git a/ext/intl/timezone/timezone_methods.cpp b/ext/intl/timezone/timezone_methods.cpp index 1435679fe7..c35f0b8721 100644 --- a/ext/intl/timezone/timezone_methods.cpp +++ b/ext/intl/timezone/timezone_methods.cpp @@ -24,6 +24,9 @@ #include #include #include "intl_convertcpp.h" + +#include "../common/common_date.h" + extern "C" { #define USE_TIMEZONE_POINTER 1 #include "timezone_class.h" -- cgit v1.2.1 From 2498c90c71980168b5b9ac2fa006340b9460b1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Sun, 22 Jul 2012 04:13:17 +0200 Subject: Readded accidentally removed line --- ext/intl/dateformat/dateformat.h | 1 + 1 file changed, 1 insertion(+) (limited to 'ext/intl') diff --git a/ext/intl/dateformat/dateformat.h b/ext/intl/dateformat/dateformat.h index a5a747328f..f11918b79f 100755 --- a/ext/intl/dateformat/dateformat.h +++ b/ext/intl/dateformat/dateformat.h @@ -40,5 +40,6 @@ These are not necessary at this point of time #define CALENDAR_YEAR "tm_year" #define CALENDAR_WDAY "tm_wday" #define CALENDAR_YDAY "tm_yday" +#define CALENDAR_ISDST "tm_isdst" #endif // DATE_FORMATTER_H -- cgit v1.2.1