Skip to content

Commit

Permalink
PHPC-2429: Fix UTCDateTime with negative timestamps
Browse files Browse the repository at this point in the history
  • Loading branch information
alcaeus committed Sep 2, 2024
1 parent f47915e commit 3974587
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 5 deletions.
23 changes: 18 additions & 5 deletions src/BSON/UTCDateTime.c
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand All @@ -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)
Expand Down
33 changes: 33 additions & 0 deletions tests/bson/bson-utcdatetime-todatetime-003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
MongoDB\BSON\UTCDateTime::toDateTime() with dates before the Unix epoch
--INI--
date.timezone=UTC
--FILE--
<?php

$dates = [
'1960-01-01 12:12:12.1',
'1969-12-31 23:59:59.999',
];

foreach ($dates as $date) {
$dateTime = new \DateTimeImmutable($date);
echo $dateTime->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===
<?php exit(0); ?>
--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===

0 comments on commit 3974587

Please sign in to comment.