diff --git a/ci/deps/actions-310.yaml b/ci/deps/actions-310.yaml index 65918005ad6f1..5f7beb24674e7 100644 --- a/ci/deps/actions-310.yaml +++ b/ci/deps/actions-310.yaml @@ -47,6 +47,7 @@ dependencies: - scipy - sqlalchemy - tabulate + - tzdata>=2022a - xarray - xlrd - xlsxwriter diff --git a/ci/deps/actions-38-minimum_versions.yaml b/ci/deps/actions-38-minimum_versions.yaml index a57c7279e2e9b..f4b4d0c79aada 100644 --- a/ci/deps/actions-38-minimum_versions.yaml +++ b/ci/deps/actions-38-minimum_versions.yaml @@ -49,6 +49,7 @@ dependencies: - scipy=1.7.1 - sqlalchemy=1.4.16 - tabulate=0.8.9 + - tzdata=2022a - xarray=0.19.0 - xlrd=2.0.1 - xlsxwriter=1.4.3 diff --git a/ci/deps/actions-39.yaml b/ci/deps/actions-39.yaml index 8605a9f4520d7..99b55a4c83046 100644 --- a/ci/deps/actions-39.yaml +++ b/ci/deps/actions-39.yaml @@ -47,6 +47,7 @@ dependencies: - scipy - sqlalchemy - tabulate + - tzdata>=2022a - xarray - xlrd - xlsxwriter diff --git a/doc/source/getting_started/install.rst b/doc/source/getting_started/install.rst index 605a69b26a646..e24528e611c12 100644 --- a/doc/source/getting_started/install.rst +++ b/doc/source/getting_started/install.rst @@ -270,6 +270,23 @@ For example, :func:`pandas.read_hdf` requires the ``pytables`` package, while optional dependency is not installed, pandas will raise an ``ImportError`` when the method requiring that dependency is called. +Timezones +^^^^^^^^^ + +========================= ========================= ============================================================= +Dependency Minimum Version Notes +========================= ========================= ============================================================= +tzdata 2022.1(pypi)/ Allows the use of ``zoneinfo`` timezones with pandas. + 2022a(for system tzdata) **Note**: You only need to install the pypi package if your + system does not already provide the IANA tz database. + However, the minimum tzdata version still applies, even if it + is not enforced through an error. + + If you would like to keep your system tzdata version updated, + it is recommended to use the ``tzdata`` package from + conda-forge. +========================= ========================= ============================================================= + Visualization ^^^^^^^^^^^^^ diff --git a/environment.yml b/environment.yml index ec2e0a3860432..121df3feb9cae 100644 --- a/environment.yml +++ b/environment.yml @@ -48,6 +48,7 @@ dependencies: - scipy - sqlalchemy - tabulate + - tzdata>=2022a - xarray - xlrd - xlsxwriter diff --git a/pandas/_libs/tslibs/timezones.pyx b/pandas/_libs/tslibs/timezones.pyx index 22a154be5fcad..5992f31e96988 100644 --- a/pandas/_libs/tslibs/timezones.pyx +++ b/pandas/_libs/tslibs/timezones.pyx @@ -3,6 +3,8 @@ from datetime import ( timezone, ) +from pandas.compat._optional import import_optional_dependency + try: # py39+ import zoneinfo @@ -67,6 +69,9 @@ cdef inline bint is_utc_zoneinfo(tzinfo tz): utc_zoneinfo = ZoneInfo("UTC") except zoneinfo.ZoneInfoNotFoundError: return False + # Warn if tzdata is too old, even if there is a system tzdata to alert + # users about the mismatch between local/system tzdata + import_optional_dependency("tzdata", errors="warn", min_version="2022.1") return tz is utc_zoneinfo diff --git a/pandas/compat/_optional.py b/pandas/compat/_optional.py index ad6c6fb839f10..3801a1648f1e7 100644 --- a/pandas/compat/_optional.py +++ b/pandas/compat/_optional.py @@ -44,6 +44,7 @@ "xlwt": "1.3.0", "xlsxwriter": "1.4.3", "zstandard": "0.15.2", + "tzdata": "2022.1", } # A mapping from import name to package name (on PyPI) for packages where diff --git a/pandas/conftest.py b/pandas/conftest.py index e176707d8a8f1..babd88b60b366 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -83,6 +83,14 @@ # Import "zoneinfo" could not be resolved (reportMissingImports) import zoneinfo # type: ignore[no-redef] + # Although zoneinfo can be imported in Py39, it is effectively + # "not available" without tzdata/IANA tz data. + # We will set zoneinfo to not found in this case + try: + zoneinfo.ZoneInfo("UTC") # type: ignore[attr-defined] + except zoneinfo.ZoneInfoNotFoundError: # type: ignore[attr-defined] + zoneinfo = None + # Until https://github.com/numpy/numpy/issues/19078 is sorted out, just suppress suppress_npdev_promotion_warning = pytest.mark.filterwarnings( "ignore:Promotion of numbers and bools:FutureWarning" diff --git a/requirements-dev.txt b/requirements-dev.txt index 5a9647456cb0b..10285810d8e7a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -39,6 +39,7 @@ s3fs scipy sqlalchemy tabulate +tzdata>=2022.1 xarray xlrd xlsxwriter diff --git a/scripts/generate_pip_deps_from_conda.py b/scripts/generate_pip_deps_from_conda.py index 8cb539d3b02c8..a27d089cdb9e8 100755 --- a/scripts/generate_pip_deps_from_conda.py +++ b/scripts/generate_pip_deps_from_conda.py @@ -21,6 +21,7 @@ import yaml EXCLUDE = {"python", "c-compiler", "cxx-compiler"} +REMAP_VERSION = {"tzdata": "2022.1"} RENAME = {"pytables": "tables", "geopandas-base": "geopandas", "pytorch": "torch"} @@ -41,7 +42,8 @@ def conda_package_to_pip(package: str): pkg, version = package.split(compare) if pkg in EXCLUDE: return - + if pkg in REMAP_VERSION: + return "".join((pkg, compare, REMAP_VERSION[pkg])) if pkg in RENAME: return "".join((RENAME[pkg], compare, version)) diff --git a/scripts/validate_min_versions_in_sync.py b/scripts/validate_min_versions_in_sync.py index 4dbf6a4cdcef8..1b937673672f8 100755 --- a/scripts/validate_min_versions_in_sync.py +++ b/scripts/validate_min_versions_in_sync.py @@ -21,6 +21,7 @@ pathlib.Path("ci/deps").absolute().glob("actions-*-minimum_versions.yaml") ) CODE_PATH = pathlib.Path("pandas/compat/_optional.py").resolve() +EXCLUDE_DEPS = {"tzdata"} # pandas package is not available # in pre-commit environment sys.path.append("pandas/compat") @@ -34,6 +35,8 @@ def get_versions_from_code() -> dict[str, str]: install_map = _optional.INSTALL_MAPPING versions = _optional.VERSIONS + for item in EXCLUDE_DEPS: + versions.pop(item) return { install_map.get(k, k).casefold(): v for k, v in versions.items() @@ -55,6 +58,8 @@ def get_versions_from_ci(content: list[str]) -> tuple[dict[str, str], dict[str, elif seen_required and line.strip(): package, version = line.strip().split("=") package = package[2:] + if package in EXCLUDE_DEPS: + continue if not seen_optional: required_deps[package] = version else: