15
15
*/
16
16
17
17
#include <math.h>
18
+ #include <inttypes.h>
18
19
19
20
#include <php.h>
20
21
#include <zend_smart_str.h>
@@ -213,8 +214,9 @@ static PHP_METHOD(MongoDB_BSON_UTCDateTime, toDateTime)
213
214
{
214
215
php_phongo_utcdatetime_t * intern ;
215
216
php_date_obj * datetime_obj ;
216
- char * sec ;
217
+ char * sec_str ;
217
218
size_t sec_len ;
219
+ int64_t sec , usec ;
218
220
219
221
intern = Z_UTCDATETIME_OBJ_P (getThis ());
220
222
@@ -223,11 +225,23 @@ static PHP_METHOD(MongoDB_BSON_UTCDateTime, toDateTime)
223
225
object_init_ex (return_value , php_date_get_date_ce ());
224
226
datetime_obj = Z_PHPDATE_P (return_value );
225
227
226
- sec_len = spprintf (& sec , 0 , "@%" PRId64 , intern -> milliseconds / 1000 );
227
- php_date_initialize (datetime_obj , sec , sec_len , NULL , NULL , 0 );
228
- efree (sec );
228
+ sec = intern -> milliseconds / 1000 ;
229
+ usec = (llabs (intern -> milliseconds ) % 1000 ) * 1000 ;
230
+ if (intern -> milliseconds < 0 && usec != 0 ) {
231
+ /* For dates before the unix epoch, we need to subtract the microseconds from the timestamp.
232
+ * Since we can't directly pass microseconds when calling php_date_initialize due to a bug in PHP,
233
+ * we manually decrement the timestamp and subtract the number of microseconds from a full seconds
234
+ * to store in the us field. */
235
+ sec -- ;
236
+ usec = 1000000 - usec ;
237
+ }
238
+
239
+ /* TODO PHP 8.1.7+: microseconds can be included in the format string */
240
+ sec_len = spprintf (& sec_str , 0 , "@%" PRId64 , sec );
241
+ php_date_initialize (datetime_obj , sec_str , sec_len , NULL , NULL , 0 );
242
+ efree (sec_str );
229
243
230
- datetime_obj -> time -> us = ( intern -> milliseconds % 1000 ) * 1000 ;
244
+ datetime_obj -> time -> us = usec ;
231
245
}
232
246
233
247
static PHP_METHOD (MongoDB_BSON_UTCDateTime , jsonSerialize )
0 commit comments