From ac7822dd9dd0d5cf0855df8a750468d453bc3053 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Mon, 10 Jan 2022 20:32:26 +0100 Subject: [PATCH] fix timestamp formats --- ion.c | 9 +++-- ion.stub.php | 15 ++++++++- ion_arginfo.h | 61 ++++++++++++++++++++++++++++++++-- ion_private.h | 56 +++++++++++++++++++++++-------- tests/Timestamp.phpt | 12 ++++--- tests/serialize/timestamp.phpt | 11 +++--- 6 files changed, 136 insertions(+), 28 deletions(-) diff --git a/ion.c b/ion.c index 81231d6..8bf7e0a 100644 --- a/ion.c +++ b/ion.c @@ -409,13 +409,13 @@ static ZEND_METHOD(ion_Timestamp, __construct) PTR_CHECK(obj); zend_long precision; - zend_object *precision_obj; + zend_object *precision_obj = NULL, *format_obj = NULL; zend_string *fmt = NULL, *dt = NULL; zval *tz = NULL; ZEND_PARSE_PARAMETERS_START(1, 4) Z_PARAM_OBJ_OF_CLASS_OR_LONG(precision_obj, ce_Timestamp_Precision, precision) Z_PARAM_OPTIONAL - Z_PARAM_STR_OR_NULL(fmt) + Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL(format_obj, ce_Timestamp_Format, fmt) Z_PARAM_STR_OR_NULL(dt) Z_PARAM_ZVAL(tz) ZEND_PARSE_PARAMETERS_END(); @@ -423,6 +423,9 @@ static ZEND_METHOD(ion_Timestamp, __construct) if (precision_obj) { precision = Z_LVAL_P(zend_enum_fetch_case_value(precision_obj)); } + if (format_obj) { + fmt = Z_STR_P(zend_enum_fetch_case_value(format_obj)); + } php_ion_timestamp_ctor(obj, precision, fmt, dt, tz); } static ZEND_METHOD(ion_Timestamp, __toString) @@ -1785,6 +1788,7 @@ PHP_MINIT_FUNCTION(ion) if (SUCCESS != g_sym_init()) { return FAILURE; } + g_intern_str_init(); // Catalog php_ion_register(catalog, Catalog, zend_ce_countable); @@ -1826,6 +1830,7 @@ PHP_MINIT_FUNCTION(ion) // Timestamp ce_Timestamp = register_class_ion_Timestamp(php_date_get_date_ce()); + ce_Timestamp_Format = register_class_ion_Timestamp_Format(); ce_Timestamp_Precision = register_class_ion_Timestamp_Precision(); // Type diff --git a/ion.stub.php b/ion.stub.php index 158553e..fd90224 100644 --- a/ion.stub.php +++ b/ion.stub.php @@ -234,6 +234,19 @@ enum Precision : int { case SecTZ = 0x1|0x2|0x4|0x10|0x20|0x80; case FracTZ = 0x1|0x2|0x4|0x10|0x20|0x40|0x80; } + +namespace ion\Timestamp; +enum Format : string { + case Year = "Y\T"; + case Month = "Y-m\T"; + case Day = "Y-m-d\T"; + case Min = "Y-m-d\TH:i"; + case Sec = "Y-m-d\TH:i:s"; + case Frac = "Y-m-d\TH:i:s.v"; + case MinTZ = "Y-m-d\TH:iP"; + case SecTZ = "Y-m-d\TH:i:sP"; + case FracTZ = "Y-m-d\TH:i:s.vP"; +} namespace ion; class Timestamp extends \DateTime { public readonly int $precision; @@ -241,7 +254,7 @@ class Timestamp extends \DateTime { public function __construct( Timestamp\Precision|int $precision, - ?string $format = null, + Timestamp\Format|string|null $format = null, ?string $datetime = null, ?\DateTimeZone $timezone = null, ) {} diff --git a/ion_arginfo.h b/ion_arginfo.h index cdb4a17..895bb87 100644 --- a/ion_arginfo.h +++ b/ion_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ab42c2a137aa474d0da60bcc65144da7a9472948 */ + * Stub hash: 8e04adcc2af90243429a756e14d48507f8411309 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_ion_Symbol_Table_PHP, 0, 0, ion\\Symbol\\Table, 0) ZEND_END_ARG_INFO() @@ -158,7 +158,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ion_Timestamp___construct, 0, 0, 1) ZEND_ARG_OBJ_TYPE_MASK(0, precision, ion\\Timestamp\\Precision, MAY_BE_LONG, NULL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, format, IS_STRING, 1, "null") + ZEND_ARG_OBJ_TYPE_MASK(0, format, ion\\Timestamp\\Format, MAY_BE_STRING|MAY_BE_NULL, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, datetime, IS_STRING, 1, "null") ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null") ZEND_END_ARG_INFO() @@ -776,6 +776,11 @@ static const zend_function_entry class_ion_Timestamp_Precision_methods[] = { }; +static const zend_function_entry class_ion_Timestamp_Format_methods[] = { + ZEND_FE_END +}; + + static const zend_function_entry class_ion_Timestamp_methods[] = { ZEND_ME(ion_Timestamp, __construct, arginfo_class_ion_Timestamp___construct, ZEND_ACC_PUBLIC) ZEND_ME(ion_Timestamp, __toString, arginfo_class_ion_Timestamp___toString, ZEND_ACC_PUBLIC) @@ -1496,6 +1501,58 @@ static zend_class_entry *register_class_ion_Timestamp_Precision(void) return class_entry; } +static zend_class_entry *register_class_ion_Timestamp_Format(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("ion\\Timestamp\\Format", IS_STRING, class_ion_Timestamp_Format_methods); + + zval enum_case_Year_value; + zend_string *enum_case_Year_value_str = zend_string_init("Y\\T", sizeof("Y\\T") - 1, 1); + ZVAL_STR(&enum_case_Year_value, enum_case_Year_value_str); + zend_enum_add_case_cstr(class_entry, "Year", &enum_case_Year_value); + + zval enum_case_Month_value; + zend_string *enum_case_Month_value_str = zend_string_init("Y-m\\T", sizeof("Y-m\\T") - 1, 1); + ZVAL_STR(&enum_case_Month_value, enum_case_Month_value_str); + zend_enum_add_case_cstr(class_entry, "Month", &enum_case_Month_value); + + zval enum_case_Day_value; + zend_string *enum_case_Day_value_str = zend_string_init("Y-m-d\\T", sizeof("Y-m-d\\T") - 1, 1); + ZVAL_STR(&enum_case_Day_value, enum_case_Day_value_str); + zend_enum_add_case_cstr(class_entry, "Day", &enum_case_Day_value); + + zval enum_case_Min_value; + zend_string *enum_case_Min_value_str = zend_string_init("Y-m-d\\TH:i", sizeof("Y-m-d\\TH:i") - 1, 1); + ZVAL_STR(&enum_case_Min_value, enum_case_Min_value_str); + zend_enum_add_case_cstr(class_entry, "Min", &enum_case_Min_value); + + zval enum_case_Sec_value; + zend_string *enum_case_Sec_value_str = zend_string_init("Y-m-d\\TH:i:s", sizeof("Y-m-d\\TH:i:s") - 1, 1); + ZVAL_STR(&enum_case_Sec_value, enum_case_Sec_value_str); + zend_enum_add_case_cstr(class_entry, "Sec", &enum_case_Sec_value); + + zval enum_case_Frac_value; + zend_string *enum_case_Frac_value_str = zend_string_init("Y-m-d\\TH:i:s.v", sizeof("Y-m-d\\TH:i:s.v") - 1, 1); + ZVAL_STR(&enum_case_Frac_value, enum_case_Frac_value_str); + zend_enum_add_case_cstr(class_entry, "Frac", &enum_case_Frac_value); + + zval enum_case_MinTZ_value; + zend_string *enum_case_MinTZ_value_str = zend_string_init("Y-m-d\\TH:iP", sizeof("Y-m-d\\TH:iP") - 1, 1); + ZVAL_STR(&enum_case_MinTZ_value, enum_case_MinTZ_value_str); + zend_enum_add_case_cstr(class_entry, "MinTZ", &enum_case_MinTZ_value); + + zval enum_case_SecTZ_value; + zend_string *enum_case_SecTZ_value_str = zend_string_init("Y-m-d\\TH:i:sP", sizeof("Y-m-d\\TH:i:sP") - 1, 1); + ZVAL_STR(&enum_case_SecTZ_value, enum_case_SecTZ_value_str); + zend_enum_add_case_cstr(class_entry, "SecTZ", &enum_case_SecTZ_value); + + zval enum_case_FracTZ_value; + zend_string *enum_case_FracTZ_value_str = zend_string_init("Y-m-d\\TH:i:s.vP", sizeof("Y-m-d\\TH:i:s.vP") - 1, 1); + ZVAL_STR(&enum_case_FracTZ_value, enum_case_FracTZ_value_str); + zend_enum_add_case_cstr(class_entry, "FracTZ", &enum_case_FracTZ_value); + + return class_entry; +} + static zend_class_entry *register_class_ion_Timestamp(zend_class_entry *class_entry_DateTime) { zend_class_entry ce, *class_entry; diff --git a/ion_private.h b/ion_private.h index 600f18b..acb2403 100644 --- a/ion_private.h +++ b/ion_private.h @@ -128,6 +128,26 @@ LOCAL int g_sym_init(void) return SUCCESS; } +static struct { + zend_string *Year, *Month, *Day, *Min, *Sec, *Frac, *MinTZ, *SecTZ, *FracTZ; +} g_intern_str; + +static void g_intern_str_init() +{ +#define NEW_INTERN_STR(s) \ + g_intern_str.s = zend_string_init_interned(#s, sizeof(#s)-1, 1) + NEW_INTERN_STR(Year); + NEW_INTERN_STR(Month); + NEW_INTERN_STR(Day); + NEW_INTERN_STR(Min); + NEW_INTERN_STR(Sec); + NEW_INTERN_STR(Frac); + NEW_INTERN_STR(MinTZ); + NEW_INTERN_STR(SecTZ); + NEW_INTERN_STR(FracTZ); +#undef NEW_INTERN_STR +} + typedef struct php_ion_serializer { ION_WRITER *writer; ION_WRITER_OPTIONS *options; @@ -223,6 +243,7 @@ static zend_class_entry *ce_Symbol_Table_Shared, *ce_Symbol_Table_System, *ce_Timestamp, + *ce_Timestamp_Format, *ce_Timestamp_Precision, *ce_Type, *ce_Unserializer, @@ -860,23 +881,34 @@ LOCAL decQuad *ion_ts_frac_from_usec(decQuad *frac, int usec, decContext *ctx) return decQuadDivide(frac, decQuadFromInt32(&us, usec), decQuadFromInt32(µsecs, 1000000), ctx); } +LOCAL zend_string *php_ion_timestamp_format_fetch(zend_string *fmt_case) +{ + return Z_STR_P(zend_enum_fetch_case_value(zend_enum_get_case(ce_Timestamp_Format, fmt_case))); +} + LOCAL zend_string *php_dt_format_from_precision(uint8_t precision) { - switch (precision & 0x7f) { + switch (precision) { + case ION_TS_FRAC | 0x80: + return php_ion_timestamp_format_fetch(g_intern_str.FracTZ); case ION_TS_FRAC: - return zend_string_init(ZEND_STRL("c"), 0); + return php_ion_timestamp_format_fetch(g_intern_str.Frac); + case ION_TS_SEC | 0x80: + return php_ion_timestamp_format_fetch(g_intern_str.SecTZ); case ION_TS_SEC: - return zend_string_init(ZEND_STRL("Y-m-d\\TH:i:sP"), 0); + return php_ion_timestamp_format_fetch(g_intern_str.Sec); + case ION_TS_MIN | 0x80: + return php_ion_timestamp_format_fetch(g_intern_str.MinTZ); case ION_TS_MIN: - return zend_string_init(ZEND_STRL("Y-m-d\\TH:iP"), 0); + return php_ion_timestamp_format_fetch(g_intern_str.Min); case ION_TS_DAY: - return zend_string_init(ZEND_STRL("Y-m-d\\T"), 0); + return php_ion_timestamp_format_fetch(g_intern_str.Day); case ION_TS_MONTH: - return zend_string_init(ZEND_STRL("Y-m\\T"), 0); + return php_ion_timestamp_format_fetch(g_intern_str.Month); case ION_TS_YEAR: - return zend_string_init(ZEND_STRL("Y\\T"), 0); + return php_ion_timestamp_format_fetch(g_intern_str.Year); default: - return zend_string_init(ZEND_STRL("c"), 0); + return zend_one_char_string['c']; } } @@ -884,9 +916,7 @@ LOCAL timelib_time* php_time_from_ion(const ION_TIMESTAMP *ts, decContext *ctx, { timelib_time *time = ecalloc(1, sizeof(*time)); - int precision = ION_TS_FRAC; - ion_timestamp_get_precision(ts, &precision); - switch (precision) { + switch (ts->precision & 0x7f) { case ION_TS_FRAC: time->us = php_usec_from_ion(&ts->fraction, ctx); /* fallthrough */ @@ -908,7 +938,7 @@ LOCAL timelib_time* php_time_from_ion(const ION_TIMESTAMP *ts, decContext *ctx, /* fallthrough */ default: time->z = ts->tz_offset * 60; - if (time->z) { + if (time->z || ts->precision & 0x80) { time->zone_type = TIMELIB_ZONETYPE_OFFSET; } else { time->zone_type = TIMELIB_ZONETYPE_ID; @@ -917,7 +947,7 @@ LOCAL timelib_time* php_time_from_ion(const ION_TIMESTAMP *ts, decContext *ctx, } if (fmt) { - *fmt = php_dt_format_from_precision(precision); + *fmt = php_dt_format_from_precision(ts->precision); } return time; } diff --git a/tests/Timestamp.phpt b/tests/Timestamp.phpt index c0f0171..e92905b 100644 --- a/tests/Timestamp.phpt +++ b/tests/Timestamp.phpt @@ -18,8 +18,9 @@ $full = "2021-12-07T14:08:51.123456+00:00"; var_dump($t=new Timestamp(Timestamp\Precision::Day, datetime:$full),(string)$t); var_dump($t=new Timestamp(Timestamp\Precision::Day->value, datetime:$full),(string)$t); var_dump($t=new Timestamp(Timestamp\Precision::Min, datetime:"2020-10-01"),(string)$t); +$t->setTimezone(new DateTimeZone("Europe/Helsinki")); +var_dump((string) $t); var_dump($t=new Timestamp(Timestamp\Precision::Day, "!Y-m", "2000-10"),(string)$t); - var_dump(ion\unserialize(ion\serialize(clone new ion\Timestamp(ion\Timestamp\Precision::Sec, DateTime::RFC3339, "1971-02-03T04:05:06Z")))); ?> DONE @@ -56,7 +57,7 @@ object(ion\Timestamp)#%d (5) { ["precision"]=> int(23) ["format"]=> - string(11) "Y-m-d\TH:iP" + string(10) "Y-m-d\TH:i" ["date"]=> string(26) "2020-10-01 00:00:00.000000" ["timezone_type"]=> @@ -64,7 +65,8 @@ object(ion\Timestamp)#%d (5) { ["timezone"]=> string(3) "CET" } -string(22) "2020-10-01T00:00+02:00" +string(16) "2020-10-01T00:00" +string(16) "2020-10-01T01:00" object(ion\Timestamp)#%d (5) { ["precision"]=> int(7) @@ -78,11 +80,11 @@ object(ion\Timestamp)#%d (5) { string(3) "CET" } string(11) "2000-10-01T" -object(ion\Timestamp)#8 (3) { +object(ion\Timestamp)#%d (3) { ["precision"]=> int(55) ["format"]=> - string(13) "Y-m-d\TH:i:sP" + string(12) "Y-m-d\TH:i:s" ["date"]=> string(26) "1971-02-03 04:05:06.000000" } diff --git a/tests/serialize/timestamp.phpt b/tests/serialize/timestamp.phpt index 21941af..396bbb5 100644 --- a/tests/serialize/timestamp.phpt +++ b/tests/serialize/timestamp.phpt @@ -6,7 +6,7 @@ ion TEST int(247) ["format"]=> - string(1) "c" + string(15) "Y-m-d\TH:i:s.vP" ["date"]=> string(26) "1971-02-03 04:05:06.789000" } +string(29) "1971-02-03T04:05:06.789+00:00" string(24) "1971-02-03T04:05:06.789Z" -object(ion\Timestamp)#6 (3) { +object(ion\Timestamp)#%d (3) { ["precision"]=> int(247) ["format"]=> - string(1) "c" + string(15) "Y-m-d\TH:i:s.vP" ["date"]=> string(26) "1971-02-03 04:05:06.789000" } -- 2.30.2