Added IntlTimeZone::fromDateTimeZone() and ::toDateTimeZone.

IntlTimeZone::fromDateTimeZone(DateTimeZone $dtz) converts from an
ext/date TimeZone to an IntlTimeZone. The conversion is done by feeding
the time zone name (essentially what would be given by
DateTimeZone::getName()) to ICU's TimeZone::createTimeZone except if it's
an offset time zone. In that case, the offset is read from the ext/date
time zone object structure and an appopriate id (of the form
GMT<+|-><HH:MM>) is given to ICU's TimeZone::createTimeZone. Not all
ext/date time zones are recognized for ICU. For instance, WEST is not.
Note that these kind of abbreviations, as far as I can tell, can only be
created via ext/date DateTime, not directly through DateTimeZone's
constructor.

For IntlTimeZone::toDateTimeZone(), the behavior is symmetrical.
We instantiate a DateTimeZone and then call its constructor if we don't
have an offset time zone, otherwise we mess with its structure. If the
timezone is not valid for ext/date, then we allow the exception of
DateTimeZone constructor to propagate.
This commit is contained in:
Gustavo André dos Santos Lopes 2012-04-30 15:15:09 +02:00
parent 22f4a30748
commit 4cfd9995da
9 changed files with 308 additions and 2 deletions

View File

