pax_global_header00006660000000000000000000000064145152573110014516gustar00rootroot0000000000000052 comment=6e9ffca1ffbd9c9ecc974c4ca45ee3c2912ef7c9 tzlocal-5.2/000077500000000000000000000000001451525731100130345ustar00rootroot00000000000000tzlocal-5.2/.github/000077500000000000000000000000001451525731100143745ustar00rootroot00000000000000tzlocal-5.2/.github/workflows/000077500000000000000000000000001451525731100164315ustar00rootroot00000000000000tzlocal-5.2/.github/workflows/test.yml000066400000000000000000000020161451525731100201320ustar00rootroot00000000000000name: Run the test suite on: push: branches: [master] pull_request: jobs: test: strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3.8"] exclude: - os: macos-latest python-version: 3.8 - os: macos-latest python-version: pypy3 - os: windows-latest python-version: 3.8 - os: windows-latest python-version: pypy3 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - uses: actions/cache@v3 with: path: ~/.cache/pip key: pip-test-${{ matrix.python-version }}-${{ matrix.os }} - name: Install dependencies run: pip install .[devenv] - name: Test with pytest run: pytest tzlocal-5.2/.gitignore000066400000000000000000000000501451525731100150170ustar00rootroot00000000000000*.pyc *.wpr *.wpu *.egg-info *.egg .tox tzlocal-5.2/.pre-commit-config.yaml000066400000000000000000000007701451525731100173210ustar00rootroot00000000000000repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.0.292 hooks: - id: ruff args: [--fix, --show-fixes] - repo: https://github.com/psf/black-pre-commit-mirror rev: 23.9.1 hooks: - id: black - repo: https://github.com/regebro/pyroma rev: '4.2' hooks: - id: pyroma tzlocal-5.2/CHANGES.txt000066400000000000000000000217521451525731100146540ustar00rootroot00000000000000Changes ======= 5.2 (2023-10-22) ---------------- - Added a pre-commit config - Updated python versions [hugovk] - Added type hints for the public functions - Moved to using pyproject.toml for project config. 5.1 (2023-10-04) ---------------- - The Unicode data doesn't change tz names when IANA does, so what is current in IANA is treated as an alias in Unicode data. This version handles that. 5.0.1 (2023-05-15) ------------------ - The logging info under windows made it look like it looked up the registry info even when you had a TZ environment, but it doesn't actually do that. - Improved the handling of loggers. 5.0 (2023-05-14) ---------------- - Fixed a bug in the new assert_tz_offset method. 5.0b2 (2023-04-11) ------------------ - Change how the system offset is calculated to deal with non-DST temporary changes, such as Ramadan time in Morocco. - Change the default to only warn when the timezone offset and system offset disagree (but still not even warn if TZ is set) - Add the assert_tz_offset() method to the top level for those who want to explicitly check and fail. 5.0b1 (2023-04-07) ------------------ - Removed the deprecation shim. 4.4b1 (2023-03-20) ------------------ - Added debug logging 4.3 (2023-03-18) ---------------- - Improved the error message when the ZoneInfo cannot be found - Don't error out because we find multiple possible timezones for a symlink. - More stable on Android/Termux with proot 4.2 (2022-04-02) ---------------- - If TZ environment variable is set to /etc/localhost, and that's a link to a zoneinfo file, then tzlocal will now find the timezone name, and not just return a localtime TZ object. 4.1 (2021-10-29) ---------------- - No changes from 4.1b1. 4.1b1 (2021-10-28) ------------------ - 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) ------------------ - Improved the error message when you had a conflict including a /etc/localtime symlink. 4.0.1 (2021-10-19) ------------------ - A long time bug in Ubuntu docker images seem to not get fixed, so I added a workaround. 4.0.1b1 (2021-10-18) -------------------- - Handle UCT and Zulu as synonyms for UTC, while treating GMT and UTC as different. 4.0 (2021-10-18) ---------------- - No changes. 4.0b5 (2021-10-18) ------------------ - Fixed a bug in the Windows DST support. 4.0b4 (2021-10-18) ------------------ - Added support for turning off DST in Windows. That only works in whole hour timezones, and honestly, if you need to turn off DST, you should just use UTC as a timezone. 4.0b3 (2021-10-08) ------------------ - Returning pytz_deprecation_shim zones to lower the surprise for pytz users. - The Windows OS environment variable 'TZ' will allow an override for setting the timezone. The override timezone will be asserted for timezone validity bit not compared against the systems timezone offset. This allows for overriding the timezone when running tests. - Dropped support for Windows 2000, XP and Vista, supports Windows 7, 8 and 10. 4.0b2 (2021-09-26) ------------------ - Big refactor; Implemented get_localzone_name() functions. - Adding a Windows OS environment variable 'TZ' will allow an override for setting the timezone (also see 4.0b3). 4.0b1 (2021-08-21) ------------------ - Now finds and compares all the configs (under Unix-like systems) and tells you what files it found and how they conflict. This should make it a lot easier to figure out what goes wrong. 3.0 (2021-08-13) ---------------- - Modernized the packaging, moving to setup.cfg etc. - Handles if the text config files incorrectly is a TZ file. (revanSZ) 3.0b1 (2020-09-21) ------------------ - Dropped Python 2 support - Switched timezone provider from pytz to zoneinfo (PEP 615) 2.1 (2020-05-08) ---------------- - No changes. 2.1b1 (2020-02-08) ------------------ - The is_dst flag is wrong for Europe/Dublin on some Unix releases. I changed to another way of determining if DST is in effect or not. - Added support for Python 3.7 and 3.8. Dropped 3.5 although it still works. 2.0.0 (2019-07-23) ------------------ - No differences since 2.0.0b3 Major differences since 1.5.1 ............................. - When no time zone configuration can be find, tzlocal now return UTC. This is a major difference from 1.x, where an exception would be raised. This change is because Docker images often have no configuration at all, and the unix utilities will then default to UTC, so we follow that. - If tzlocal on Unix finds a timezone name in a /etc config file, then tzlocal now verifies that the timezone it fouds has the same offset as the local computer is configured with. If it doesn't, something is configured incorrectly. (Victor Torres, regebro) - Get timezone via Termux `getprop` wrapper on Android. It's not officially supported because we can't test it, but at least we make an effort. (Jean Jordaan) Minor differences and bug fixes ............................... - Skip comment lines when parsing /etc/timezone. (Edward Betts) - Don't load timezone from current directory. (Gabriel Corona) - Now verifies that the config files actually contain something before reading them. (Zackary Welch, regebro) - Got rid of a BytesWarning (Mickaël Schoentgen) - Now handles if config file paths exists, but are directories. - Moved tests out from distributions - Support wheels 1.5.1 (2017-12-01) ------------------ - 1.5 had a bug that slipped through testing, fixed that, increased test coverage. 1.5 (2017-11-30) ---------------- - No longer treats macOS as special, but as a unix. - get_windows_info.py is renamed to update_windows_mappings.py - Windows mappings now also contain mappings from deprecated zoneinfo names. (Preston-Landers, regebro) 1.4 (2017-04-18) ---------------- - I use MIT on my other projects, so relicensing. 1.4b1 (2017-04-14) ------------------ - Dropping support for Python versions nobody uses (2.5, 3.1, 3.2), adding 3.6 Python 3.1 and 3.2 still works, 2.5 has been broken for some time. - Ayalash's OS X fix didn't work on Python 2.7, fixed that. 1.3.2 (2017-04-12) ------------------ - Ensure closing of subprocess on OS X (ayalash) - Removed unused imports (jwilk) - Closes stdout and stderr to get rid of ResourceWarnings (johnwquarles) - Updated Windows timezones (axil) 1.3 (2016-10-15) ---------------- - #34: Added support for /var/db/zoneinfo 1.2.2 (2016-03-02) ------------------ - #30: Fixed a bug on OS X. 1.2.1 (2016-02-28) ------------------ - Tests failed if TZ was set in the environment. (EdwardBetts) - Replaces os.popen() with subprocess.Popen() for OS X to handle when systemsetup doesn't exist. (mckabi, cewing) 1.2 (2015-06-14) ---------------- - Systemd stores no time zone name, forcing us to look at the name of the file that localtime symlinks to. (cameris) 1.1.2 (2014-10-18) ------------------ - Timezones that has 3 items did not work on Mac OS X. (Marc Van Olmen) - Now doesn't fail if the TZ environment variable isn't an Olsen time zone. - Some timezones on Windows can apparently be empty (perhaps the are deleted). Now these are ignored. (Xiaokun Zhu) 1.1.1 (2014-01-29) ------------------ - I forgot to add Etc/UTC as an alias for Etc/GMT. 1.1 (2014-01-28) ---------------- - Adding better support for OS X. - Added support to map from tzdata/Olsen names to Windows names. (Thanks to Benjamen Meyer). 1.0 (2013-05-29) ---------------- - Fixed some more cases where spaces needs replacing with underscores. - Better handling of misconfigured /etc/timezone. - Better error message on Windows if we can't find a timezone at all. 0.3 (2012-09-13) ---------------- - Windows 7 support. - Python 2.5 supported; because it only needed a __future__ import. - Python 3.3 tested, it worked. - Got rid of relative imports, because I don't actually like them, so I don't know why I used them in the first place. - For each Windows zone, use the default zoneinfo zone, not the last one. 0.2 (2012-09-12) ---------------- - Python 3 support. 0.1 (2012-09-11) ---------------- - Initial release. tzlocal-5.2/LICENSE.txt000066400000000000000000000020441451525731100146570ustar00rootroot00000000000000Copyright 2011-2017 Lennart Regebro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tzlocal-5.2/MANIFEST.in000066400000000000000000000002121451525731100145650ustar00rootroot00000000000000include *.py include *.rst include *.txt include tzlocal/py.typed recursive-include tests/test_data * include tests/*.py exclude Makefile tzlocal-5.2/Makefile000066400000000000000000000013671451525731100145030ustar00rootroot00000000000000root_dir := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) bin_dir := $(root_dir)/ve/bin all: devenv test # The fullrelease script is a part of zest.releaser, which is the last # package installed, so if it exists, the devenv is installed. devenv: ve/bin/fullrelease ve/bin/fullrelease: virtualenv ve $(bin_dir)/pip install -e .[devenv] update_mapping: $(bin_dir)/python update_windows_mappings.py check: $(bin_dir)/black tzlocal tests $(bin_dir)/flake8 tzlocal tests $(bin_dir)/pyroma -d . coverage: $(bin_dir)/coverage run $(bin_dir)/pytest $(bin_dir)/coverage html $(bin_dir)/coverage report test: $(bin_dir)/pytest release: update_mapping check $(bin_dir)/fullrelease clean: rm -rf ve .coverage htmlcov build .pytest_cache tzlocal-5.2/README.rst000066400000000000000000000144011451525731100145230ustar00rootroot00000000000000tzlocal ======= API CHANGE! ----------- With version 3.0 of tzlocal, tzlocal no longer returned `pytz` objects, but `zoneinfo` objects, which has a different API. Since 4.0, it now restored partial compatibility for `pytz` users through Paul Ganssle's `pytz_deprecation_shim`. tzlocal 4.0 also adds an official function `get_localzone_name()` to get only the timezone name, instead of a timezone object. On unix, it can raise an error if you don't have a timezone name configured, where `get_localzone()` will succeed, so only use that if you need the timezone name. 4.0 also adds way more information on what is going wrong in your configuration when the configuration files are unclear or contradictory. Version 5.0 removes the `pytz_deprecation_shim`, and now only returns `zoneinfo` objects, like verion 3.0 did. If you need `pytz` objects, you have to stay on version 4.0. If there are bugs in version 4.0, I will release updates, but there will be no further functional changes on the 4.x branch. Info ---- This Python module returns the `IANA time zone name `_ for your local time zone or a ``tzinfo`` object with the local timezone information, under Unix and Windows. It requires Python 3.8 or later, and will use the ``backports.tzinfo`` package, for Python 3.8. This module attempts to fix a glaring hole in the ``pytz`` and ``zoneinfo`` modules, that there is no way to get the local timezone information, unless you know the zoneinfo name, and under several Linux distros that's hard or impossible to figure out. With ``tzlocal`` you only need to call ``get_localzone()`` and you will get a ``tzinfo`` object with the local time zone info. On some Unices you will still not get to know what the timezone name is, but you don't need that when you have the tzinfo file. However, if the timezone name is readily available it will be used. What it's not for ----------------- It's not for converting the current time between UTC and your local time. There are other, simpler ways of doing this. This is ig you need to know things like the name of the time zone, or if you need to be able to convert between your time zone and another time zone for times that are in the future or in the past. For current time conversions to and from UTC, look in the Python ``time`` module. Supported systems ----------------- These are the systems that are in theory supported: * Windows 2000 and later * Any unix-like system with a ``/etc/localtime`` or ``/usr/local/etc/localtime`` If you have one of the above systems and it does not work, it's a bug. Please report it. Please note that if you are getting a time zone called ``local``, this is not a bug, it's actually the main feature of ``tzlocal``, that even if your system does NOT have a configuration file with the zoneinfo name of your time zone, it will still work. You can also use ``tzlocal`` to get the name of your local timezone, but only if your system is configured to make that possible. ``tzlocal`` looks for the timezone name in ``/etc/timezone``, ``/var/db/zoneinfo``, ``/etc/sysconfig/clock`` and ``/etc/conf.d/clock``. If your ``/etc/localtime`` is a symlink it can also extract the name from that symlink. If you need the name of your local time zone, then please make sure your system is properly configured to allow that. If your unix system doesn't have a timezone configured, tzlocal will default to UTC. Notes on Docker --------------- It turns out that Docker images frequently have broken timezone setups. This usually resuts in a warning that the configuration is wrong, or that the timezone offset doesn't match the found timezone. The easiest way to fix that is to set a TZ variable in your docker setup to whatever timezone you want, which is usually the timezone your host computer has. Usage ----- Load the local timezone: >>> from tzlocal import get_localzone >>> tz = get_localzone() >>> tz zoneinfo.ZoneInfo(key='Europe/Warsaw') Create a local datetime: >>> from datetime import datetime >>> dt = datetime(2015, 4, 10, 7, 22, tzinfo=tz) >>> dt datetime.datetime(2015, 4, 10, 7, 22, tzinfo=zoneinfo.ZoneInfo(key='Europe/Warsaw')) Lookup another timezone with ``zoneinfo`` (``backports.zoneinfo`` on Python 3.8 or earlier): >>> from zoneinfo import ZoneInfo >>> eastern = ZoneInfo('US/Eastern') Convert the datetime: >>> dt.astimezone(eastern) datetime.datetime(2015, 4, 10, 1, 22, tzinfo=zoneinfo.ZoneInfo(key='US/Eastern')) If you just want the name of the local timezone, use `get_localzone_name()`: >>> from tzlocal import get_localzone_name >>> get_localzone_name() "Europe/Warsaw" Please note that under Unix, `get_localzone_name()` may fail if there is no zone configured, where `get_localzone()` would generally succeed. Troubleshooting --------------- If you don't get the result you expect, try running it with debugging turned on. Start a python interpreter that has tzlocal installed, and run the following code:: import logging logging.basicConfig(level="DEBUG") import tzlocal tzlocal.get_localzone() The output should look something like this, and this will tell you what configurations were found:: DEBUG:root:/etc/timezone found, contents: Europe/Warsaw DEBUG:root:/etc/localtime found DEBUG:root:2 found: {'/etc/timezone': 'Europe/Warsaw', '/etc/localtime is a symlink to': 'Europe/Warsaw'} zoneinfo.ZoneInfo(key='Europe/Warsaw') Development ----------- For ease of development, there is a Makefile that will help you with basic tasks, like creating a development environment with all the necessary tools (although you need a supported Python version installed first):: $ make devenv To run tests:: $ make test Check the syntax:: $ make check Maintainer ---------- * Lennart Regebro, regebro@gmail.com Contributors ------------ * Marc Van Olmen * Benjamen Meyer * Manuel Ebert * Xiaokun Zhu * Cameris * Edward Betts * McK KIM * Cris Ewing * Ayala Shachar * Lev Maximov * Jakub Wilk * John Quarles * Preston Landers * Victor Torres * Jean Jordaan * Zackary Welch * Mickaël Schoentgen * Gabriel Corona * Alex Grönholm * Julin S * Miroslav Å edivý * revansSZ * Sam Treweek * Peter Di Pasquale * Rongrong (Sorry if I forgot someone) License ------- * MIT https://opensource.org/licenses/MIT tzlocal-5.2/pyproject.toml000066400000000000000000000032271451525731100157540ustar00rootroot00000000000000[build-system] requires = ["setuptools >= 64"] build-backend = "setuptools.build_meta" [project] name = "tzlocal" description = "tzinfo object for the local timezone" readme = "README.rst" version = "5.2" authors = [{name = "Lennart Regebro", email = "regebro@gmail.com"}] license = {text = "MIT"} keywords = ["timezone"] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Operating System :: Microsoft :: Windows", "Operating System :: Unix", "Operating System :: MacOS :: MacOS X", "Typing :: Typed", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", ] requires-python = ">= 3.8" dependencies = [ "backports.zoneinfo; python_version < '3.9'", "tzdata; platform_system == 'Windows'", ] [project.urls] "Source code" = "https://github.com/regebro/tzlocal" Changelog = "https://github.com/regebro/tzlocal/blob/master/CHANGES.txt" "Issue tracker" = "https://github.com/regebro/tzlocal/issues" [project.optional-dependencies] devenv = [ "pytest >= 4.3", "pytest-mock >= 3.3", "pytest-cov", "check_manifest", "zest.releaser", ] [tool.ruff] line-length = 120 select = [ "E", "F", "W", # default flake-8 "I", # isort "ISC", # flake8-implicit-str-concat "PGH", # pygrep-hooks "RUF100", # unused noqa (yesqa) "UP", # pyupgrade ] ignore = ["UP015"] [tool.zest.releaser] create-wheel = true [tool.pytest.ini_options] testpaths = "tests" tzlocal-5.2/tests/000077500000000000000000000000001451525731100141765ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/000077500000000000000000000000001451525731100161465ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/Africa/000077500000000000000000000000001451525731100173335ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/Africa/Harare000066400000000000000000000002351451525731100204600ustar00rootroot00000000000000TZif2‚FÇd LMTCATTZif2ÿÿÿÿ‚FÇd LMTCAT CAT-2 tzlocal-5.2/tests/test_data/UTC000066400000000000000000000001661451525731100165270ustar00rootroot00000000000000TZif2UTCTZif2UTC UTC0 tzlocal-5.2/tests/test_data/conflicting/000077500000000000000000000000001451525731100204455ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/conflicting/etc/000077500000000000000000000000001451525731100212205ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/conflicting/etc/conf.d/000077500000000000000000000000001451525731100223675ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/conflicting/etc/conf.d/clock000066400000000000000000000000411451525731100234000ustar00rootroot00000000000000TIMEZONE = "Africa/Johannesburg" tzlocal-5.2/tests/test_data/conflicting/etc/localtime000077700000000000000000000000001451525731100313762../usr/share/zoneinfo/Africa/Harareustar00rootroot00000000000000tzlocal-5.2/tests/test_data/conflicting/etc/sysconfig/000077500000000000000000000000001451525731100232245ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/conflicting/etc/sysconfig/clock000066400000000000000000000000251451525731100242370ustar00rootroot00000000000000ZONE="Europe/Warsaw" tzlocal-5.2/tests/test_data/conflicting/etc/timezone000066400000000000000000000001001451525731100227640ustar00rootroot00000000000000Europe/Paris# We allow comments. It's unusual, but has happened tzlocal-5.2/tests/test_data/conflicting/usr/000077500000000000000000000000001451525731100212565ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/conflicting/usr/share/000077500000000000000000000000001451525731100223605ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/conflicting/usr/share/zoneinfo/000077500000000000000000000000001451525731100242075ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/conflicting/usr/share/zoneinfo/Africa/000077500000000000000000000000001451525731100253745ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/conflicting/usr/share/zoneinfo/Africa/Harare000066400000000000000000000002351451525731100265210ustar00rootroot00000000000000TZif2‚FÇd LMTCATTZif2ÿÿÿÿ‚FÇd LMTCAT CAT-2 tzlocal-5.2/tests/test_data/conflicting/var/000077500000000000000000000000001451525731100212355ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/conflicting/var/db/000077500000000000000000000000001451525731100216225ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/conflicting/var/db/zoneinfo000066400000000000000000000001231451525731100233700ustar00rootroot00000000000000America/New_York localhost # The host is not a part of the format, but is allowed. tzlocal-5.2/tests/test_data/localtime/000077500000000000000000000000001451525731100201175ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/localtime/etc/000077500000000000000000000000001451525731100206725ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/localtime/etc/localtime000066400000000000000000000002351451525731100225660ustar00rootroot00000000000000TZif2‚FÇd LMTCATTZif2ÿÿÿÿ‚FÇd LMTCAT CAT-2 tzlocal-5.2/tests/test_data/noconflict/000077500000000000000000000000001451525731100203045ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/noconflict/etc/000077500000000000000000000000001451525731100210575ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/noconflict/etc/conf.d/000077500000000000000000000000001451525731100222265ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/noconflict/etc/conf.d/clock000066400000000000000000000000251451525731100232410ustar00rootroot00000000000000TIMEZONE = "Etc/UCT" tzlocal-5.2/tests/test_data/noconflict/etc/localtime000077700000000000000000000000001451525731100276052../usr/share/zoneinfo/Zuluustar00rootroot00000000000000tzlocal-5.2/tests/test_data/noconflict/etc/sysconfig/000077500000000000000000000000001451525731100230635ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/noconflict/etc/sysconfig/clock000066400000000000000000000000141451525731100240740ustar00rootroot00000000000000ZONE="Zulu" tzlocal-5.2/tests/test_data/noconflict/etc/timezone000066400000000000000000000000731451525731100226340ustar00rootroot00000000000000Etc/UTC# We allow comments. It's unusual, but has happened tzlocal-5.2/tests/test_data/noconflict/usr/000077500000000000000000000000001451525731100211155ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/noconflict/usr/share/000077500000000000000000000000001451525731100222175ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/noconflict/usr/share/zoneinfo/000077500000000000000000000000001451525731100240465ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/noconflict/usr/share/zoneinfo/Etc/000077500000000000000000000000001451525731100245615ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/noconflict/usr/share/zoneinfo/Etc/UCT000077700000000000000000000000001451525731100255102UTCustar00rootroot00000000000000tzlocal-5.2/tests/test_data/noconflict/usr/share/zoneinfo/Etc/UTC000066400000000000000000000001661451525731100251420ustar00rootroot00000000000000TZif2UTCTZif2UTC UTC0 tzlocal-5.2/tests/test_data/noconflict/usr/share/zoneinfo/UTC000077700000000000000000000000001451525731100255102Etc/UCTustar00rootroot00000000000000tzlocal-5.2/tests/test_data/noconflict/usr/share/zoneinfo/Zulu000077700000000000000000000000001451525731100253012UTCustar00rootroot00000000000000tzlocal-5.2/tests/test_data/symlink_localtime/000077500000000000000000000000001451525731100216655ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/symlink_localtime/etc/000077500000000000000000000000001451525731100224405ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/symlink_localtime/etc/localtime000077700000000000000000000000001451525731100326162../usr/share/zoneinfo/Africa/Harareustar00rootroot00000000000000tzlocal-5.2/tests/test_data/symlink_localtime/usr/000077500000000000000000000000001451525731100224765ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/symlink_localtime/usr/share/000077500000000000000000000000001451525731100236005ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/symlink_localtime/usr/share/zoneinfo/000077500000000000000000000000001451525731100254275ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/symlink_localtime/usr/share/zoneinfo/Africa/000077500000000000000000000000001451525731100266145ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/symlink_localtime/usr/share/zoneinfo/Africa/Harare000066400000000000000000000002351451525731100277410ustar00rootroot00000000000000TZif2‚FÇd LMTCATTZif2ÿÿÿÿ‚FÇd LMTCAT CAT-2 tzlocal-5.2/tests/test_data/termux/000077500000000000000000000000001451525731100174725ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/termux/system/000077500000000000000000000000001451525731100210165ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/termux/system/bin/000077500000000000000000000000001451525731100215665ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/termux/system/bin/getprop000066400000000000000000000001041451525731100231640ustar00rootroot00000000000000This is just a file to make tzlocal think it's termux under Android tzlocal-5.2/tests/test_data/timezone/000077500000000000000000000000001451525731100200005ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/timezone/etc/000077500000000000000000000000001451525731100205535ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/timezone/etc/timezone000066400000000000000000000001011451525731100223200ustar00rootroot00000000000000Africa/Harare# We allow comments. It's unusual, but has happened tzlocal-5.2/tests/test_data/timezone_setting/000077500000000000000000000000001451525731100215355ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/timezone_setting/etc/000077500000000000000000000000001451525731100223105ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/timezone_setting/etc/conf.d/000077500000000000000000000000001451525731100234575ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/timezone_setting/etc/conf.d/clock000066400000000000000000000000331451525731100244710ustar00rootroot00000000000000TIMEZONE = "Africa/Harare" tzlocal-5.2/tests/test_data/top_line_comment/000077500000000000000000000000001451525731100215015ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/top_line_comment/etc/000077500000000000000000000000001451525731100222545ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/top_line_comment/etc/timezone000066400000000000000000000000461451525731100240310ustar00rootroot00000000000000# Comment on first line Africa/Harare tzlocal-5.2/tests/test_data/ubuntu_docker_bug/000077500000000000000000000000001451525731100216545ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/ubuntu_docker_bug/etc/000077500000000000000000000000001451525731100224275ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/ubuntu_docker_bug/etc/timezone000066400000000000000000000000051451525731100241770ustar00rootroot00000000000000/UTC tzlocal-5.2/tests/test_data/vardbzoneinfo/000077500000000000000000000000001451525731100210145ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/vardbzoneinfo/etc/000077500000000000000000000000001451525731100215675ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/vardbzoneinfo/etc/timezone000066400000000000000000000000001451525731100233320ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/vardbzoneinfo/var/000077500000000000000000000000001451525731100216045ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/vardbzoneinfo/var/db/000077500000000000000000000000001451525731100221715ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/vardbzoneinfo/var/db/zoneinfo000066400000000000000000000001201451525731100237340ustar00rootroot00000000000000Africa/Harare localhost # The host is not a part of the format, but is allowed. tzlocal-5.2/tests/test_data/zone_setting/000077500000000000000000000000001451525731100206565ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/zone_setting/etc/000077500000000000000000000000001451525731100214315ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/zone_setting/etc/sysconfig/000077500000000000000000000000001451525731100234355ustar00rootroot00000000000000tzlocal-5.2/tests/test_data/zone_setting/etc/sysconfig/clock000066400000000000000000000000251451525731100244500ustar00rootroot00000000000000ZONE="Africa/Harare" tzlocal-5.2/tests/test_tzlocal.py000066400000000000000000000243411451525731100172630ustar00rootroot00000000000000import os import platform import sys from datetime import datetime, timezone from pathlib import Path from unittest.mock import MagicMock, Mock import pytest import tzlocal.unix import tzlocal.utils if sys.version_info >= (3, 9): from zoneinfo import ZoneInfo, ZoneInfoNotFoundError else: from backports.zoneinfo import ZoneInfo, ZoneInfoNotFoundError import logging logging.basicConfig(level=logging.DEBUG) @pytest.fixture(scope="session", autouse=True) def clear_tz_env_variable(): os.environ.pop("TZ", None) def tz_path(zonefile: str = None) -> str: path = Path(__file__).parent.joinpath("test_data") if zonefile: return str(path / zonefile) else: return str(path) def test_env(monkeypatch): tz_harare = tzlocal.utils._tz_from_env(":Africa/Harare") assert str(tz_harare) == "Africa/Harare" # Some Unices allow this as well, so we must allow it: tz_harare = tzlocal.utils._tz_from_env("Africa/Harare") assert str(tz_harare) == "Africa/Harare" path = tz_path(os.path.join("Africa", "Harare")) tz_local = tzlocal.utils._tz_from_env(":" + path) assert str(tz_local) == "Africa/Harare" # Make sure the local timezone is the same as the Harare one above. # We test this with a past date, so that we don't run into future changes # of the Harare timezone. dt = datetime(2012, 1, 1, 5) assert dt.replace(tzinfo=tz_harare) == dt.replace(tzinfo=tz_local) tz_local = tzlocal.utils._tz_from_env(tz_path("UTC")) assert str(tz_local) == "UTC" path = tz_path(os.path.join("localtime", "etc", "localtime")) tz_local = tzlocal.utils._tz_from_env(path) assert str(tz_local) == "localtime" # Non-zoneinfo timezones are not supported in the TZ environment. pytest.raises(ZoneInfoNotFoundError, tzlocal.utils._tz_from_env, "GMT+03:00") # With a zone that doesn't exist, raises error pytest.raises(ZoneInfoNotFoundError, tzlocal.utils._tz_from_env, "Just Nonsense") def test_timezone(): # Most versions of Ubuntu tz = tzlocal.unix._get_localzone(_root=tz_path("timezone")) assert str(tz) == "Africa/Harare" def test_timezone_top_line_comment(): tz = tzlocal.unix._get_localzone(_root=tz_path("top_line_comment")) assert str(tz) == "Africa/Harare" def test_zone_setting(): # A ZONE setting in /etc/sysconfig/clock, f ex CentOS tz = tzlocal.unix._get_localzone(_root=tz_path("zone_setting")) assert str(tz) == "Africa/Harare" def test_timezone_setting(): # A ZONE setting in /etc/conf.d/clock, f ex Gentoo tz = tzlocal.unix._get_localzone(_root=tz_path("timezone_setting")) assert str(tz) == "Africa/Harare" @pytest.mark.skipif( platform.system() == "Windows", reason="Symbolic links are not available on Windows" ) def test_symlink_localtime(): # A ZONE setting in the target path of a symbolic linked localtime, f ex systemd distributions tz = tzlocal.unix._get_localzone(_root=tz_path("symlink_localtime")) assert str(tz) == "Africa/Harare" def test_vardbzoneinfo_setting(): # A ZONE setting in /etc/conf.d/clock, f ex Gentoo tz = tzlocal.unix._get_localzone(_root=tz_path("vardbzoneinfo")) assert str(tz) == "Africa/Harare" def test_only_localtime(): tz = tzlocal.unix._get_localzone(_root=tz_path("localtime")) assert str(tz) == "local" dt = datetime(2012, 1, 1, 5) assert dt.replace(tzinfo=ZoneInfo("Africa/Harare")) == dt.replace(tzinfo=tz) def test_get_reload(mocker, monkeypatch): mocker.patch("tzlocal.utils.assert_tz_offset") # Clear any cached zone monkeypatch.setattr(tzlocal.unix, "_cache_tz", None) monkeypatch.setenv("TZ", "Africa/Harare") tz_harare = tzlocal.unix.get_localzone() assert str(tz_harare) == "Africa/Harare" # Changing the TZ makes no difference, because it's cached monkeypatch.setenv("TZ", "Africa/Johannesburg") tz_harare = tzlocal.unix.get_localzone() assert str(tz_harare) == "Africa/Harare" # So we reload it tz_harare = tzlocal.unix.reload_localzone() assert str(tz_harare) == "Africa/Johannesburg" def test_fail(recwarn): with pytest.warns(UserWarning, match="Can not find any timezone configuration"): tz = tzlocal.unix._get_localzone(_root=tz_path()) assert tz == timezone.utc def test_assert_tz_offset(): # The local zone should be the local zone: local = tzlocal.get_localzone() tzlocal.utils.assert_tz_offset(local) # Get a non local zone. Let's use Chatham, population 600. other = ZoneInfo("Pacific/Chatham") pytest.raises(ValueError, tzlocal.utils.assert_tz_offset, other) # But you can change it do it only warns other = ZoneInfo("Pacific/Chatham") with pytest.warns(UserWarning): tzlocal.utils.assert_tz_offset(other, error=False) def test_win32(mocker): if sys.platform == "win32": # Ironically, these tests don't work on Windows. import tzlocal.win32 # Just check on Windows that the code works, and that we get # something reasonable back. tz = tzlocal.win32.get_localzone() # It should be a timezone with a slash in it, at least: assert "/" in str(tz) return # Yes, winreg is all mocked out, but this test means we at least # catch syntax errors, etc. mocker.patch("tzlocal.utils.assert_tz_offset") winreg = MagicMock() winreg.EnumValue.configure_mock( return_value=("TimeZoneKeyName", "Belarus Standard Time") ) sys.modules["winreg"] = winreg import tzlocal.win32 tz = tzlocal.win32.get_localzone() assert str(tz) == "Europe/Minsk" tz = tzlocal.win32.reload_localzone() assert str(tz) == "Europe/Minsk" winreg.EnumValue.configure_mock( return_value=("TimeZoneKeyName", "Not a real timezone") ) pytest.raises(ZoneInfoNotFoundError, tzlocal.win32._get_localzone_name) # Old XP style reginfo should fail winreg.EnumValue.configure_mock( return_value=("TimeZoneKeyName", "Belarus Standard Time") ) tzlocal.win32.valuestodict = Mock( return_value={ "StandardName": "Mocked Standard Time", "Std": "Mocked Standard Time", } ) pytest.raises(LookupError, tzlocal.win32._get_localzone_name) def test_win32_env(mocker, monkeypatch): sys.modules["winreg"] = MagicMock() import tzlocal.win32 mocker.patch("tzlocal.utils.assert_tz_offset") monkeypatch.setattr(tzlocal.win32, "_cache_tz", None) monkeypatch.setenv("TZ", "Europe/Berlin") tzlocal.win32._cache_tz_name = None tzname = tzlocal.win32.get_localzone_name() assert tzname == "Europe/Berlin" tz = tzlocal.win32.get_localzone() assert str(tz) == "Europe/Berlin" def test_win32_no_dst(mocker): mocker.patch("tzlocal.utils.assert_tz_offset") valuesmock = mocker.patch("tzlocal.win32.valuestodict") # If you turn off the DST, tzlocal returns "Etc/GMT+zomething": valuesmock.configure_mock( return_value={ "TimeZoneKeyName": "Romance Standard Time", "DynamicDaylightTimeDisabled": 1, } ) tzlocal.win32._cache_tz_name = None tzlocal.win32._cache_tz = None assert str(tzlocal.win32.get_localzone()) == "Etc/GMT-1" # Except if the timezone doesn't have daylight savings at all, # then just return the timezone in question, because why not? valuesmock.configure_mock( return_value={ "TimeZoneKeyName": "Belarus Standard Time", "DynamicDaylightTimeDisabled": 1, } ) tz = tzlocal.win32._get_localzone_name() assert tz == "Europe/Minsk" # Now, if you disable this in a timezone with DST, that has a # non-whole hour offset, then there's nothing we can return. valuesmock.configure_mock( return_value={ "TimeZoneKeyName": "Cen. Australia Standard Time", "DynamicDaylightTimeDisabled": 1, } ) pytest.raises(ZoneInfoNotFoundError, tzlocal.win32._get_localzone_name) # But again, if there is no DST, that works fine: valuesmock.configure_mock( return_value={ "TimeZoneKeyName": "Aus Central W. Standard Time", "DynamicDaylightTimeDisabled": 1, } ) tz = tzlocal.win32._get_localzone_name() assert tz == "Australia/Eucla" def test_termux(mocker): subprocess = MagicMock() subprocess.check_output.configure_mock(return_value=b"Africa/Johannesburg") sys.modules["subprocess"] = subprocess tz = tzlocal.unix._get_localzone(_root=tz_path("termux")) assert str(tz) == "Africa/Johannesburg" @pytest.mark.skipif( platform.system() == "Windows", reason="Symbolic links are not available on Windows" ) def test_conflicting(): with pytest.raises(ZoneInfoNotFoundError) as excinfo: tzlocal.unix._get_localzone(_root=tz_path("conflicting")) message = excinfo.value.args[0] assert "Multiple conflicting time zone configurations found:\n" in message assert "Europe/Paris" in message assert "America/New_York" in message assert "Europe/Warsaw" in message assert "Africa/Johannesburg" in message 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) == "Etc/UTC" def test_zoneinfo_compatibility(): os.environ["TZ"] = "Africa/Harare" tzlocal.unix.reload_localzone() tz_harare = tzlocal.unix.get_localzone() assert str(tz_harare) == "Africa/Harare" os.environ["TZ"] = "America/New_York" tzlocal.unix.reload_localzone() tz_newyork = tzlocal.unix.get_localzone() assert str(tz_newyork) == "America/New_York" dt = datetime(2021, 10, 1, 12, 00) dt = dt.replace(tzinfo=tz_harare) assert dt.utcoffset().total_seconds() == 7200 dt = dt.replace(tzinfo=tz_newyork) assert dt.utcoffset().total_seconds() == -14400 del os.environ["TZ"] def test_get_localzone_name(): tzlocal.unix._cache_tz_name = None os.environ["TZ"] = "America/New_York" assert tzlocal.unix.get_localzone_name() == "America/New_York" del os.environ["TZ"] def test_ubuntu_docker_bug(): tz = tzlocal.unix._get_localzone(_root=tz_path("ubuntu_docker_bug")) assert str(tz) == "UTC" tzlocal-5.2/tzlocal/000077500000000000000000000000001451525731100145045ustar00rootroot00000000000000tzlocal-5.2/tzlocal/__init__.py000066400000000000000000000006141451525731100166160ustar00rootroot00000000000000import sys if sys.platform == "win32": from tzlocal.win32 import ( get_localzone, get_localzone_name, reload_localzone, ) else: from tzlocal.unix import get_localzone, get_localzone_name, reload_localzone from tzlocal.utils import assert_tz_offset __all__ = [ "get_localzone", "get_localzone_name", "reload_localzone", "assert_tz_offset", ] tzlocal-5.2/tzlocal/py.typed000066400000000000000000000000001451525731100161710ustar00rootroot00000000000000tzlocal-5.2/tzlocal/unix.py000066400000000000000000000177501451525731100160530ustar00rootroot00000000000000import logging import os import re import sys import warnings from datetime import timezone from tzlocal import utils if sys.version_info >= (3, 9): import zoneinfo # pragma: no cover else: from backports import zoneinfo # pragma: no cover _cache_tz = None _cache_tz_name = None log = logging.getLogger("tzlocal") def _get_localzone_name(_root="/"): """Tries to find the local timezone configuration. This method finds the timezone name, if it can, or it returns None. The parameter _root makes the function look for files like /etc/localtime beneath the _root directory. This is primarily used by the tests. In normal usage you call the function without parameters.""" # First try the ENV setting. tzenv = utils._tz_name_from_env() if tzenv: return tzenv # Are we under Termux on Android? if os.path.exists(os.path.join(_root, "system/bin/getprop")): log.debug("This looks like Termux") import subprocess try: androidtz = ( subprocess.check_output(["getprop", "persist.sys.timezone"]) .strip() .decode() ) return androidtz except (OSError, subprocess.CalledProcessError): # proot environment or failed to getprop log.debug("It's not termux?") pass # Now look for distribution specific configuration files # that contain the timezone name. # Stick all of them in a dict, to compare later. found_configs = {} for configfile in ("etc/timezone", "var/db/zoneinfo"): tzpath = os.path.join(_root, configfile) try: with open(tzpath) as tzfile: data = tzfile.read() log.debug(f"{tzpath} found, contents:\n {data}") etctz = data.strip("/ \t\r\n") if not etctz: # Empty file, skip continue for etctz in etctz.splitlines(): # Get rid of host definitions and comments: if " " in etctz: etctz, dummy = etctz.split(" ", 1) if "#" in etctz: etctz, dummy = etctz.split("#", 1) if not etctz: continue found_configs[tzpath] = etctz.replace(" ", "_") except (OSError, UnicodeDecodeError): # File doesn't exist or is a directory, or it's a binary file. continue # CentOS has a ZONE setting in /etc/sysconfig/clock, # OpenSUSE has a TIMEZONE setting in /etc/sysconfig/clock and # Gentoo has a TIMEZONE setting in /etc/conf.d/clock # We look through these files for a timezone: zone_re = re.compile(r"\s*ZONE\s*=\s*\"") timezone_re = re.compile(r"\s*TIMEZONE\s*=\s*\"") end_re = re.compile('"') for filename in ("etc/sysconfig/clock", "etc/conf.d/clock"): tzpath = os.path.join(_root, filename) try: with open(tzpath, "rt") as tzfile: data = tzfile.readlines() log.debug(f"{tzpath} found, contents:\n {data}") for line in data: # Look for the ZONE= setting. match = zone_re.match(line) if match is None: # No ZONE= setting. Look for the TIMEZONE= setting. match = timezone_re.match(line) if match is not None: # Some setting existed line = line[match.end() :] etctz = line[: end_re.search(line).start()] # We found a timezone found_configs[tzpath] = etctz.replace(" ", "_") except (OSError, UnicodeDecodeError): # UnicodeDecode handles when clock is symlink to /etc/localtime continue # systemd distributions use symlinks that include the zone name, # see manpage of localtime(5) and timedatectl(1) tzpath = os.path.join(_root, "etc/localtime") if os.path.exists(tzpath) and os.path.islink(tzpath): log.debug(f"{tzpath} found") etctz = os.path.realpath(tzpath) start = etctz.find("/") + 1 while start != 0: etctz = etctz[start:] try: zoneinfo.ZoneInfo(etctz) tzinfo = f"{tzpath} is a symlink to" found_configs[tzinfo] = etctz.replace(" ", "_") # Only need first valid relative path in simlink. break except zoneinfo.ZoneInfoNotFoundError: pass start = etctz.find("/") + 1 if len(found_configs) > 0: log.debug(f"{len(found_configs)} found:\n {found_configs}") # We found some explicit config of some sort! if len(found_configs) > 1: # Uh-oh, multiple configs. See if they match: unique_tzs = set() zoneinfopath = os.path.join(_root, "usr", "share", "zoneinfo") directory_depth = len(zoneinfopath.split(os.path.sep)) for tzname in found_configs.values(): # Look them up in /usr/share/zoneinfo, and find what they # really point to: path = os.path.realpath(os.path.join(zoneinfopath, *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" for key, value in found_configs.items(): message += f"{key}: {value}\n" message += "Fix the configuration, or set the time zone in a TZ environment variable.\n" raise zoneinfo.ZoneInfoNotFoundError(message) # We found exactly one config! Use it. return list(found_configs.values())[0] def _get_localzone(_root="/"): """Creates a timezone object from the timezone name. If there is no timezone config, it will try to create a file from the localtime timezone, and if there isn't one, it will default to UTC. The parameter _root makes the function look for files like /etc/localtime beneath the _root directory. This is primarily used by the tests. In normal usage you call the function without parameters.""" # First try the ENV setting. tzenv = utils._tz_from_env() if tzenv: return tzenv tzname = _get_localzone_name(_root) if tzname is None: # No explicit setting existed. Use localtime log.debug("No explicit setting existed. Use localtime") for filename in ("etc/localtime", "usr/local/etc/localtime"): tzpath = os.path.join(_root, filename) if not os.path.exists(tzpath): continue with open(tzpath, "rb") as tzfile: tz = zoneinfo.ZoneInfo.from_file(tzfile, key="local") break else: warnings.warn("Can not find any timezone configuration, defaulting to UTC.") tz = timezone.utc else: tz = zoneinfo.ZoneInfo(tzname) if _root == "/": # We are using a file in etc to name the timezone. # Verify that the timezone specified there is actually used: utils.assert_tz_offset(tz, error=False) return tz def get_localzone_name() -> str: """Get the computers configured local timezone name, if any.""" global _cache_tz_name if _cache_tz_name is None: _cache_tz_name = _get_localzone_name() return _cache_tz_name def get_localzone() -> zoneinfo.ZoneInfo: """Get the computers configured local timezone, if any.""" global _cache_tz if _cache_tz is None: _cache_tz = _get_localzone() return _cache_tz def reload_localzone() -> zoneinfo.ZoneInfo: """Reload the cached localzone. You need to call this if the timezone has changed.""" global _cache_tz_name global _cache_tz _cache_tz_name = _get_localzone_name() _cache_tz = _get_localzone() return _cache_tz tzlocal-5.2/tzlocal/utils.py000066400000000000000000000064011451525731100162170ustar00rootroot00000000000000import calendar import datetime import logging import os import time import warnings try: import zoneinfo # pragma: no cover except ImportError: from backports import zoneinfo # pragma: no cover from tzlocal import windows_tz log = logging.getLogger("tzlocal") def get_tz_offset(tz): """Get timezone's offset using built-in function datetime.utcoffset().""" return int(datetime.datetime.now(tz).utcoffset().total_seconds()) def assert_tz_offset(tz, error=True): """Assert that system's timezone offset equals to the timezone offset found. If they don't match, we probably have a misconfiguration, for example, an incorrect timezone set in /etc/timezone file in systemd distributions. If error is True, this method will raise a ValueError, otherwise it will emit a warning. """ tz_offset = get_tz_offset(tz) system_offset = calendar.timegm(time.localtime()) - calendar.timegm(time.gmtime()) # No one has timezone offsets less than a minute, so this should be close enough: if abs(tz_offset - system_offset) > 60: msg = ( f"Timezone offset does not match system offset: {tz_offset} != {system_offset}. " "Please, check your config files." ) if error: raise ValueError(msg) warnings.warn(msg) def _tz_name_from_env(tzenv=None): if tzenv is None: tzenv = os.environ.get("TZ") if not tzenv: return None log.debug(f"Found a TZ environment: {tzenv}") if tzenv[0] == ":": tzenv = tzenv[1:] if tzenv in windows_tz.tz_win: # Yup, it's a timezone return tzenv if os.path.isabs(tzenv) and os.path.exists(tzenv): # It's a file specification, expand it, if possible parts = os.path.realpath(tzenv).split(os.sep) # Is it a zone info zone? possible_tz = "/".join(parts[-2:]) if possible_tz in windows_tz.tz_win: # Yup, it is return possible_tz # Maybe it's a short one, like UTC? if parts[-1] in windows_tz.tz_win: # Indeed return parts[-1] log.debug("TZ does not contain a time zone name") return None def _tz_from_env(tzenv=None): if tzenv is None: tzenv = os.environ.get("TZ") if not tzenv: return None # Some weird format that exists: if tzenv[0] == ":": tzenv = tzenv[1:] # TZ specifies a file if os.path.isabs(tzenv) and os.path.exists(tzenv): # Try to see if we can figure out the name tzname = _tz_name_from_env(tzenv) if not tzname: # Nope, not a standard timezone name, just take the filename tzname = tzenv.split(os.sep)[-1] with open(tzenv, "rb") as tzfile: return zoneinfo.ZoneInfo.from_file(tzfile, key=tzname) # TZ must specify a zoneinfo zone. try: tz = zoneinfo.ZoneInfo(tzenv) # That worked, so we return this: return tz except zoneinfo.ZoneInfoNotFoundError: # Nope, it's something like "PST4DST" etc, we can't handle that. raise zoneinfo.ZoneInfoNotFoundError( f"tzlocal() does not support non-zoneinfo timezones like {tzenv}. \n" "Please use a timezone in the form of Continent/City" ) from None tzlocal-5.2/tzlocal/win32.py000066400000000000000000000112441451525731100160220ustar00rootroot00000000000000import logging from datetime import datetime try: import _winreg as winreg except ImportError: import winreg try: import zoneinfo # pragma: no cover except ImportError: from backports import zoneinfo # pragma: no cover from tzlocal import utils from tzlocal.windows_tz import win_tz _cache_tz = None _cache_tz_name = None log = logging.getLogger("tzlocal") def valuestodict(key): """Convert a registry key's values to a dictionary.""" result = {} size = winreg.QueryInfoKey(key)[1] for i in range(size): data = winreg.EnumValue(key, i) result[data[0]] = data[1] return result def _get_dst_info(tz): # Find the offset for when it doesn't have DST: dst_offset = std_offset = None has_dst = False year = datetime.now().year for dt in (datetime(year, 1, 1), datetime(year, 6, 1)): if tz.dst(dt).total_seconds() == 0.0: # OK, no DST during winter, get this offset std_offset = tz.utcoffset(dt).total_seconds() else: has_dst = True return has_dst, std_offset, dst_offset def _get_localzone_name(): # Windows is special. It has unique time zone names (in several # meanings of the word) available, but unfortunately, they can be # translated to the language of the operating system, so we need to # do a backwards lookup, by going through all time zones and see which # one matches. tzenv = utils._tz_name_from_env() if tzenv: return tzenv log.debug("Looking up time zone info from registry") handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" localtz = winreg.OpenKey(handle, TZLOCALKEYNAME) keyvalues = valuestodict(localtz) localtz.Close() if "TimeZoneKeyName" in keyvalues: # Windows 7 and later # For some reason this returns a string with loads of NUL bytes at # least on some systems. I don't know if this is a bug somewhere, I # just work around it. tzkeyname = keyvalues["TimeZoneKeyName"].split("\x00", 1)[0] else: # Don't support XP any longer raise LookupError("Can not find Windows timezone configuration") timezone = win_tz.get(tzkeyname) if timezone is None: # Nope, that didn't work. Try adding "Standard Time", # it seems to work a lot of times: timezone = win_tz.get(tzkeyname + " Standard Time") # Return what we have. if timezone is None: raise zoneinfo.ZoneInfoNotFoundError(tzkeyname) if keyvalues.get("DynamicDaylightTimeDisabled", 0) == 1: # DST is disabled, so don't return the timezone name, # instead return Etc/GMT+offset tz = zoneinfo.ZoneInfo(timezone) has_dst, std_offset, dst_offset = _get_dst_info(tz) if not has_dst: # The DST is turned off in the windows configuration, # but this timezone doesn't have DST so it doesn't matter return timezone if std_offset is None: raise zoneinfo.ZoneInfoNotFoundError( f"{tzkeyname} claims to not have a non-DST time!?" ) if std_offset % 3600: # I can't convert this to an hourly offset raise zoneinfo.ZoneInfoNotFoundError( f"tzlocal can't support disabling DST in the {timezone} zone." ) # This has whole hours as offset, return it as Etc/GMT return f"Etc/GMT{-std_offset//3600:+.0f}" return timezone def get_localzone_name() -> str: """Get the zoneinfo timezone name that matches the Windows-configured timezone.""" global _cache_tz_name if _cache_tz_name is None: _cache_tz_name = _get_localzone_name() return _cache_tz_name def get_localzone() -> zoneinfo.ZoneInfo: """Returns the zoneinfo-based tzinfo object that matches the Windows-configured timezone.""" global _cache_tz if _cache_tz is None: _cache_tz = zoneinfo.ZoneInfo(get_localzone_name()) if not utils._tz_name_from_env(): # If the timezone does NOT come from a TZ environment variable, # verify that it's correct. If it's from the environment, # we accept it, this is so you can run tests with different timezones. utils.assert_tz_offset(_cache_tz, error=False) return _cache_tz def reload_localzone() -> zoneinfo.ZoneInfo: """Reload the cached localzone. You need to call this if the timezone has changed.""" global _cache_tz global _cache_tz_name _cache_tz_name = _get_localzone_name() _cache_tz = zoneinfo.ZoneInfo(_cache_tz_name) utils.assert_tz_offset(_cache_tz, error=False) return _cache_tz tzlocal-5.2/tzlocal/windows_tz.py000066400000000000000000001045351451525731100172750ustar00rootroot00000000000000# This file is autogenerated by the update_windows_mapping.py script # Do not edit. win_tz = { "AUS Central Standard Time": "Australia/Darwin", "AUS Eastern Standard Time": "Australia/Sydney", "Afghanistan Standard Time": "Asia/Kabul", "Alaskan Standard Time": "America/Anchorage", "Aleutian Standard Time": "America/Adak", "Altai Standard Time": "Asia/Barnaul", "Arab Standard Time": "Asia/Riyadh", "Arabian Standard Time": "Asia/Dubai", "Arabic Standard Time": "Asia/Baghdad", "Argentina Standard Time": "America/Buenos_Aires", "Astrakhan Standard Time": "Europe/Astrakhan", "Atlantic Standard Time": "America/Halifax", "Aus Central W. Standard Time": "Australia/Eucla", "Azerbaijan Standard Time": "Asia/Baku", "Azores Standard Time": "Atlantic/Azores", "Bahia Standard Time": "America/Bahia", "Bangladesh Standard Time": "Asia/Dhaka", "Belarus Standard Time": "Europe/Minsk", "Bougainville Standard Time": "Pacific/Bougainville", "Canada Central Standard Time": "America/Regina", "Cape Verde Standard Time": "Atlantic/Cape_Verde", "Caucasus Standard Time": "Asia/Yerevan", "Cen. Australia Standard Time": "Australia/Adelaide", "Central America Standard Time": "America/Guatemala", "Central Asia Standard Time": "Asia/Almaty", "Central Brazilian Standard Time": "America/Cuiaba", "Central Europe Standard Time": "Europe/Budapest", "Central European Standard Time": "Europe/Warsaw", "Central Pacific Standard Time": "Pacific/Guadalcanal", "Central Standard Time": "America/Chicago", "Central Standard Time (Mexico)": "America/Mexico_City", "Chatham Islands Standard Time": "Pacific/Chatham", "China Standard Time": "Asia/Shanghai", "Cuba Standard Time": "America/Havana", "Dateline Standard Time": "Etc/GMT+12", "E. Africa Standard Time": "Africa/Nairobi", "E. Australia Standard Time": "Australia/Brisbane", "E. Europe Standard Time": "Europe/Chisinau", "E. South America Standard Time": "America/Sao_Paulo", "Easter Island Standard Time": "Pacific/Easter", "Eastern Standard Time": "America/New_York", "Eastern Standard Time (Mexico)": "America/Cancun", "Egypt Standard Time": "Africa/Cairo", "Ekaterinburg Standard Time": "Asia/Yekaterinburg", "FLE Standard Time": "Europe/Kiev", "Fiji Standard Time": "Pacific/Fiji", "GMT Standard Time": "Europe/London", "GTB Standard Time": "Europe/Bucharest", "Georgian Standard Time": "Asia/Tbilisi", "Greenland Standard Time": "America/Godthab", "Greenwich Standard Time": "Atlantic/Reykjavik", "Haiti Standard Time": "America/Port-au-Prince", "Hawaiian Standard Time": "Pacific/Honolulu", "India Standard Time": "Asia/Calcutta", "Iran Standard Time": "Asia/Tehran", "Israel Standard Time": "Asia/Jerusalem", "Jordan Standard Time": "Asia/Amman", "Kaliningrad Standard Time": "Europe/Kaliningrad", "Korea Standard Time": "Asia/Seoul", "Libya Standard Time": "Africa/Tripoli", "Line Islands Standard Time": "Pacific/Kiritimati", "Lord Howe Standard Time": "Australia/Lord_Howe", "Magadan Standard Time": "Asia/Magadan", "Magallanes Standard Time": "America/Punta_Arenas", "Marquesas Standard Time": "Pacific/Marquesas", "Mauritius Standard Time": "Indian/Mauritius", "Middle East Standard Time": "Asia/Beirut", "Montevideo Standard Time": "America/Montevideo", "Morocco Standard Time": "Africa/Casablanca", "Mountain Standard Time": "America/Denver", "Mountain Standard Time (Mexico)": "America/Mazatlan", "Myanmar Standard Time": "Asia/Rangoon", "N. Central Asia Standard Time": "Asia/Novosibirsk", "Namibia Standard Time": "Africa/Windhoek", "Nepal Standard Time": "Asia/Katmandu", "New Zealand Standard Time": "Pacific/Auckland", "Newfoundland Standard Time": "America/St_Johns", "Norfolk Standard Time": "Pacific/Norfolk", "North Asia East Standard Time": "Asia/Irkutsk", "North Asia Standard Time": "Asia/Krasnoyarsk", "North Korea Standard Time": "Asia/Pyongyang", "Omsk Standard Time": "Asia/Omsk", "Pacific SA Standard Time": "America/Santiago", "Pacific Standard Time": "America/Los_Angeles", "Pacific Standard Time (Mexico)": "America/Tijuana", "Pakistan Standard Time": "Asia/Karachi", "Paraguay Standard Time": "America/Asuncion", "Qyzylorda Standard Time": "Asia/Qyzylorda", "Romance Standard Time": "Europe/Paris", "Russia Time Zone 10": "Asia/Srednekolymsk", "Russia Time Zone 11": "Asia/Kamchatka", "Russia Time Zone 3": "Europe/Samara", "Russian Standard Time": "Europe/Moscow", "SA Eastern Standard Time": "America/Cayenne", "SA Pacific Standard Time": "America/Bogota", "SA Western Standard Time": "America/La_Paz", "SE Asia Standard Time": "Asia/Bangkok", "Saint Pierre Standard Time": "America/Miquelon", "Sakhalin Standard Time": "Asia/Sakhalin", "Samoa Standard Time": "Pacific/Apia", "Sao Tome Standard Time": "Africa/Sao_Tome", "Saratov Standard Time": "Europe/Saratov", "Singapore Standard Time": "Asia/Singapore", "South Africa Standard Time": "Africa/Johannesburg", "South Sudan Standard Time": "Africa/Juba", "Sri Lanka Standard Time": "Asia/Colombo", "Sudan Standard Time": "Africa/Khartoum", "Syria Standard Time": "Asia/Damascus", "Taipei Standard Time": "Asia/Taipei", "Tasmania Standard Time": "Australia/Hobart", "Tocantins Standard Time": "America/Araguaina", "Tokyo Standard Time": "Asia/Tokyo", "Tomsk Standard Time": "Asia/Tomsk", "Tonga Standard Time": "Pacific/Tongatapu", "Transbaikal Standard Time": "Asia/Chita", "Turkey Standard Time": "Europe/Istanbul", "Turks And Caicos Standard Time": "America/Grand_Turk", "US Eastern Standard Time": "America/Indianapolis", "US Mountain Standard Time": "America/Phoenix", "UTC": "Etc/UTC", "UTC+12": "Etc/GMT-12", "UTC+13": "Etc/GMT-13", "UTC-02": "Etc/GMT+2", "UTC-08": "Etc/GMT+8", "UTC-09": "Etc/GMT+9", "UTC-11": "Etc/GMT+11", "Ulaanbaatar Standard Time": "Asia/Ulaanbaatar", "Venezuela Standard Time": "America/Caracas", "Vladivostok Standard Time": "Asia/Vladivostok", "Volgograd Standard Time": "Europe/Volgograd", "W. Australia Standard Time": "Australia/Perth", "W. Central Africa Standard Time": "Africa/Lagos", "W. Europe Standard Time": "Europe/Berlin", "W. Mongolia Standard Time": "Asia/Hovd", "West Asia Standard Time": "Asia/Tashkent", "West Bank Standard Time": "Asia/Hebron", "West Pacific Standard Time": "Pacific/Port_Moresby", "Yakutsk Standard Time": "Asia/Yakutsk", "Yukon Standard Time": "America/Whitehorse", } # Old name for the win_tz variable: tz_names = win_tz tz_win = { "": "Central Standard Time (Mexico)", "Africa/Abidjan": "Greenwich Standard Time", "Africa/Accra": "Greenwich Standard Time", "Africa/Addis_Ababa": "E. Africa Standard Time", "Africa/Algiers": "W. Central Africa Standard Time", "Africa/Asmara": "E. Africa Standard Time", "Africa/Asmera": "E. Africa Standard Time", "Africa/Bamako": "Greenwich Standard Time", "Africa/Bangui": "W. Central Africa Standard Time", "Africa/Banjul": "Greenwich Standard Time", "Africa/Bissau": "Greenwich Standard Time", "Africa/Blantyre": "South Africa Standard Time", "Africa/Brazzaville": "W. Central Africa Standard Time", "Africa/Bujumbura": "South Africa Standard Time", "Africa/Cairo": "Egypt Standard Time", "Africa/Casablanca": "Morocco Standard Time", "Africa/Ceuta": "Romance Standard Time", "Africa/Conakry": "Greenwich Standard Time", "Africa/Dakar": "Greenwich Standard Time", "Africa/Dar_es_Salaam": "E. Africa Standard Time", "Africa/Djibouti": "E. Africa Standard Time", "Africa/Douala": "W. Central Africa Standard Time", "Africa/El_Aaiun": "Morocco Standard Time", "Africa/Freetown": "Greenwich Standard Time", "Africa/Gaborone": "South Africa Standard Time", "Africa/Harare": "South Africa Standard Time", "Africa/Johannesburg": "South Africa Standard Time", "Africa/Juba": "South Sudan Standard Time", "Africa/Kampala": "E. Africa Standard Time", "Africa/Khartoum": "Sudan Standard Time", "Africa/Kigali": "South Africa Standard Time", "Africa/Kinshasa": "W. Central Africa Standard Time", "Africa/Lagos": "W. Central Africa Standard Time", "Africa/Libreville": "W. Central Africa Standard Time", "Africa/Lome": "Greenwich Standard Time", "Africa/Luanda": "W. Central Africa Standard Time", "Africa/Lubumbashi": "South Africa Standard Time", "Africa/Lusaka": "South Africa Standard Time", "Africa/Malabo": "W. Central Africa Standard Time", "Africa/Maputo": "South Africa Standard Time", "Africa/Maseru": "South Africa Standard Time", "Africa/Mbabane": "South Africa Standard Time", "Africa/Mogadishu": "E. Africa Standard Time", "Africa/Monrovia": "Greenwich Standard Time", "Africa/Nairobi": "E. Africa Standard Time", "Africa/Ndjamena": "W. Central Africa Standard Time", "Africa/Niamey": "W. Central Africa Standard Time", "Africa/Nouakchott": "Greenwich Standard Time", "Africa/Ouagadougou": "Greenwich Standard Time", "Africa/Porto-Novo": "W. Central Africa Standard Time", "Africa/Sao_Tome": "Sao Tome Standard Time", "Africa/Timbuktu": "Greenwich Standard Time", "Africa/Tripoli": "Libya Standard Time", "Africa/Tunis": "W. Central Africa Standard Time", "Africa/Windhoek": "Namibia Standard Time", "America/Adak": "Aleutian Standard Time", "America/Anchorage": "Alaskan Standard Time", "America/Anguilla": "SA Western Standard Time", "America/Antigua": "SA Western Standard Time", "America/Araguaina": "Tocantins Standard Time", "America/Argentina/Buenos_Aires": "Argentina Standard Time", "America/Argentina/Catamarca": "Argentina Standard Time", "America/Argentina/ComodRivadavia": "Argentina Standard Time", "America/Argentina/Cordoba": "Argentina Standard Time", "America/Argentina/Jujuy": "Argentina Standard Time", "America/Argentina/La_Rioja": "Argentina Standard Time", "America/Argentina/Mendoza": "Argentina Standard Time", "America/Argentina/Rio_Gallegos": "Argentina Standard Time", "America/Argentina/Salta": "Argentina Standard Time", "America/Argentina/San_Juan": "Argentina Standard Time", "America/Argentina/San_Luis": "Argentina Standard Time", "America/Argentina/Tucuman": "Argentina Standard Time", "America/Argentina/Ushuaia": "Argentina Standard Time", "America/Aruba": "SA Western Standard Time", "America/Asuncion": "Paraguay Standard Time", "America/Atikokan": "SA Pacific Standard Time", "America/Atka": "Aleutian Standard Time", "America/Bahia": "Bahia Standard Time", "America/Bahia_Banderas": "Central Standard Time (Mexico)", "America/Barbados": "SA Western Standard Time", "America/Belem": "SA Eastern Standard Time", "America/Belize": "Central America Standard Time", "America/Blanc-Sablon": "SA Western Standard Time", "America/Boa_Vista": "SA Western Standard Time", "America/Bogota": "SA Pacific Standard Time", "America/Boise": "Mountain Standard Time", "America/Buenos_Aires": "Argentina Standard Time", "America/Cambridge_Bay": "Mountain Standard Time", "America/Campo_Grande": "Central Brazilian Standard Time", "America/Cancun": "Eastern Standard Time (Mexico)", "America/Caracas": "Venezuela Standard Time", "America/Catamarca": "Argentina Standard Time", "America/Cayenne": "SA Eastern Standard Time", "America/Cayman": "SA Pacific Standard Time", "America/Chicago": "Central Standard Time", "America/Chihuahua": "Central Standard Time (Mexico)", "America/Ciudad_Juarez": "Mountain Standard Time", "America/Coral_Harbour": "SA Pacific Standard Time", "America/Cordoba": "Argentina Standard Time", "America/Costa_Rica": "Central America Standard Time", "America/Creston": "US Mountain Standard Time", "America/Cuiaba": "Central Brazilian Standard Time", "America/Curacao": "SA Western Standard Time", "America/Danmarkshavn": "Greenwich Standard Time", "America/Dawson": "Yukon Standard Time", "America/Dawson_Creek": "US Mountain Standard Time", "America/Denver": "Mountain Standard Time", "America/Detroit": "Eastern Standard Time", "America/Dominica": "SA Western Standard Time", "America/Edmonton": "Mountain Standard Time", "America/Eirunepe": "SA Pacific Standard Time", "America/El_Salvador": "Central America Standard Time", "America/Ensenada": "Pacific Standard Time (Mexico)", "America/Fort_Nelson": "US Mountain Standard Time", "America/Fort_Wayne": "US Eastern Standard Time", "America/Fortaleza": "SA Eastern Standard Time", "America/Glace_Bay": "Atlantic Standard Time", "America/Godthab": "Greenland Standard Time", "America/Goose_Bay": "Atlantic Standard Time", "America/Grand_Turk": "Turks And Caicos Standard Time", "America/Grenada": "SA Western Standard Time", "America/Guadeloupe": "SA Western Standard Time", "America/Guatemala": "Central America Standard Time", "America/Guayaquil": "SA Pacific Standard Time", "America/Guyana": "SA Western Standard Time", "America/Halifax": "Atlantic Standard Time", "America/Havana": "Cuba Standard Time", "America/Hermosillo": "US Mountain Standard Time", "America/Indiana/Indianapolis": "US Eastern Standard Time", "America/Indiana/Knox": "Central Standard Time", "America/Indiana/Marengo": "US Eastern Standard Time", "America/Indiana/Petersburg": "Eastern Standard Time", "America/Indiana/Tell_City": "Central Standard Time", "America/Indiana/Vevay": "US Eastern Standard Time", "America/Indiana/Vincennes": "Eastern Standard Time", "America/Indiana/Winamac": "Eastern Standard Time", "America/Indianapolis": "US Eastern Standard Time", "America/Inuvik": "Mountain Standard Time", "America/Iqaluit": "Eastern Standard Time", "America/Jamaica": "SA Pacific Standard Time", "America/Jujuy": "Argentina Standard Time", "America/Juneau": "Alaskan Standard Time", "America/Kentucky/Louisville": "Eastern Standard Time", "America/Kentucky/Monticello": "Eastern Standard Time", "America/Knox_IN": "Central Standard Time", "America/Kralendijk": "SA Western Standard Time", "America/La_Paz": "SA Western Standard Time", "America/Lima": "SA Pacific Standard Time", "America/Los_Angeles": "Pacific Standard Time", "America/Louisville": "Eastern Standard Time", "America/Lower_Princes": "SA Western Standard Time", "America/Maceio": "SA Eastern Standard Time", "America/Managua": "Central America Standard Time", "America/Manaus": "SA Western Standard Time", "America/Marigot": "SA Western Standard Time", "America/Martinique": "SA Western Standard Time", "America/Matamoros": "Central Standard Time", "America/Mazatlan": "Mountain Standard Time (Mexico)", "America/Mendoza": "Argentina Standard Time", "America/Menominee": "Central Standard Time", "America/Merida": "Central Standard Time (Mexico)", "America/Metlakatla": "Alaskan Standard Time", "America/Mexico_City": "Central Standard Time (Mexico)", "America/Miquelon": "Saint Pierre Standard Time", "America/Moncton": "Atlantic Standard Time", "America/Monterrey": "Central Standard Time (Mexico)", "America/Montevideo": "Montevideo Standard Time", "America/Montreal": "Eastern Standard Time", "America/Montserrat": "SA Western Standard Time", "America/Nassau": "Eastern Standard Time", "America/New_York": "Eastern Standard Time", "America/Nipigon": "Eastern Standard Time", "America/Nome": "Alaskan Standard Time", "America/Noronha": "UTC-02", "America/North_Dakota/Beulah": "Central Standard Time", "America/North_Dakota/Center": "Central Standard Time", "America/North_Dakota/New_Salem": "Central Standard Time", "America/Nuuk": "Greenland Standard Time", "America/Ojinaga": "Central Standard Time", "America/Panama": "SA Pacific Standard Time", "America/Pangnirtung": "Eastern Standard Time", "America/Paramaribo": "SA Eastern Standard Time", "America/Phoenix": "US Mountain Standard Time", "America/Port-au-Prince": "Haiti Standard Time", "America/Port_of_Spain": "SA Western Standard Time", "America/Porto_Acre": "SA Pacific Standard Time", "America/Porto_Velho": "SA Western Standard Time", "America/Puerto_Rico": "SA Western Standard Time", "America/Punta_Arenas": "Magallanes Standard Time", "America/Rainy_River": "Central Standard Time", "America/Rankin_Inlet": "Central Standard Time", "America/Recife": "SA Eastern Standard Time", "America/Regina": "Canada Central Standard Time", "America/Resolute": "Central Standard Time", "America/Rio_Branco": "SA Pacific Standard Time", "America/Rosario": "Argentina Standard Time", "America/Santa_Isabel": "Pacific Standard Time (Mexico)", "America/Santarem": "SA Eastern Standard Time", "America/Santiago": "Pacific SA Standard Time", "America/Santo_Domingo": "SA Western Standard Time", "America/Sao_Paulo": "E. South America Standard Time", "America/Scoresbysund": "Azores Standard Time", "America/Shiprock": "Mountain Standard Time", "America/Sitka": "Alaskan Standard Time", "America/St_Barthelemy": "SA Western Standard Time", "America/St_Johns": "Newfoundland Standard Time", "America/St_Kitts": "SA Western Standard Time", "America/St_Lucia": "SA Western Standard Time", "America/St_Thomas": "SA Western Standard Time", "America/St_Vincent": "SA Western Standard Time", "America/Swift_Current": "Canada Central Standard Time", "America/Tegucigalpa": "Central America Standard Time", "America/Thule": "Atlantic Standard Time", "America/Thunder_Bay": "Eastern Standard Time", "America/Tijuana": "Pacific Standard Time (Mexico)", "America/Toronto": "Eastern Standard Time", "America/Tortola": "SA Western Standard Time", "America/Vancouver": "Pacific Standard Time", "America/Virgin": "SA Western Standard Time", "America/Whitehorse": "Yukon Standard Time", "America/Winnipeg": "Central Standard Time", "America/Yakutat": "Alaskan Standard Time", "America/Yellowknife": "Mountain Standard Time", "Antarctica/Casey": "Central Pacific Standard Time", "Antarctica/Davis": "SE Asia Standard Time", "Antarctica/DumontDUrville": "West Pacific Standard Time", "Antarctica/Macquarie": "Tasmania Standard Time", "Antarctica/Mawson": "West Asia Standard Time", "Antarctica/McMurdo": "New Zealand Standard Time", "Antarctica/Palmer": "SA Eastern Standard Time", "Antarctica/Rothera": "SA Eastern Standard Time", "Antarctica/South_Pole": "New Zealand Standard Time", "Antarctica/Syowa": "E. Africa Standard Time", "Antarctica/Vostok": "Central Asia Standard Time", "Arctic/Longyearbyen": "W. Europe Standard Time", "Asia/Aden": "Arab Standard Time", "Asia/Almaty": "Central Asia Standard Time", "Asia/Amman": "Jordan Standard Time", "Asia/Anadyr": "Russia Time Zone 11", "Asia/Aqtau": "West Asia Standard Time", "Asia/Aqtobe": "West Asia Standard Time", "Asia/Ashgabat": "West Asia Standard Time", "Asia/Ashkhabad": "West Asia Standard Time", "Asia/Atyrau": "West Asia Standard Time", "Asia/Baghdad": "Arabic Standard Time", "Asia/Bahrain": "Arab Standard Time", "Asia/Baku": "Azerbaijan Standard Time", "Asia/Bangkok": "SE Asia Standard Time", "Asia/Barnaul": "Altai Standard Time", "Asia/Beirut": "Middle East Standard Time", "Asia/Bishkek": "Central Asia Standard Time", "Asia/Brunei": "Singapore Standard Time", "Asia/Calcutta": "India Standard Time", "Asia/Chita": "Transbaikal Standard Time", "Asia/Choibalsan": "Ulaanbaatar Standard Time", "Asia/Chongqing": "China Standard Time", "Asia/Chungking": "China Standard Time", "Asia/Colombo": "Sri Lanka Standard Time", "Asia/Dacca": "Bangladesh Standard Time", "Asia/Damascus": "Syria Standard Time", "Asia/Dhaka": "Bangladesh Standard Time", "Asia/Dili": "Tokyo Standard Time", "Asia/Dubai": "Arabian Standard Time", "Asia/Dushanbe": "West Asia Standard Time", "Asia/Famagusta": "GTB Standard Time", "Asia/Gaza": "West Bank Standard Time", "Asia/Harbin": "China Standard Time", "Asia/Hebron": "West Bank Standard Time", "Asia/Ho_Chi_Minh": "SE Asia Standard Time", "Asia/Hong_Kong": "China Standard Time", "Asia/Hovd": "W. Mongolia Standard Time", "Asia/Irkutsk": "North Asia East Standard Time", "Asia/Istanbul": "Turkey Standard Time", "Asia/Jakarta": "SE Asia Standard Time", "Asia/Jayapura": "Tokyo Standard Time", "Asia/Jerusalem": "Israel Standard Time", "Asia/Kabul": "Afghanistan Standard Time", "Asia/Kamchatka": "Russia Time Zone 11", "Asia/Karachi": "Pakistan Standard Time", "Asia/Kashgar": "Central Asia Standard Time", "Asia/Kathmandu": "Nepal Standard Time", "Asia/Katmandu": "Nepal Standard Time", "Asia/Khandyga": "Yakutsk Standard Time", "Asia/Kolkata": "India Standard Time", "Asia/Krasnoyarsk": "North Asia Standard Time", "Asia/Kuala_Lumpur": "Singapore Standard Time", "Asia/Kuching": "Singapore Standard Time", "Asia/Kuwait": "Arab Standard Time", "Asia/Macao": "China Standard Time", "Asia/Macau": "China Standard Time", "Asia/Magadan": "Magadan Standard Time", "Asia/Makassar": "Singapore Standard Time", "Asia/Manila": "Singapore Standard Time", "Asia/Muscat": "Arabian Standard Time", "Asia/Nicosia": "GTB Standard Time", "Asia/Novokuznetsk": "North Asia Standard Time", "Asia/Novosibirsk": "N. Central Asia Standard Time", "Asia/Omsk": "Omsk Standard Time", "Asia/Oral": "West Asia Standard Time", "Asia/Phnom_Penh": "SE Asia Standard Time", "Asia/Pontianak": "SE Asia Standard Time", "Asia/Pyongyang": "North Korea Standard Time", "Asia/Qatar": "Arab Standard Time", "Asia/Qostanay": "Central Asia Standard Time", "Asia/Qyzylorda": "Qyzylorda Standard Time", "Asia/Rangoon": "Myanmar Standard Time", "Asia/Riyadh": "Arab Standard Time", "Asia/Saigon": "SE Asia Standard Time", "Asia/Sakhalin": "Sakhalin Standard Time", "Asia/Samarkand": "West Asia Standard Time", "Asia/Seoul": "Korea Standard Time", "Asia/Shanghai": "China Standard Time", "Asia/Singapore": "Singapore Standard Time", "Asia/Srednekolymsk": "Russia Time Zone 10", "Asia/Taipei": "Taipei Standard Time", "Asia/Tashkent": "West Asia Standard Time", "Asia/Tbilisi": "Georgian Standard Time", "Asia/Tehran": "Iran Standard Time", "Asia/Tel_Aviv": "Israel Standard Time", "Asia/Thimbu": "Bangladesh Standard Time", "Asia/Thimphu": "Bangladesh Standard Time", "Asia/Tokyo": "Tokyo Standard Time", "Asia/Tomsk": "Tomsk Standard Time", "Asia/Ujung_Pandang": "Singapore Standard Time", "Asia/Ulaanbaatar": "Ulaanbaatar Standard Time", "Asia/Ulan_Bator": "Ulaanbaatar Standard Time", "Asia/Urumqi": "Central Asia Standard Time", "Asia/Ust-Nera": "Vladivostok Standard Time", "Asia/Vientiane": "SE Asia Standard Time", "Asia/Vladivostok": "Vladivostok Standard Time", "Asia/Yakutsk": "Yakutsk Standard Time", "Asia/Yangon": "Myanmar Standard Time", "Asia/Yekaterinburg": "Ekaterinburg Standard Time", "Asia/Yerevan": "Caucasus Standard Time", "Atlantic/Azores": "Azores Standard Time", "Atlantic/Bermuda": "Atlantic Standard Time", "Atlantic/Canary": "GMT Standard Time", "Atlantic/Cape_Verde": "Cape Verde Standard Time", "Atlantic/Faeroe": "GMT Standard Time", "Atlantic/Faroe": "GMT Standard Time", "Atlantic/Jan_Mayen": "W. Europe Standard Time", "Atlantic/Madeira": "GMT Standard Time", "Atlantic/Reykjavik": "Greenwich Standard Time", "Atlantic/South_Georgia": "UTC-02", "Atlantic/St_Helena": "Greenwich Standard Time", "Atlantic/Stanley": "SA Eastern Standard Time", "Australia/ACT": "AUS Eastern Standard Time", "Australia/Adelaide": "Cen. Australia Standard Time", "Australia/Brisbane": "E. Australia Standard Time", "Australia/Broken_Hill": "Cen. Australia Standard Time", "Australia/Canberra": "AUS Eastern Standard Time", "Australia/Currie": "Tasmania Standard Time", "Australia/Darwin": "AUS Central Standard Time", "Australia/Eucla": "Aus Central W. Standard Time", "Australia/Hobart": "Tasmania Standard Time", "Australia/LHI": "Lord Howe Standard Time", "Australia/Lindeman": "E. Australia Standard Time", "Australia/Lord_Howe": "Lord Howe Standard Time", "Australia/Melbourne": "AUS Eastern Standard Time", "Australia/NSW": "AUS Eastern Standard Time", "Australia/North": "AUS Central Standard Time", "Australia/Perth": "W. Australia Standard Time", "Australia/Queensland": "E. Australia Standard Time", "Australia/South": "Cen. Australia Standard Time", "Australia/Sydney": "AUS Eastern Standard Time", "Australia/Tasmania": "Tasmania Standard Time", "Australia/Victoria": "AUS Eastern Standard Time", "Australia/West": "W. Australia Standard Time", "Australia/Yancowinna": "Cen. Australia Standard Time", "Brazil/Acre": "SA Pacific Standard Time", "Brazil/DeNoronha": "UTC-02", "Brazil/East": "E. South America Standard Time", "Brazil/West": "SA Western Standard Time", "CST6CDT": "Central Standard Time", "Canada/Atlantic": "Atlantic Standard Time", "Canada/Central": "Central Standard Time", "Canada/Eastern": "Eastern Standard Time", "Canada/Mountain": "Mountain Standard Time", "Canada/Newfoundland": "Newfoundland Standard Time", "Canada/Pacific": "Pacific Standard Time", "Canada/Saskatchewan": "Canada Central Standard Time", "Canada/Yukon": "Yukon Standard Time", "Chile/Continental": "Pacific SA Standard Time", "Chile/EasterIsland": "Easter Island Standard Time", "Cuba": "Cuba Standard Time", "EST5EDT": "Eastern Standard Time", "Egypt": "Egypt Standard Time", "Eire": "GMT Standard Time", "Etc/GMT": "UTC", "Etc/GMT+0": "UTC", "Etc/GMT+1": "Cape Verde Standard Time", "Etc/GMT+10": "Hawaiian Standard Time", "Etc/GMT+11": "UTC-11", "Etc/GMT+12": "Dateline Standard Time", "Etc/GMT+2": "UTC-02", "Etc/GMT+3": "SA Eastern Standard Time", "Etc/GMT+4": "SA Western Standard Time", "Etc/GMT+5": "SA Pacific Standard Time", "Etc/GMT+6": "Central America Standard Time", "Etc/GMT+7": "US Mountain Standard Time", "Etc/GMT+8": "UTC-08", "Etc/GMT+9": "UTC-09", "Etc/GMT-0": "UTC", "Etc/GMT-1": "W. Central Africa Standard Time", "Etc/GMT-10": "West Pacific Standard Time", "Etc/GMT-11": "Central Pacific Standard Time", "Etc/GMT-12": "UTC+12", "Etc/GMT-13": "UTC+13", "Etc/GMT-14": "Line Islands Standard Time", "Etc/GMT-2": "South Africa Standard Time", "Etc/GMT-3": "E. Africa Standard Time", "Etc/GMT-4": "Arabian Standard Time", "Etc/GMT-5": "West Asia Standard Time", "Etc/GMT-6": "Central Asia Standard Time", "Etc/GMT-7": "SE Asia Standard Time", "Etc/GMT-8": "Singapore Standard Time", "Etc/GMT-9": "Tokyo Standard Time", "Etc/GMT0": "UTC", "Etc/Greenwich": "UTC", "Etc/UCT": "UTC", "Etc/UTC": "UTC", "Etc/Universal": "UTC", "Etc/Zulu": "UTC", "Europe/Amsterdam": "W. Europe Standard Time", "Europe/Andorra": "W. Europe Standard Time", "Europe/Astrakhan": "Astrakhan Standard Time", "Europe/Athens": "GTB Standard Time", "Europe/Belfast": "GMT Standard Time", "Europe/Belgrade": "Central Europe Standard Time", "Europe/Berlin": "W. Europe Standard Time", "Europe/Bratislava": "Central Europe Standard Time", "Europe/Brussels": "Romance Standard Time", "Europe/Bucharest": "GTB Standard Time", "Europe/Budapest": "Central Europe Standard Time", "Europe/Busingen": "W. Europe Standard Time", "Europe/Chisinau": "E. Europe Standard Time", "Europe/Copenhagen": "Romance Standard Time", "Europe/Dublin": "GMT Standard Time", "Europe/Gibraltar": "W. Europe Standard Time", "Europe/Guernsey": "GMT Standard Time", "Europe/Helsinki": "FLE Standard Time", "Europe/Isle_of_Man": "GMT Standard Time", "Europe/Istanbul": "Turkey Standard Time", "Europe/Jersey": "GMT Standard Time", "Europe/Kaliningrad": "Kaliningrad Standard Time", "Europe/Kiev": "FLE Standard Time", "Europe/Kirov": "Russian Standard Time", "Europe/Kyiv": "FLE Standard Time", "Europe/Lisbon": "GMT Standard Time", "Europe/Ljubljana": "Central Europe Standard Time", "Europe/London": "GMT Standard Time", "Europe/Luxembourg": "W. Europe Standard Time", "Europe/Madrid": "Romance Standard Time", "Europe/Malta": "W. Europe Standard Time", "Europe/Mariehamn": "FLE Standard Time", "Europe/Minsk": "Belarus Standard Time", "Europe/Monaco": "W. Europe Standard Time", "Europe/Moscow": "Russian Standard Time", "Europe/Nicosia": "GTB Standard Time", "Europe/Oslo": "W. Europe Standard Time", "Europe/Paris": "Romance Standard Time", "Europe/Podgorica": "Central Europe Standard Time", "Europe/Prague": "Central Europe Standard Time", "Europe/Riga": "FLE Standard Time", "Europe/Rome": "W. Europe Standard Time", "Europe/Samara": "Russia Time Zone 3", "Europe/San_Marino": "W. Europe Standard Time", "Europe/Sarajevo": "Central European Standard Time", "Europe/Saratov": "Saratov Standard Time", "Europe/Simferopol": "Russian Standard Time", "Europe/Skopje": "Central European Standard Time", "Europe/Sofia": "FLE Standard Time", "Europe/Stockholm": "W. Europe Standard Time", "Europe/Tallinn": "FLE Standard Time", "Europe/Tirane": "Central Europe Standard Time", "Europe/Tiraspol": "E. Europe Standard Time", "Europe/Ulyanovsk": "Astrakhan Standard Time", "Europe/Uzhgorod": "FLE Standard Time", "Europe/Vaduz": "W. Europe Standard Time", "Europe/Vatican": "W. Europe Standard Time", "Europe/Vienna": "W. Europe Standard Time", "Europe/Vilnius": "FLE Standard Time", "Europe/Volgograd": "Volgograd Standard Time", "Europe/Warsaw": "Central European Standard Time", "Europe/Zagreb": "Central European Standard Time", "Europe/Zaporozhye": "FLE Standard Time", "Europe/Zurich": "W. Europe Standard Time", "GB": "GMT Standard Time", "GB-Eire": "GMT Standard Time", "GMT+0": "UTC", "GMT-0": "UTC", "GMT0": "UTC", "Greenwich": "UTC", "Hongkong": "China Standard Time", "Iceland": "Greenwich Standard Time", "Indian/Antananarivo": "E. Africa Standard Time", "Indian/Chagos": "Central Asia Standard Time", "Indian/Christmas": "SE Asia Standard Time", "Indian/Cocos": "Myanmar Standard Time", "Indian/Comoro": "E. Africa Standard Time", "Indian/Kerguelen": "West Asia Standard Time", "Indian/Mahe": "Mauritius Standard Time", "Indian/Maldives": "West Asia Standard Time", "Indian/Mauritius": "Mauritius Standard Time", "Indian/Mayotte": "E. Africa Standard Time", "Indian/Reunion": "Mauritius Standard Time", "Iran": "Iran Standard Time", "Israel": "Israel Standard Time", "Jamaica": "SA Pacific Standard Time", "Japan": "Tokyo Standard Time", "Kwajalein": "UTC+12", "Libya": "Libya Standard Time", "MST7MDT": "Mountain Standard Time", "Mexico/BajaNorte": "Pacific Standard Time (Mexico)", "Mexico/BajaSur": "Mountain Standard Time (Mexico)", "Mexico/General": "Central Standard Time (Mexico)", "NZ": "New Zealand Standard Time", "NZ-CHAT": "Chatham Islands Standard Time", "Navajo": "Mountain Standard Time", "PRC": "China Standard Time", "PST8PDT": "Pacific Standard Time", "Pacific/Apia": "Samoa Standard Time", "Pacific/Auckland": "New Zealand Standard Time", "Pacific/Bougainville": "Bougainville Standard Time", "Pacific/Chatham": "Chatham Islands Standard Time", "Pacific/Chuuk": "West Pacific Standard Time", "Pacific/Easter": "Easter Island Standard Time", "Pacific/Efate": "Central Pacific Standard Time", "Pacific/Enderbury": "UTC+13", "Pacific/Fakaofo": "UTC+13", "Pacific/Fiji": "Fiji Standard Time", "Pacific/Funafuti": "UTC+12", "Pacific/Galapagos": "Central America Standard Time", "Pacific/Gambier": "UTC-09", "Pacific/Guadalcanal": "Central Pacific Standard Time", "Pacific/Guam": "West Pacific Standard Time", "Pacific/Honolulu": "Hawaiian Standard Time", "Pacific/Johnston": "Hawaiian Standard Time", "Pacific/Kanton": "UTC+13", "Pacific/Kiritimati": "Line Islands Standard Time", "Pacific/Kosrae": "Central Pacific Standard Time", "Pacific/Kwajalein": "UTC+12", "Pacific/Majuro": "UTC+12", "Pacific/Marquesas": "Marquesas Standard Time", "Pacific/Midway": "UTC-11", "Pacific/Nauru": "UTC+12", "Pacific/Niue": "UTC-11", "Pacific/Norfolk": "Norfolk Standard Time", "Pacific/Noumea": "Central Pacific Standard Time", "Pacific/Pago_Pago": "UTC-11", "Pacific/Palau": "Tokyo Standard Time", "Pacific/Pitcairn": "UTC-08", "Pacific/Pohnpei": "Central Pacific Standard Time", "Pacific/Ponape": "Central Pacific Standard Time", "Pacific/Port_Moresby": "West Pacific Standard Time", "Pacific/Rarotonga": "Hawaiian Standard Time", "Pacific/Saipan": "West Pacific Standard Time", "Pacific/Samoa": "UTC-11", "Pacific/Tahiti": "Hawaiian Standard Time", "Pacific/Tarawa": "UTC+12", "Pacific/Tongatapu": "Tonga Standard Time", "Pacific/Truk": "West Pacific Standard Time", "Pacific/Wake": "UTC+12", "Pacific/Wallis": "UTC+12", "Pacific/Yap": "West Pacific Standard Time", "Poland": "Central European Standard Time", "Portugal": "GMT Standard Time", "ROC": "Taipei Standard Time", "ROK": "Korea Standard Time", "Singapore": "Singapore Standard Time", "Turkey": "Turkey Standard Time", "UCT": "UTC", "US/Alaska": "Alaskan Standard Time", "US/Aleutian": "Aleutian Standard Time", "US/Arizona": "US Mountain Standard Time", "US/Central": "Central Standard Time", "US/Eastern": "Eastern Standard Time", "US/Hawaii": "Hawaiian Standard Time", "US/Indiana-Starke": "Central Standard Time", "US/Michigan": "Eastern Standard Time", "US/Mountain": "Mountain Standard Time", "US/Pacific": "Pacific Standard Time", "US/Samoa": "UTC-11", "UTC": "UTC", "Universal": "UTC", "W-SU": "Russian Standard Time", "Zulu": "UTC", } tzlocal-5.2/update_windows_mappings.py000077500000000000000000000072021451525731100203440ustar00rootroot00000000000000#!/usr/bin/env python3 # This script generates the mapping between MS Windows timezone names and # tzdata/Olsen timezone names, by retrieving a file: # http://unicode.org/cldr/data/common/supplemental/supplementalData.xml # and parsing it, and from this generating the file windows_tz.py. # # It must be run with Python 3. import ftplib import logging import tarfile from io import BytesIO from pprint import pprint from urllib.parse import urlparse from urllib.request import urlopen from xml.dom import minidom WIN_ZONES_URL = "https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml" ZONEINFO_URL = "ftp://ftp.iana.org/tz/tzdata-latest.tar.gz" logging.basicConfig(level=logging.INFO) log = logging.getLogger("tzlocal") def update_old_names(): """Fetches the list of old tz names and returns a mapping""" url = urlparse(ZONEINFO_URL) log.info("Connecting to %s" % url.netloc) ftp = ftplib.FTP(url.netloc) ftp.login() gzfile = BytesIO() log.info("Fetching zoneinfo database") ftp.retrbinary("RETR " + url.path, gzfile.write) gzfile.seek(0) log.info("Extracting backwards data") archive = tarfile.open(mode="r:gz", fileobj=gzfile) backward = {} for line in archive.extractfile("backward").readlines(): if line[0] == "#": continue if len(line.strip()) == 0: continue parts = line.split() if parts[0] != b"Link": continue backward[parts[2].decode("ascii")] = parts[1].decode("ascii") return backward def update_windows_zones(): backward = update_old_names() log.info("Fetching Windows mapping info from unicode.org") source = urlopen(WIN_ZONES_URL).read() dom = minidom.parseString(source) for element in dom.getElementsByTagName("mapTimezones"): if element.getAttribute("type") == "windows": break log.info("Making windows mapping") win_tz = {} tz_win = {} for mapping in element.getElementsByTagName("mapZone"): if mapping.getAttribute("territory") == "001": win_tz[mapping.getAttribute("other")] = mapping.getAttribute("type").split( " " )[0] if win_tz[mapping.getAttribute("other")].startswith("Etc"): print( win_tz[mapping.getAttribute("other")], mapping.getAttribute("type").split(" ")[0], ) for tz_name in mapping.getAttribute("type").split(" "): tz_win[tz_name] = mapping.getAttribute("other") log.info("Adding backwards and forwards data") # Map in the backwards (or forwards) compatible zone names for backward_compat_name, standard_name in backward.items(): if backward_compat_name not in tz_win: win_zone = tz_win.get(standard_name, None) if win_zone: tz_win[backward_compat_name] = win_zone if standard_name not in tz_win: win_zone = tz_win.get(backward_compat_name, None) if win_zone: tz_win[standard_name] = win_zone # Etc/UTC is a common but non-standard alias for Etc/GMT: tz_win["Etc/UTC"] = "UTC" log.info("Writing mapping") with open("tzlocal/windows_tz.py", "w") as out: out.write( "# This file is autogenerated by the update_windows_mapping.py script\n" "# Do not edit.\nwin_tz = " ) pprint(win_tz, out) out.write( "\n# Old name for the win_tz variable:\ntz_names = win_tz\n\ntz_win = " ) pprint(tz_win, out) log.info("Done") if __name__ == "__main__": update_windows_zones()