zopefoundation / DateTime

This package provides a DateTime data type, as known from Zope. Unless you need to communicate with Zope APIs, you're probably better off using Python's built-in datetime module.
Other
19 stars 25 forks source link

tests fail after 2038-01-10 #41

Closed bmwiedemann closed 2 years ago

bmwiedemann commented 2 years ago

BUG/PROBLEM REPORT (OR OTHER COMMON ISSUE)

What I did:

run tests on 2038-01-10 on openSUSE, I do

osc co openSUSE:Factory/python-DateTime && cd $_
osc build --vm-type=kvm --noservice --clean --build-opt=--vm-custom-opt="-rtc base=2038-01-10T00:00:00" --alternative-project=home:bmwiedemann:reproducible openSUSE_Tumbleweed

What I expect to happen:

tests should continue to pass in future

What actually happened:

3 tests failed:

     def testAddPrecision(self):  
         # Precision of serial additions
         dt = DateTime()          
 >       self.assertEqual(str(dt + 0.10 + 3.14 + 6.76 - 10), str(dt),
                          dt)              
 E       AssertionError: '2038/10/07 08:52:44.959838 UTC' != '2038/10/07 08:52:44.959840 UTC'    
 E       - 2038/10/07 08:52:44.959838 UTC
 E       ?                         ^^
 E       + 2038/10/07 08:52:44.959840 UTC
 E       ?                         ^^
 E        : 2038/10/07 08:52:44.959840 UTC

 FAILED src/DateTime/tests/test_datetime.py::DateTimeTests::testAddPrecision
 FAILED src/DateTime/tests/test_datetime.py::DateTimeTests::testConstructor4
 FAILED src/DateTime/tests/test_datetime.py::DateTimeTests::testSubtraction - ...

What version of Python and Zope/Addons I am using:

openSUSE-Tumbleweed 20220907 python-3.8

d-maurer commented 2 years ago

I tried to reproduce your problem and failed:

>>> from DateTime import DateTime
>>> dt=DateTime(2038, 10, 7,8,52,44.959838, "UTC")
>>> dt
DateTime('2038/10/07 08:52:44.959838 UTC')
>>> xdt = dt + 0.10 + 3.14 + 6.76 - 10
>>> xdt
DateTime('2038/10/07 08:52:44.959838 UTC')
>>> dt == xdt
True

Your observation is almost surely due to a rounding effect. Recently, rounding problems caused by a side effect of a compilation with the -Ofast option have been discussed. DateTime is not itself compiled but the side effects are (process) global and can effect rounding everywhere in the process.

At your place, I would:

  1. ensure that your Python has not been compiled with -Ofast; if necessary, compile it yourself and ensure a proper CFLAGS
  2. perform the DateTime computation in a Python interpreter which only imports DateTime (and nothing else).
bmwiedemann commented 2 years ago

I found a reproducer:

from DateTime import DateTime
dt = DateTime(2038, 10, 7,8,52,44.959840, "UTC")
xdt = dt + 0.10 + 3.14 + 6.76 - 10
dt == xdt

Our CFLAGS contain -O2 and no -Ofast

d-maurer commented 2 years ago

Bernhard M. Wiedemann wrote at 2022-9-12 21:00 -0700:

I found a reproducer:

from DateTime import DateTime
dt = DateTime(2038, 10, 7,8,52,44.959840, "UTC")
xdt = dt + 0.10 + 3.14 + 6.76 - 10
dt == xdt

The error is located near the end of the _parse_args method:

        sc = round(sc, 6)
    ...
        self._hour, self._minute, self._second = hr, mn, sc
    ...
        if microsecs is None:
            microsecs = long(math.floor(t * 1000000.0))
        self._micros = microsecs

While the _second computation rounds to the nearest microsecond, the microsecond computation floors instead. This leads to inconsistent _second and _micros values.

The __add__ implementation is based on _micros. The error happens due to an inconsistent _micros value after the + 0.10. In addition, __add__ is somewhat suboptimal: it knows the correct microsecond value but does not pass it; instead it lets it be computed (wrongly) by _parse_args.

Thank you for reporting this bug.

icemac commented 2 years ago

Just released the fix as https://pypi.org/project/DateTime/4.7/

bmwiedemann commented 1 year ago

I verified, that it is indeed fixed in DateTime-4.7.