From 39745878fa2fea8e70fe8f58ab5110026e689ae2 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Mon, 2 Sep 2024 13:44:52 +0200 Subject: [PATCH 1/3] PHPC-2429: Fix UTCDateTime with negative timestamps --- src/BSON/UTCDateTime.c | 23 ++++++++++--- .../bson/bson-utcdatetime-todatetime-003.phpt | 33 +++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 tests/bson/bson-utcdatetime-todatetime-003.phpt diff --git a/src/BSON/UTCDateTime.c b/src/BSON/UTCDateTime.c index c9f8d1eb4..c9f7c6cc0 100644 --- a/src/BSON/UTCDateTime.c +++ b/src/BSON/UTCDateTime.c @@ -213,8 +213,9 @@ static PHP_METHOD(MongoDB_BSON_UTCDateTime, toDateTime) { php_phongo_utcdatetime_t* intern; php_date_obj* datetime_obj; - char* sec; + char* sec_str; size_t sec_len; + int64_t sec, usec; intern = Z_UTCDATETIME_OBJ_P(getThis()); @@ -223,11 +224,23 @@ static PHP_METHOD(MongoDB_BSON_UTCDateTime, toDateTime) object_init_ex(return_value, php_date_get_date_ce()); datetime_obj = Z_PHPDATE_P(return_value); - sec_len = spprintf(&sec, 0, "@%" PRId64, intern->milliseconds / 1000); - php_date_initialize(datetime_obj, sec, sec_len, NULL, NULL, 0); - efree(sec); + sec = intern->milliseconds / 1000; + usec = (llabs(intern->milliseconds) % 1000) * 1000; + if (intern->milliseconds < 0 && usec != 0) { + /* For dates before the unix epoch, we need to subtract the microseconds from the timestamp. + * Since we can't directly pass microseconds when calling php_date_initialize due to a bug in PHP, + * we manually decrement the timestamp and subtract the number of microseconds from a full seconds + * to store in the us field. */ + sec--; + usec = 1000000 - usec; + } + + /* TODO PHP 8.1.6+: microseconds can be included in the format string */ + sec_len = spprintf(&sec_str, 0, "@%" PRId64, sec); + php_date_initialize(datetime_obj, sec_str, sec_len, NULL, NULL, 0); + efree(sec_str); - datetime_obj->time->us = (intern->milliseconds % 1000) * 1000; + datetime_obj->time->us = usec; } static PHP_METHOD(MongoDB_BSON_UTCDateTime, jsonSerialize) diff --git a/tests/bson/bson-utcdatetime-todatetime-003.phpt b/tests/bson/bson-utcdatetime-todatetime-003.phpt new file mode 100644 index 000000000..af94b18be --- /dev/null +++ b/tests/bson/bson-utcdatetime-todatetime-003.phpt @@ -0,0 +1,33 @@ +--TEST-- +MongoDB\BSON\UTCDateTime::toDateTime() with dates before the Unix epoch +--INI-- +date.timezone=UTC +--FILE-- +format(DateTimeInterface::RFC3339_EXTENDED), PHP_EOL; + + $utcDateTime = new MongoDB\BSON\UTCDateTime($dateTime); + + $newDate = $utcDateTime->toDateTime(); + echo $newDate->format(DateTimeInterface::RFC3339_EXTENDED), PHP_EOL, PHP_EOL; +} + +?> +===DONE=== + +--EXPECT-- +1960-01-01T12:12:12.100+00:00 +1960-01-01T12:12:12.100+00:00 + +1969-12-31T23:59:59.999+00:00 +1969-12-31T23:59:59.999+00:00 + +===DONE=== From 0f54fdfb9a3ad9122cddef69a5030d45b8f83fab Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Tue, 3 Sep 2024 08:42:49 +0200 Subject: [PATCH 2/3] Add missing include for llabs --- src/BSON/UTCDateTime.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BSON/UTCDateTime.c b/src/BSON/UTCDateTime.c index c9f7c6cc0..36287b8c6 100644 --- a/src/BSON/UTCDateTime.c +++ b/src/BSON/UTCDateTime.c @@ -15,6 +15,7 @@ */ #include +#include #include #include From 41e3b9a04a5097002331af1b9b640d248f30deea Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Tue, 3 Sep 2024 15:54:01 +0200 Subject: [PATCH 3/3] Fix wrong version number in comment --- src/BSON/UTCDateTime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BSON/UTCDateTime.c b/src/BSON/UTCDateTime.c index 36287b8c6..0bcb7eb24 100644 --- a/src/BSON/UTCDateTime.c +++ b/src/BSON/UTCDateTime.c @@ -236,7 +236,7 @@ static PHP_METHOD(MongoDB_BSON_UTCDateTime, toDateTime) usec = 1000000 - usec; } - /* TODO PHP 8.1.6+: microseconds can be included in the format string */ + /* TODO PHP 8.1.7+: microseconds can be included in the format string */ sec_len = spprintf(&sec_str, 0, "@%" PRId64, sec); php_date_initialize(datetime_obj, sec_str, sec_len, NULL, NULL, 0); efree(sec_str);