Commit: 2f0775b999e7859275c614a3d2d8edd1506e0d68
Author: Gustavo André dos Santos Lopes <[email protected]> Sun, 22 Jul 2012
03:54:03 +0200
Parents: 46629e35ffadae77114088c59faf301328159d83
Branches: master
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=2f0775b999e7859275c614a3d2d8edd1506e0d68
Log:
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.
Changed paths:
M ext/intl/calendar/calendar_methods.cpp
M ext/intl/common/common_date.cpp
M ext/intl/common/common_date.h
M ext/intl/config.m4
M ext/intl/config.w32
M ext/intl/dateformat/dateformat_class.c
M ext/intl/dateformat/dateformat_format.c
A ext/intl/dateformat/dateformat_format_object.cpp
A ext/intl/dateformat/dateformat_format_object.h
M ext/intl/msgformat/msgformat_helpers.cpp
M ext/intl/php_intl.c
M ext/intl/tests/dateformat_format.phpt
A ext/intl/tests/dateformat_formatObject_calendar.phpt
A ext/intl/tests/dateformat_formatObject_datetime..phpt
A ext/intl/tests/dateformat_formatObject_error.phpt
M ext/intl/tests/msgfmt_format_error5.phpt
M ext/intl/timezone/timezone_class..cpp
M ext/intl/timezone/timezone_class.h
M ext/intl/timezone/timezone_methods.cpp
diff --git a/ext/intl/calendar/calendar_methods.cpp b/ext/intl/calendar/calendar_methods.cpp
index 8562a2d..f59edaa 100644
--- a/ext/intl/calendar/calendar_methods.cpp
+++ b/ext/intl/calendar/calendar_methods.cpp
@@ -23,7 +23,10 @@
#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"
diff --git a/ext/intl/common/common_date.cpp b/ext/intl/common/common_date.cpp
index 812a196..ee99881 100644
--- a/ext/intl/common/common_date.cpp
+++ b/ext/intl/common/common_date.cpp
@@ -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;
diff --git a/ext/intl/common/common_date.h b/ext/intl/common/common_date.h
index cfa14d1..d2396cb 100644
--- a/ext/intl/common/common_date.h
+++ b/ext/intl/common/common_date.h
@@ -21,9 +21,20 @@
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 */
diff --git a/ext/intl/config.m4 b/ext/intl/config.m4
index 8598161..c33f47f 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 d57c7f3..8514364 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 fda67f1..809a1c6 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 468a3d7..d5a17f9 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 0000000..e8981fa
--- /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 |
+ | [email protected] so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Gustavo Lopes <[email protected]> |
+ +----------------------------------------------------------------------+
+*/
+
+#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
index 0000000..d80ea87
--- /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 |
+ | [email protected] so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Gustavo Lopes <[email protected]> |
+ +----------------------------------------------------------------------+
+ */
+
+#include <php.h>
+
+PHP_FUNCTION(datefmt_format_object);
diff --git a/ext/intl/msgformat/msgformat_helpers.cpp b/ext/intl/msgformat/msgformat_helpers.cpp
index c822319..9ee1cdc 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 59272db..f314870 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 d09de0e..8664eea 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 0000000..03371a9
--- /dev/null
+++ b/ext/intl/tests/dateformat_formatObject_calendar.phpt
@@ -0,0 +1,41 @@
+--TEST--
+IntlDateFormatter::formatObject(): IntlCalendar tests
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+--FILE--
+<?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==
+
+--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 0000000..bfc26cb
--- /dev/null
+++ b/ext/intl/tests/dateformat_formatObject_datetime.phpt
@@ -0,0 +1,34 @@
+--TEST--
+IntlDateFormatter::formatObject(): DateTime tests
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+--FILE--
+<?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==
+
+--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 0000000..7aaf69e
--- /dev/null
+++ b/ext/intl/tests/dateformat_formatObject_error.phpt
@@ -0,0 +1,74 @@
+--TEST--
+IntlDateFormatter::formatObject(): error conditions
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+--FILE--
+<?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==
+
+--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 052d0ef..ebbd455 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 6e62c34..27cf41a 100644
--- a/ext/intl/timezone/timezone_class.cpp
+++ b/ext/intl/timezone/timezone_class.cpp
@@ -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,
diff --git a/ext/intl/timezone/timezone_class.h b/ext/intl/timezone/timezone_class.h
index 0d3c0ed..a638f6d 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 1435679..c35f0b8 100644
--- a/ext/intl/timezone/timezone_methods.cpp
+++ b/ext/intl/timezone/timezone_methods.cpp
@@ -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"