From 5fa84513311ea29999780126d727e463e02d7e8e Mon Sep 17 00:00:00 2001 From: Andrei Bacescu <9010245+bacescuandrei@users.noreply.github.com> Date: Sun, 25 Oct 2026 10:50:39 +0200 Subject: [PATCH 1/2] Fixed time of day of negative timestamps with a fraction of second _GetTimeValues truncated the number of seconds towards zero, so for a negative normalized timestamp with a fraction of second, such as PosixTimeInMilliseconds(-500), GetDateWithTimeOfDay returned 1970-01-01 00:00:00 instead of 1969-12-31 23:59:59. The same applied to CocoaTime.CopyToDateTimeString, which combined the truncated seconds with the floored fraction of second (timestamp % 1), so -0.25 was formatted as 2001-01-01 00:00:00.750000 instead of 2000-12-31 23:59:59.750000. The number of seconds is now rounded down. --- dfdatetime/cocoa_time.py | 3 ++- dfdatetime/interface.py | 3 ++- tests/cocoa_time.py | 5 +++++ tests/interface.py | 9 +++++++++ tests/posix_time.py | 5 +++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/dfdatetime/cocoa_time.py b/dfdatetime/cocoa_time.py index 418a8ff..7261ae9 100644 --- a/dfdatetime/cocoa_time.py +++ b/dfdatetime/cocoa_time.py @@ -1,6 +1,7 @@ """Cocoa timestamp implementation.""" import decimal +import math from dfdatetime import definitions from dfdatetime import factory @@ -121,7 +122,7 @@ def CopyToDateTimeString(self): return None number_of_days, hours, minutes, seconds = self._GetTimeValues( - int(self._timestamp) + math.floor(self._timestamp) ) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH diff --git a/dfdatetime/interface.py b/dfdatetime/interface.py index cf5eac4..bca2975 100644 --- a/dfdatetime/interface.py +++ b/dfdatetime/interface.py @@ -2,6 +2,7 @@ import abc import decimal +import math from dfdatetime import definitions @@ -875,7 +876,7 @@ def _GetTimeValues(self, number_of_seconds): Returns: tuple[int, int, int, int]: days, hours, minutes, seconds. """ - number_of_seconds = int(number_of_seconds) + number_of_seconds = math.floor(number_of_seconds) number_of_minutes, seconds = divmod(number_of_seconds, 60) number_of_hours, minutes = divmod(number_of_minutes, 60) number_of_days, hours = divmod(number_of_hours, 24) diff --git a/tests/cocoa_time.py b/tests/cocoa_time.py index 4b20c03..f67e8ce 100644 --- a/tests/cocoa_time.py +++ b/tests/cocoa_time.py @@ -101,6 +101,11 @@ def testCopyToDateTimeString(self): date_time_string = cocoa_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) + cocoa_time_object = cocoa_time.CocoaTime(timestamp=-0.25) + + date_time_string = cocoa_time_object.CopyToDateTimeString() + self.assertEqual(date_time_string, "2000-12-31 23:59:59.750000") + def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" cocoa_time_object = cocoa_time.CocoaTime(timestamp=395011845.546875) diff --git a/tests/interface.py b/tests/interface.py index 93640ab..bd303a4 100644 --- a/tests/interface.py +++ b/tests/interface.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 """Tests for the date and time interfaces.""" +import decimal import unittest from dfdatetime import definitions @@ -725,6 +726,14 @@ def testGetTimeValues(self): self.assertEqual(minutes, 56) self.assertEqual(seconds, 50) + days, hours, minutes, seconds = date_time_values._GetTimeValues( + decimal.Decimal("-0.5") + ) + self.assertEqual(days, -1) + self.assertEqual(hours, 23) + self.assertEqual(minutes, 59) + self.assertEqual(seconds, 59) + days, hours, minutes, seconds = date_time_values._GetTimeValues(-190990) self.assertEqual(days, -3) self.assertEqual(hours, 18) diff --git a/tests/posix_time.py b/tests/posix_time.py index e59436f..edee130 100644 --- a/tests/posix_time.py +++ b/tests/posix_time.py @@ -337,6 +337,11 @@ def testGetDateWithTimeOfDay(self): date_with_time_of_day_tuple = posix_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) + posix_time_object = posix_time.PosixTimeInMilliseconds(timestamp=-500) + + date_with_time_of_day_tuple = posix_time_object.GetDateWithTimeOfDay() + self.assertEqual(date_with_time_of_day_tuple, (1969, 12, 31, 23, 59, 59)) + posix_time_object = posix_time.PosixTimeInMilliseconds() date_with_time_of_day_tuple = posix_time_object.GetDateWithTimeOfDay() From eb180d608920a21ece873b53c13f22023123777a Mon Sep 17 00:00:00 2001 From: Andrei Bacescu <9010245+bacescuandrei@users.noreply.github.com> Date: Sun, 25 Oct 2026 15:14:08 +0200 Subject: [PATCH 2/2] Fixed time of day of negative Delphi and OLE Automation dates DelphiDateTime and OLEAutomationDate also truncated the number of seconds towards zero before determining the date and time values, while the fraction of second is floored with % 1, so DelphiDateTime(-0.000001) was formatted as 1899-12-30 00:00:00.913600 instead of 1899-12-29 23:59:59.913600. The number of seconds is now rounded down. --- dfdatetime/delphi_date_time.py | 3 ++- dfdatetime/ole_automation_date.py | 5 ++++- tests/delphi_date_time.py | 4 ++++ tests/ole_automation_date.py | 6 ++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dfdatetime/delphi_date_time.py b/dfdatetime/delphi_date_time.py index 7065026..03cb302 100644 --- a/dfdatetime/delphi_date_time.py +++ b/dfdatetime/delphi_date_time.py @@ -1,6 +1,7 @@ """Delphi TDateTime implementation.""" import decimal +import math from dfdatetime import definitions from dfdatetime import factory @@ -129,7 +130,7 @@ def CopyToDateTimeString(self): number_of_seconds = self._timestamp * definitions.SECONDS_PER_DAY number_of_days, hours, minutes, seconds = self._GetTimeValues( - int(number_of_seconds) + math.floor(number_of_seconds) ) # The maximum date supported by TDateTime values is limited to: # 9999-12-31 23:59:59.999 (approximate 2958465 days since epoch). diff --git a/dfdatetime/ole_automation_date.py b/dfdatetime/ole_automation_date.py index 291c93b..5510066 100644 --- a/dfdatetime/ole_automation_date.py +++ b/dfdatetime/ole_automation_date.py @@ -1,6 +1,7 @@ """OLE automation date (or Floatingtime or Application time) implementation.""" import decimal +import math from dfdatetime import definitions from dfdatetime import factory @@ -130,7 +131,9 @@ def CopyToDateTimeString(self): timestamp = self._timestamp * definitions.SECONDS_PER_DAY - number_of_days, hours, minutes, seconds = self._GetTimeValues(int(timestamp)) + number_of_days, hours, minutes, seconds = self._GetTimeValues( + math.floor(timestamp) + ) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH diff --git a/tests/delphi_date_time.py b/tests/delphi_date_time.py index a36043c..7e214df 100644 --- a/tests/delphi_date_time.py +++ b/tests/delphi_date_time.py @@ -138,6 +138,10 @@ def testCopyToDateTimeString(self): date_time_string = delphi_date_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, "2013-06-18 19:50:00.553919") + delphi_date_time_object = delphi_date_time.DelphiDateTime(timestamp=-0.000001) + date_time_string = delphi_date_time_object.CopyToDateTimeString() + self.assertEqual(date_time_string, "1899-12-29 23:59:59.913600") + delphi_date_time_object = delphi_date_time.DelphiDateTime() date_time_string = delphi_date_time_object.CopyToDateTimeString() diff --git a/tests/ole_automation_date.py b/tests/ole_automation_date.py index d66268b..49cdbb7 100644 --- a/tests/ole_automation_date.py +++ b/tests/ole_automation_date.py @@ -101,6 +101,12 @@ def testCopyToDateTimeString(self): date_time_string = ole_automation_date_object.CopyToDateTimeString() self.assertEqual(date_time_string, "2017-11-05 11:32:00.038400") + ole_automation_date_object = ole_automation_date.OLEAutomationDate( + timestamp=-0.000001 + ) + date_time_string = ole_automation_date_object.CopyToDateTimeString() + self.assertEqual(date_time_string, "1899-12-29 23:59:59.913600") + ole_automation_date_object = ole_automation_date.OLEAutomationDate() date_time_string = ole_automation_date_object.CopyToDateTimeString()