Skip to content

Commit

Permalink
Backport zoneinfo logic into tzfile
Browse files Browse the repository at this point in the history
This moves `tzfile` into its own submodule containing what is
effectively a backport of the pure python implementation of
`zoneinfo.ZoneInfo`, but with the semantics of a `dateutil.tz.tzfile`
object.

The major differences from zoneinfo are:

1. All caching logic is implemented in `tz.gettz` rather than in
   `tz.tzfile`.
2. `tzfile` objects are pickled by value, not by reference to the IANA
   key.
3. `tzfile` has equality-by-value semantics, whereas `zoneinfo` has
   equality-by-identity semantics.

This does change the internal implementation details of the class. It
also fixes at least two bugs:

1. It adds support for V2 and V3 files, which also solves the 2038
   problem (and the "slim zoneinfo" problem).
2. Prior to this change, `dateutil` did not use the `fold` attribute
   during gaps (only during folds), in violation of PEP 495. This fixes
   that, but that does change the semantics of imaginary times a bit
   (and thus may affect some mechanisms for detecting imaginary times).

This is mostly a code dump with compatibility adjustments. It's quite
possible that we'll want to refactor, particularly with respect to the
way POSIX strings are handled. Theoretically `tzfile` could fall back to
a `tz.tzstr` or some other `tz.tzrange`, or we could refactor `tzstr`
and `tzrange` in terms of _CalendarOffset, _DayOffset and _TZStr (or
both, by refactoring the internal logic of `tzstr` and/or `tzrange`).
  • Loading branch information
pganssle committed May 20, 2021
1 parent 41bdf2e commit bd7a0b5
Show file tree
Hide file tree
Showing 3 changed files with 1,046 additions and 552 deletions.
8 changes: 3 additions & 5 deletions dateutil/test/test_tz.py
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,6 @@ def gettz(self, name):
zoneinfo_file = zoneinfo.get_zonefile_instance()
return zoneinfo_file.get(name)

@pytest.mark.xfail(reason="Gap logic currently does not match PEP 495")
def testZoneInfoFileStart1(self):
tzc = self.gettz("EST5EDT")
self.assertEqual(
Expand All @@ -1222,7 +1221,7 @@ def testZoneInfoFileEnd1(self):
self.assertEqual(datetime(2003, 10, 26, 0, 59, tzinfo=tzc).tzname(),
"EDT", MISSING_TARBALL)

end_est = tz.enfold(datetime(2003, 10, 26, 1, 00, tzinfo=tzc), fold=1)
end_est = tz.enfold(datetime(2003, 10, 26, 1, 0, tzinfo=tzc), fold=1)
self.assertEqual(end_est.tzname(), "EST")

def testZoneInfoOffsetSignal(self):
Expand Down Expand Up @@ -2001,7 +2000,6 @@ def testGap(self):


class TZTest(unittest.TestCase):
@pytest.mark.xfail(reason="Gap logic currently does not match PEP 495")
def testFileStart1(self):
tzc = tz.tzfile(BytesIO(base64.b64decode(TZFILE_EST5EDT)))
self.assertEqual(datetime(2003, 4, 6, 1, 59, tzinfo=tzc).tzname(), "EST")
Expand All @@ -2017,7 +2015,7 @@ def testFileEnd1(self):
tzc = tz.tzfile(BytesIO(base64.b64decode(TZFILE_EST5EDT)))
self.assertEqual(datetime(2003, 10, 26, 0, 59, tzinfo=tzc).tzname(),
"EDT")
end_est = tz.enfold(datetime(2003, 10, 26, 1, 00, tzinfo=tzc))
end_est = tz.enfold(datetime(2003, 10, 26, 1, 0, tzinfo=tzc))
self.assertEqual(end_est.tzname(), "EST")

def testFileLastTransition(self):
Expand All @@ -2026,7 +2024,7 @@ def testFileLastTransition(self):
self.assertEqual(datetime(2037, 10, 25, 0, 59, tzinfo=tzc).tzname(),
"EDT")

last_date = tz.enfold(datetime(2037, 10, 25, 1, 00, tzinfo=tzc), fold=1)
last_date = tz.enfold(datetime(2037, 10, 25, 1, 0, tzinfo=tzc), fold=1)
self.assertEqual(last_date.tzname(),
"EST")

Expand Down
Loading

0 comments on commit bd7a0b5

Please sign in to comment.