]> granicus.if.org Git - php/commitdiff
Added IntlTimeZone::fromDateTimeZone() and ::toDateTimeZone.
authorGustavo André dos Santos Lopes <cataphract@php.net>
Mon, 30 Apr 2012 13:15:09 +0000 (15:15 +0200)
committerGustavo André dos Santos Lopes <cataphract@php.net>
Thu, 17 May 2012 15:23:51 +0000 (17:23 +0200)
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.

ext/intl/php_intl.c
ext/intl/tests/timezone_fromDateTimeZone_basic.phpt [new file with mode: 0644]
ext/intl/tests/timezone_fromDateTimeZone_error.phpt [new file with mode: 0644]
ext/intl/tests/timezone_toDateTimeZone_basic.phpt [new file with mode: 0644]
ext/intl/tests/timezone_toDateTimeZone_error.phpt [new file with mode: 0644]
ext/intl/timezone/timezone_class.cpp
ext/intl/timezone/timezone_class.h
ext/intl/timezone/timezone_methods.cpp
ext/intl/timezone/timezone_methods.h

index 51aece69d55462e0558116cbce724a3ec5a3497b..4924fdef391929f243702def75cd4cb78aaaff97 100755 (executable)
@@ -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 )
 
diff --git a/ext/intl/tests/timezone_fromDateTimeZone_basic.phpt b/ext/intl/tests/timezone_fromDateTimeZone_basic.phpt
new file mode 100644 (file)
index 0000000..10e2621
--- /dev/null
@@ -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)
diff --git a/ext/intl/tests/timezone_fromDateTimeZone_error.phpt b/ext/intl/tests/timezone_fromDateTimeZone_error.phpt
new file mode 100644 (file)
index 0000000..4d6c153
--- /dev/null
@@ -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
diff --git a/ext/intl/tests/timezone_toDateTimeZone_basic.phpt b/ext/intl/tests/timezone_toDateTimeZone_basic.phpt
new file mode 100644 (file)
index 0000000..d22aa68
--- /dev/null
@@ -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)
diff --git a/ext/intl/tests/timezone_toDateTimeZone_error.phpt b/ext/intl/tests/timezone_toDateTimeZone_error.phpt
new file mode 100644 (file)
index 0000000..e48d7ac
--- /dev/null
@@ -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
index 70323687379e4eb9daa901649df4db95250c7cf9..31893fe4910c90a763d970c9858720fe2a13d759 100644 (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
index d5fabb92805999186f8ae35e75c253fb027139ac..0d3c0edde4721dfbd312138063b08b524d6dab60 100644 (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);
index eaa6b46e3940b2868ed9442ba4d2cdee1529c2ae..e596cc5174a82409a7e2e4a96b3dfda4173c14d8 100644 (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
index 207caa3cb065c5745e7b8143469e6ed6c26adba6..824f72a0d21b7539d8f5b3118bd247264cfac245 100644 (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);