@ -414,6 +414,10 @@ ZEND_BEGIN_ARG_INFO_EX( arginfo_tz_idarg_static, 0, 0, 1 )
ZEND_ARG_INFO( 0, zoneId )
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX( arginfo_tz_from_date_time_zone, 0, 0, 1 )
ZEND_ARG_OBJ_INFO( 0, dateTimeZone, IntlDateTimeZone, 0 )
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX( arginfo_tz_create_enumeration, 0, 0, 0 )
ZEND_ARG_INFO( 0, countryOrRawOffset )
ZEND_END_ARG_INFO()
@ -719,6 +723,7 @@ zend_function_entry intl_functions[] = {
/* TimeZone functions */
PHP_FE( intltz_create_time_zone, arginfo_tz_idarg_static )
PHP_FE( intltz_from_date_time_zone, arginfo_tz_from_date_time_zone )
PHP_FE( intltz_create_default, arginfo_tz_void )
PHP_FE( intltz_get_id, arginfo_tz_only_tz )
PHP_FE( intltz_get_gmt, arginfo_tz_void )
@ -742,6 +747,7 @@ zend_function_entry intl_functions[] = {
PHP_FE( intltz_has_same_rules, arginfo_tz_has_same_rules )
PHP_FE( intltz_get_display_name, arginfo_tz_get_display_name )
PHP_FE( intltz_get_dst_savings, arginfo_tz_only_tz )
PHP_FE( intltz_to_date_time_zone, arginfo_tz_only_tz )
PHP_FE( intltz_get_error_code, arginfo_tz_only_tz )
PHP_FE( intltz_get_error_message, arginfo_tz_only_tz )

View File

@ -0,0 +1,41 @@
--TEST--
IntlTimeZone::fromDateTimeZone(): basic test
--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", "nl");
date_default_timezone_set('Europe/Lisbon');
$tz = IntlTimeZone::fromDateTimeZone(new DateTimeZone('Europe/Amsterdam'));
var_dump($tz->getID(), $tz->getRawOffset());
$dt = new DateTime('2012-01-01 00:00:00 CET');
$dtz = $dt->getTimeZone();
/* this is different from new DateTimeZone('CET'),
* which gives a Europe/Berlin timezone */
var_dump($dtz->getName());
$tz = IntlTimeZone::fromDateTimeZone($dtz);
var_dump($tz->getID(), $tz->getRawOffset());
$dt = new DateTime('2012-01-01 00:00:00 +0340');
$dtz = $dt->getTimeZone();
/* I don't think this timezone can be generated without a DateTime object */
var_dump($dtz->getName());
$tz = IntlTimeZone::fromDateTimeZone($dtz);
var_dump($tz->getID(), $tz->getRawOffset() /* (3*60+40)*60000 */);
--EXPECTF--
string(16) "Europe/Amsterdam"
int(3600000)
string(3) "CET"
string(3) "CET"
int(3600000)
string(6) "+03:40"
string(%d) "GMT+03%s0"
int(13200000)

View File

@ -0,0 +1,48 @@
--TEST--
IntlTimeZone::fromDateTimeZone(): argument errors
--SKIPIF--
<?php
if (!extension_loaded('intl'))
die('skip intl extension not enabled');
--FILE--
<?php
ini_set("intl.error_level", E_WARNING);
var_dump(IntlTimeZone::fromDateTimeZone());
var_dump(IntlTimeZone::fromDateTimeZone(1,2));
var_dump(IntlTimeZone::fromDateTimeZone('sdfds'));
var_dump(IntlTimeZone::fromDateTimeZone(new stdclass));
$dt = new DateTime('2012-08-01 00:00:00 WEST');
var_dump(IntlTimeZone::fromDateTimeZone($dt->getTimeZone()));
var_dump(intltz_from_date_time_zone());
--EXPECTF--
Warning: IntlTimeZone::fromDateTimeZone() expects exactly 1 parameter, 0 given in %s on line %d
Warning: IntlTimeZone::fromDateTimeZone(): intltz_from_date_time_zone: bad arguments in %s on line %d
NULL
Warning: IntlTimeZone::fromDateTimeZone() expects exactly 1 parameter, 2 given in %s on line %d
Warning: IntlTimeZone::fromDateTimeZone(): intltz_from_date_time_zone: bad arguments in %s on line %d
NULL
Warning: IntlTimeZone::fromDateTimeZone() expects parameter 1 to be DateTimeZone, string given in %s on line %d
Warning: IntlTimeZone::fromDateTimeZone(): intltz_from_date_time_zone: bad arguments in %s on line %d
NULL
Warning: IntlTimeZone::fromDateTimeZone() expects parameter 1 to be DateTimeZone, object given in %s on line %d
Warning: IntlTimeZone::fromDateTimeZone(): intltz_from_date_time_zone: bad arguments in %s on line %d
NULL
Warning: IntlTimeZone::fromDateTimeZone(): intltz_from_date_time_zone: time zone id 'WEST' extracted from ext/date DateTimeZone not recognized in %s on line %d
NULL
Warning: intltz_from_date_time_zone() expects exactly 1 parameter, 0 given in %s on line %d
Warning: intltz_from_date_time_zone(): intltz_from_date_time_zone: bad arguments in %s on line %d
NULL

View File

@ -0,0 +1,38 @@
--TEST--
IntlTimeZone::toDateTimeZone(): basic test
--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", "nl");
date_default_timezone_set('Europe/Lisbon');
function do_test(IntlTimeZone $tz, $proc = false) {
var_dump($tz->getID(), $tz->getRawOffset());
if (!$proc)
$dtz = $tz->toDateTimeZone();
else
$dtz = intltz_to_date_time_zone($tz);
var_dump($dtz->getName(), $dtz->getOffset(new DateTime('2012-01-01 00:00:00')));
}
do_test(IntlTimeZone::createTimeZone('CET'));
do_test(IntlTimeZone::createTimeZone('Europe/Amsterdam'));
do_test(IntlTimeZone::createTimeZone('GMT+0405'), true);
--EXPECTF--
string(3) "CET"
int(3600000)
string(13) "Europe/Berlin"
int(3600)
string(16) "Europe/Amsterdam"
int(3600000)
string(16) "Europe/Amsterdam"
int(3600)
string(%s) "GMT+04%s5"
int(14700000)
string(6) "+04:05"
int(14700)

View File

@ -0,0 +1,38 @@
--TEST--
IntlTimeZone::toDateTimeZone(): errors
--SKIPIF--
<?php
if (!extension_loaded('intl'))
die('skip intl extension not enabled');
--FILE--
<?php
ini_set("intl.error_level", E_WARNING);
$tz = IntlTimeZone::createTimeZone('Etc/Unknown');
var_dump($tz->toDateTimeZone(''));
try {
var_dump($tz->toDateTimeZone());
} catch (Exception $e) {
var_dump($e->getMessage());
}
var_dump(intltz_to_date_time_zone());
var_dump(intltz_to_date_time_zone(1));
--EXPECTF--
Warning: IntlTimeZone::toDateTimeZone() expects exactly 0 parameters, 1 given in %s on line %d
Warning: IntlTimeZone::toDateTimeZone(): intltz_to_date_time_zone: bad arguments in %s on line %d
bool(false)
Warning: IntlTimeZone::toDateTimeZone(): intltz_to_date_time_zone: DateTimeZone constructor threw exception in %s on line %d
string(66) "DateTimeZone::__construct(): Unknown or bad timezone (Etc/Unknown)"
Warning: intltz_to_date_time_zone() expects exactly 1 parameter, 0 given in %s on line %d
Warning: intltz_to_date_time_zone(): intltz_to_date_time_zone: bad arguments in %s on line %d
bool(false)
Catchable fatal error: Argument 1 passed to intltz_to_date_time_zone() must be an instance of IntlTimeZone, integer given in %s on line %d

View File

@ -29,6 +29,7 @@ extern "C" {
#include "timezone_class.h"
#include "timezone_methods.h"
#include <zend_exceptions.h>
#include <zend_interfaces.h>
/* avoid redefinition of int8_t, already defined in unicode/pwin32.h */
#define _MSC_STDINT_H_ 1
#include <ext/date/php_date.h>
@ -107,7 +108,7 @@ U_CFUNC TimeZone *timezone_convert_datetimezone(int type,
UnicodeString s = UnicodeString(id, id_len, US_INV);
timeZone = TimeZone::createTimeZone(s);
#if U_ICU_VERSION_MAJOR_NUM >= 49
if (timeZone == TimeZone::getUnknown()) {
if (*timeZone == TimeZone::getUnknown()) {
#else
UnicodeString resultingId;
timeZone->getID(resultingId);
@ -115,7 +116,7 @@ U_CFUNC TimeZone *timezone_convert_datetimezone(int type,
|| resultingId == UnicodeString("GMT", -1, US_INV)) {
#endif
spprintf(&message, 0, "%s: time zone id '%s' "
"extracted from ext/date TimeZone not recognized", func, id);
"extracted from ext/date DateTimeZone not recognized", func, id);
intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
message, 1 TSRMLS_CC);
efree(message);
@ -126,6 +127,72 @@ U_CFUNC TimeZone *timezone_convert_datetimezone(int type,
}
/* }}} */
/* {{{ timezone_convert_to_datetimezone
* Convert from TimeZone to DateTimeZone object */
U_CFUNC zval *timezone_convert_to_datetimezone(const TimeZone *timeZone,
intl_error *outside_error,
const char *func TSRMLS_DC)
{
zval *ret = NULL;
UnicodeString id;
char *message = NULL;
php_timezone_obj *tzobj;
timeZone->getID(id);
if (id.isBogus()) {
spprintf(&message, 0, "%s: could not obtain TimeZone id", func);
intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
message, 1 TSRMLS_CC);
goto error;
}
MAKE_STD_ZVAL(ret);
object_init_ex(ret, php_date_get_timezone_ce());
tzobj = (php_timezone_obj *)zend_objects_get_address(ret TSRMLS_CC);
if (id.compare(0, 3, UnicodeString("GMT", sizeof("GMT")-1, US_INV)) == 0) {
/* The DateTimeZone constructor doesn't support offset time zones,
* so we must mess with DateTimeZone structure ourselves */
tzobj->initialized = 1;
tzobj->type = TIMELIB_ZONETYPE_OFFSET;
//convert offset from milliseconds to minutes
tzobj->tzi.utc_offset = -1 * timeZone->getRawOffset() / (60 * 1000);
} else {
/* Call the constructor! */
zval arg = zval_used_for_init;
Z_TYPE(arg) = IS_STRING;
if (intl_charFromString(id, &Z_STRVAL(arg), &Z_STRLEN(arg),
&INTL_ERROR_CODE(*outside_error)) == FAILURE) {
spprintf(&message, 0, "%s: could not convert id to UTF-8", func);
intl_errors_set(outside_error, INTL_ERROR_CODE(*outside_error),
message, 1 TSRMLS_CC);
goto error;
}
zend_call_method_with_1_params(&ret, NULL, NULL, "__construct",
NULL, &arg);
if (EG(exception)) {
spprintf(&message, 0,
"%s: DateTimeZone constructor threw exception", func);
intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
message, 1 TSRMLS_CC);
zend_object_store_ctor_failed(ret TSRMLS_CC);
goto error;
}
}
return ret;
error:
if (message) {
efree(message);
}
if (ret) {
zval_ptr_dtor(&ret);
}
return NULL;
}
/* }}} */
/* {{{ timezone_process_timezone_argument
* TimeZone argument processor. outside_error may be NULL (for static functions/constructors) */
U_CFUNC TimeZone *timezone_process_timezone_argument(zval **zv_timezone,
@ -400,6 +467,10 @@ ZEND_BEGIN_ARG_INFO_EX(ainfo_tz_idarg, 0, 0, 1)
ZEND_ARG_INFO(0, zoneId)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(ainfo_tz_fromDateTimeZone, 0, 0, 1)
ZEND_ARG_OBJ_INFO(0, otherTimeZone, IntlTimeZone, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(ainfo_tz_createEnumeration, 0, 0, 0)
ZEND_ARG_INFO(0, countryOrRawOffset)
ZEND_END_ARG_INFO()
@ -451,6 +522,7 @@ ZEND_END_ARG_INFO()
*/
static zend_function_entry TimeZone_class_functions[] = {
PHP_ME_MAPPING(createTimeZone, intltz_create_time_zone, ainfo_tz_idarg, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_ME_MAPPING(fromDateTimeZone, intltz_from_date_time_zone, ainfo_tz_idarg, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_ME_MAPPING(createDefault, intltz_create_default, ainfo_tz_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_ME_MAPPING(getGMT, intltz_get_gmt, ainfo_tz_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
#if U_ICU_VERSION_MAJOR_NUM >= 49
@ -475,6 +547,7 @@ static zend_function_entry TimeZone_class_functions[] = {
PHP_ME_MAPPING(hasSameRules, intltz_has_same_rules, ainfo_tz_hasSameRules, ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(getDisplayName, intltz_get_display_name, ainfo_tz_getDisplayName, ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(getDSTSavings, intltz_get_dst_savings, ainfo_tz_void, ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(toDateTimeZone, intltz_to_date_time_zone, ainfo_tz_void, ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(getErrorCode, intltz_get_error_code, ainfo_tz_void, ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(getErrorMessage, intltz_get_error_message, ainfo_tz_void, ZEND_ACC_PUBLIC)
PHP_FE_END

View File

@ -60,6 +60,7 @@ typedef struct {
}
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);
void timezone_object_construct(const TimeZone *zone, zval *object, int owned TSRMLS_DC);

View File

@ -28,6 +28,9 @@ extern "C" {
#include "intl_convert.h"
#include "../locale/locale.h"
#include <zend_exceptions.h>
/* avoid redefinition of int8_t, already defined in unicode/pwin32.h */
#define _MSC_STDINT_H_ 1
#include <ext/date/php_date.h>
}
#include "common/common_enum.h"
@ -57,6 +60,37 @@ U_CFUNC PHP_FUNCTION(intltz_create_time_zone)
timezone_object_construct(tz, return_value, 1 TSRMLS_CC);
}
U_CFUNC PHP_FUNCTION(intltz_from_date_time_zone)
{
zval *zv_timezone;
TimeZone *tz;
php_timezone_obj *tzobj;
intl_error_reset(NULL TSRMLS_CC);
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O",
&zv_timezone, php_date_get_timezone_ce()) == FAILURE) {
intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
"intltz_from_date_time_zone: bad arguments", 0 TSRMLS_CC);
RETURN_NULL();
}
tzobj = (php_timezone_obj *)zend_objects_get_address(zv_timezone TSRMLS_CC);
if (!tzobj->initialized) {
intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
"intltz_from_date_time_zone: DateTimeZone object is unconstructed",
0 TSRMLS_CC);
RETURN_NULL();
}
tz = timezone_convert_datetimezone(tzobj->type, tzobj, FALSE, NULL,
"intltz_from_date_time_zone" TSRMLS_CC);
if (tz == NULL) {
RETURN_NULL();
}
timezone_object_construct(tz, return_value, 1 TSRMLS_CC);
}
U_CFUNC PHP_FUNCTION(intltz_create_default)
{
intl_error_reset(NULL TSRMLS_CC);
@ -549,6 +583,29 @@ U_CFUNC PHP_FUNCTION(intltz_get_dst_savings)
RETURN_LONG((long)to->utimezone->getDSTSavings());
}
U_CFUNC PHP_FUNCTION(intltz_to_date_time_zone)
{
TIMEZONE_METHOD_INIT_VARS;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
"O", &object, TimeZone_ce_ptr) == FAILURE) {
intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
"intltz_to_date_time_zone: bad arguments", 0 TSRMLS_CC);
RETURN_FALSE;
}
TIMEZONE_METHOD_FETCH_OBJECT;
zval *ret = timezone_convert_to_datetimezone(to->utimezone,
&TIMEZONE_ERROR(to), "intltz_to_date_time_zone" TSRMLS_CC);
if (ret) {
RETURN_ZVAL(ret, 1, 1);
} else {
RETURN_FALSE;
}
}
U_CFUNC PHP_FUNCTION(intltz_get_error_code)
{
TIMEZONE_METHOD_INIT_VARS

View File

@ -21,6 +21,8 @@
PHP_FUNCTION(intltz_create_time_zone);
PHP_FUNCTION(intltz_from_date_time_zone);
PHP_FUNCTION(intltz_create_default);
PHP_FUNCTION(intltz_get_id);
@ -55,6 +57,8 @@ PHP_FUNCTION(intltz_get_display_name);
PHP_FUNCTION(intltz_get_dst_savings);
PHP_FUNCTION(intltz_to_date_time_zone);
PHP_FUNCTION(intltz_get_error_code);
PHP_FUNCTION(intltz_get_error_message);