From 5f1028fdfb7c9c52f8d30c2c766100d1c7113838 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Fri, 7 May 2021 12:59:03 +1000 Subject: [PATCH] Avoid COM dates that end up with ms=1000, instead rounding to the next second. Fixes #1655. (#1683) --- CHANGES.txt | 4 ++++ com/win32com/test/testPyComTest.py | 4 ++++ win32/src/PyTime.cpp | 9 +++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 658445ff2..566d0b7c3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -11,6 +11,10 @@ Since build 300: * Fix a bug in `win32profile.GetEnvironmentStrings()` relating to environment variables with an equals sign (@maxim-krikun in #1661) +* Fixed a bug where certain COM dates would fail to be converted to a Python + datetime object with `ValueError: microsecond must be in 0..999999`. Shoutout + to @hujiaxing for reporting and helping reproduce the issue (#1655) + * Added win32com.shell.SHGetKnownFolderPath() and related constants. * Shifted work in win32.lib.pywin32_bootstrap to Python's import system from diff --git a/com/win32com/test/testPyComTest.py b/com/win32com/test/testPyComTest.py index f8394c1bc..0c58bacd7 100644 --- a/com/win32com/test/testPyComTest.py +++ b/com/win32com/test/testPyComTest.py @@ -273,6 +273,10 @@ def TestCommon(o, is_generated): later = now + datetime.timedelta(seconds=1) TestApplyResult(o.EarliestDate, (now, later), now) + # The below used to fail with `ValueError: microsecond must be in 0..999999` - see #1655 + # https://planetcalc.com/7027/ says that float is: Sun, 25 Mar 1951 7:23:49 am + assert o.MakeDate(18712.308206013888) == datetime.datetime.fromisoformat("1951-03-25 07:23:49+00:00") + progress("Checking currency") # currency. pythoncom.__future_currency__ = 1 diff --git a/win32/src/PyTime.cpp b/win32/src/PyTime.cpp index 335152382..57d7b9e69 100644 --- a/win32/src/PyTime.cpp +++ b/win32/src/PyTime.cpp @@ -380,11 +380,16 @@ PyObject *PyWin_NewTime(PyObject *timeOb) double minutes = (hours - (int)hours) * 60.0; double seconds = round((minutes - (int)minutes) * 60.0, 4); double milliseconds = round((seconds - (int)seconds) * 1000.0, 0); - // assert(milliseconds>=0.0 && milliseconds<=999.0); - // Strip off the msec part of time double TimeWithoutMsecs = t - (ONETHOUSANDMILLISECONDS / 1000.0 * milliseconds); + // We might have rounded ms to 1000 which blows up datetime. Round up + // to the next second. + if (milliseconds >= 1000) { + TimeWithoutMsecs += ONETHOUSANDMILLISECONDS; + milliseconds = 0; + } + // Let the OS translate the variant date/time SYSTEMTIME st; if (!VariantTimeToSystemTime(TimeWithoutMsecs, &st)) {