Skip to content

Commit

Permalink
Redo the synonym handling completely, and now only use zoneinfo symlinks
Browse files Browse the repository at this point in the history
- It turns out a lot of Linux distributions make the links between zoneinfo
  aliases backwards, so instead of linking GB to Europe/London it actually
  links the other way. When /etc/localtime then links to Europe/London, and you
  also have a config file saying Europe/London, the code that checks if
  /etc/localtime is a symlink ends up at GB instead of Europe/London and
  we get an error, as it thinks GB and Europe/London are different zones.

  So now we check the symlink of all timezones in the uniqueness test. We still
  return the name in the config file, though, so you would only get GB or Zulu
  returned as the time zone instead of Europe/London or UTC if your only
  configuration is the /etc/localtime symlink, as that's checked last, and
  tzlocal will return the first configuration found.

- The above change also means that GMT and UTC are no longer seen as synonyms,
  as zoneinfo does not see them as synonyms. This might be controversial,
  but you just have to live with it. Pick one and stay with it. ;-)
  • Loading branch information
regebro committed Oct 28, 2021
1 parent f1049d2 commit 4d25c45
Show file tree
Hide file tree
Showing 9 changed files with 33 additions and 14 deletions.
17 changes: 16 additions & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,22 @@ Changes
4.0.3 (unreleased)
------------------

- Nothing changed yet.
- It turns out a lot of Linux distributions make the links between zoneinfo
aliases backwards, so instead of linking GB to Europe/London it actually
links the other way. When /etc/localtime then links to Europe/London, and you
also have a config file saying Europe/London, the code that checks if
/etc/localtime is a symlink ends up at GB instead of Europe/London and
we get an error, as it thinks GB and Europe/London are different zones.

So now we check the symlink of all timezones in the uniqueness test. We still
return the name in the config file, though, so you would only get GB or Zulu
returned as the time zone instead of Europe/London or UTC if your only
configuration is the /etc/localtime symlink, as that's checked last, and
tzlocal will return the first configuration found.

- The above change also means that GMT and UTC are no longer seen as synonyms,
as zoneinfo does not see them as synonyms. This might be controversial,
but you just have to live with it. Pick one and stay with it. ;-)


4.0.2 (2021-10-26)
Expand Down
1 change: 1 addition & 0 deletions tests/test_data/noconflict/etc/localtime
2 changes: 1 addition & 1 deletion tests/test_data/noconflict/etc/timezone
Original file line number Diff line number Diff line change
@@ -1 +1 @@
UTC# We allow comments. It's unusual, but has happened
Etc/UTC# We allow comments. It's unusual, but has happened
1 change: 1 addition & 0 deletions tests/test_data/noconflict/usr/share/zoneinfo/Etc/UCT
Binary file not shown.
1 change: 1 addition & 0 deletions tests/test_data/noconflict/usr/share/zoneinfo/UTC
1 change: 1 addition & 0 deletions tests/test_data/noconflict/usr/share/zoneinfo/Zulu
5 changes: 4 additions & 1 deletion tests/test_tzlocal.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,12 @@ def test_conflicting():
assert "localtime is a symlink to: Africa/Harare" in message


@pytest.mark.skipif(
platform.system() == "Windows", reason="Symbolic links are not available on Windows"
)
def test_noconflict():
tz = tzlocal.unix._get_localzone(_root=tz_path("noconflict"))
assert str(tz) == "UTC"
assert str(tz) == "Etc/UTC"


def test_pytz_compatibility():
Expand Down
19 changes: 8 additions & 11 deletions tzlocal/unix.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,15 @@ def _get_localzone_name(_root="/"):
if len(found_configs) > 1:
# Uh-oh, multiple configs. See if they match:
unique_tzs = set()
zoneinfo = os.path.join(_root, "usr", "share", "zoneinfo")
directory_depth = len(zoneinfo.split(os.path.sep))

for tzname in found_configs.values():
# Get rid of any Etc's
tzname = tzname.replace("Etc/", "")
# Let's handle these synonyms as well. Many systems have tons
# of synonyms, including country names and other non-zoneinfo
# nonsense. Those will be seen as different ones. Let's stick
# to the official zoneinfo Continent/City names.
if tzname in ["GMT0", "GMT+0", "GMT-0"]:
tzname = "GMT"
if tzname in ["UCT", "Zulu"]:
tzname = "UTC"
unique_tzs.add(tzname)
# Look them up in /usr/share/zoneinfo, and find what they
# really point to:
path = os.path.realpath(os.path.join(zoneinfo, *tzname.split("/")))
real_zone_name = "/".join(path.split(os.path.sep)[directory_depth:])
unique_tzs.add(real_zone_name)

if len(unique_tzs) != 1:
message = "Multiple conflicting time zone configurations found:\n"
Expand Down

0 comments on commit 4d25c45

Please sign in to comment.