Edgewall Software
Modify

Opened 5 years ago

Closed 5 years ago

Last modified 3 years ago

#11563 closed defect (fixed)

Three datefmt.to_datetime() tests fail when TZ=Europe/London

Reported by: Alex Willmerer <al.willmer@…> Owned by: Jun Omae
Priority: normal Milestone: 0.12.6
Component: general Version: 1.1.1dev
Severity: normal Keywords: timezone
Cc: Branch:
Release Notes:

Fix LocalTimezone with date/time on DST boundaries and timezone changes different from current standard and daylight timezones.

API Changes:

Description (last modified by Ryan J Ollos)

Three unit tests in source:/trunk/trac/util/tests/datefmt.py fail in latest trunk (r12624 on Ubuntu 13.10/Python 2.7.5, set to the UK timezone. trac.util.to_datetime(<n>) returns a datetime that is 1 hour ahead of that returned by datetime.fromtimestamp(<n>), where n is a POSIX timestamp.

In January 1970 the UK was 1 hour ahead of UTC. This was changed in October 1971, since then the UK has been on GMT (UTC+0) during winter months.

(venv)$ PYTHONPATH=. python trac/util/tests/datefmt.py
................F.FF...................................................................................
======================================================================
FAIL: test_to_datetime (__main__.DateFormatTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "trac/util/tests/datefmt.py", line 716, in test_to_datetime
    self.assertEqual(datefmt.to_datetime(23), expected)
AssertionError: datetime.datetime(1970, 1, 1, 2, 0, 23, tzinfo=<LocalTimezone "GMT" 0:00:00>) != datetime.datetime(1970, 1, 1, 1, 0, 23, tzinfo=<LocalTimezone "GMT" 0:00:00>)

======================================================================
FAIL: test_to_datetime_microsecond_negative_timestamps (__main__.DateFormatTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "trac/util/tests/datefmt.py", line 734, in test_to_datetime_microsecond_negative_timestamps
    self.assertEqual(datefmt.to_datetime(-2345678912), expected)
AssertionError: datetime.datetime(1970, 1, 1, 1, 20, 54, 321088, tzinfo=<LocalTimezone "GMT" 0:00:00>) != datetime.datetime(1970, 1, 1, 0, 20, 54, 321088, tzinfo=<LocalTimezone "GMT" 0:00:00>)

======================================================================
FAIL: test_to_datetime_microsecond_timestamps (__main__.DateFormatTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "trac/util/tests/datefmt.py", line 723, in test_to_datetime_microsecond_timestamps
    self.assertEqual(datefmt.to_datetime(2345678912), expected)
AssertionError: datetime.datetime(1970, 1, 1, 2, 39, 5, 678912, tzinfo=<LocalTimezone "GMT" 0:00:00>) != datetime.datetime(1970, 1, 1, 1, 39, 5, 678912, tzinfo=<LocalTimezone "GMT" 0:00:00>)

----------------------------------------------------------------------
Ran 103 tests in 0.355s

FAILED (failures=3)

Other timezones are not affected (checked by Jun Omae)

$ git status -sb
## 0.12-stable
$ TZ=Europe/London ~/venv/py24/bin/python setup.py test -s trac.util.tests.datefmt.suite ...
FAILED (failures=3)
$ TZ=GMT ~/venv/py24/bin/python setup.py test -s trac.util.tests.datefmt.suite ...
OK
$ TZ=Europe/Berlin ~/venv/py24/bin/python setup.py test -s trac.util.tests.datefmt.suite ...
OK

This was initially discussed in trac-dev:wzKUgAe38PU/hJEP8mwfRc4J.

Attachments (0)

Change History (8)

comment:1 by Jun Omae, 5 years ago

Milestone: 0.12.6
Owner: set to Jun Omae
Status: newassigned

Proposed changes in jomae.git@t11563.

This issue occurs when the date/time has timezone which is different from time.timezone and time.altzone.

time std dst
now GMT BST (GMT+0100)
1968 - 1971 BST (GMT+0100) n/a

comment:2 by Jun Omae, 5 years ago

Improved unit tests in [500f3a78/jomae.git].

comment:3 by Alex Willmer <al.willmer@…>, 5 years ago

All trac.util tests pass for me

(venv)~/src/d4/jomae$ git branch
* t11563
  trunk
(venv)~/src/d4/jomae$ python setup.py test -s trac.util.tests.suite
running test
running egg_info
writing requirements to Trac.egg-info/requires.txt
writing Trac.egg-info/PKG-INFO
writing top-level names to Trac.egg-info/top_level.txt
writing dependency_links to Trac.egg-info/dependency_links.txt
writing entry points to Trac.egg-info/entry_points.txt
reading manifest file 'Trac.egg-info/SOURCES.txt'
writing manifest file 'Trac.egg-info/SOURCES.txt'
running build_ext
test_existing (trac.util.tests.AtomicFileTestCase) ... ok
test_existing_open_for_reading (trac.util.tests.AtomicFileTestCase) ... ok
...
test_tracerror_with_tgettext (trac.util.tests.html.ToFragmentTestCase) ... ok
test_unicode (trac.util.tests.html.ToFragmentTestCase) ... ok

----------------------------------------------------------------------
Ran 169 tests in 0.587s

OK

comment:4 by Jun Omae, 5 years ago

I noticed that another issue on DST boundaries and timezone changes, e.g. 2011-03-27 02:00 and 2010-10-31 03:00 in Moscow. It's different between pytz's timezone and localtz in trac.util.datefmt.

>>> import os
>>> from datetime import datetime
>>> from trac.util.datefmt import to_datetime, get_timezone, localtz
>>> os.environ['TZ']
'Europe/Moscow'
>>> localtz
<LocalTimezone "MSK" 4:00:00 "MSK" 4:00:00>
>>> tz = get_timezone('Europe/Moscow')
>>>
>>> t = datetime(2011, 3, 27, 2)
>>> to_datetime(t, tz)
datetime.datetime(2011, 3, 27, 3, 0, tzinfo=<DstTzInfo 'Europe/Moscow' MSK+4:00:00 STD>)
>>> to_datetime(t, localtz)
datetime.datetime(2011, 3, 27, 1, 0, tzinfo=<LocalTimezone "MSK" 4:00:00>)
>>>
>>> t = datetime(2010, 10, 31, 3)
>>> to_datetime(t, tz)
datetime.datetime(2010, 10, 31, 3, 0, tzinfo=<DstTzInfo 'Europe/Moscow' MSK+3:00:00 STD>)
>>> to_datetime(t, localtz)
datetime.datetime(2010, 10, 31, 2, 0, tzinfo=<LocalTimezone "MSK" 4:00:00>)

I updated the branch jomae.git@t11563 and added more unit tests.

>>> t = datetime(2011, 3, 27, 2)
>>> to_datetime(t, tz)
datetime.datetime(2011, 3, 27, 3, 0, tzinfo=<DstTzInfo 'Europe/Moscow' MSK+4:00:00 STD>)
>>> to_datetime(t, localtz)
datetime.datetime(2011, 3, 27, 3, 0, tzinfo=<LocalTimezone "MSK" 4:00:00>)
>>>
>>> t = datetime(2010, 10, 31, 3)
>>> to_datetime(t, tz)
datetime.datetime(2010, 10, 31, 3, 0, tzinfo=<DstTzInfo 'Europe/Moscow' MSK+3:00:00 STD>)
>>> to_datetime(t, localtz)
datetime.datetime(2010, 10, 31, 3, 0, tzinfo=<LocalTimezone "UTC+03:00" 3:00:00>)

In the branch, all unit tests pass with Python 2.4-2.7 on i386 and x86-64.

I'll push it tonight.

comment:5 by Jun Omae, 5 years ago

Release Notes: modified (diff)
Resolution: fixed
Status: assignedclosed

Committed in [12633] and merged in [12634-12635].

comment:6 by Ryan J Ollos, 5 years ago

Description: modified (diff)

comment:7 by Ryan J Ollos, 3 years ago

Proposed refactoring to address confusing line and unpythonic is True:

  • trac/util/datefmt.py

    diff --git a/trac/util/datefmt.py b/trac/util/datefmt.py
    index 659b7f7..4f8bac2 100644
    a b class LocalTimezone(tzinfo):  
    10221022            if not std_correct:
    10231023                raise ValueError('Non existent time "%s"' % dt)
    10241024        tt = None
    1025         if std_correct is dst_correct is True:
     1025        if std_correct and dst_correct:
    10261026            tt = local_tt[bool(is_dst)]
    1027         elif std_correct is True:
     1027        elif std_correct:
    10281028            tt = local_tt[0]
    1029         elif dst_correct is True:
     1029        elif dst_correct:
    10301030            tt = local_tt[1]
    10311031        if tt:
    10321032            utc_ts = to_timestamp(datetime(tzinfo=utc, *tt[:6]))

Unit tests pass for me after the change.

comment:8 by anonymous, 3 years ago

comment:7 changes committed in [14732].

Modify Ticket

Change Properties
Set your email in Preferences
Action
as closed The owner will remain Jun Omae.
The resolution will be deleted. Next status will be 'reopened'.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.