GitHub - ringsaturn/tzfpy: Probably the fastest Python package to convert longitude/latitude to timezone name. (original) (raw)

tzfpy

Note

  1. It's probably the fastest Python package to convert longitude/latitude to timezone name.
  2. This package use a simplified polygon data and not so accurate around borders.
  3. Rust use lazy init, so first calling will be a little slow.
  4. Use about 40MB memory.
  5. It's tested under Python 3.9+.
  6. Try it online:

Usage

Please note that new timezone names may be added to tzfpy, which could be incompatible with old version package like pytz or tzdata. As an option, tzfpy supports install compatible version of those packages with extra params.

Install just tzfpy

pip install tzfpy

Install with pytz

pip install "tzfpy[pytz]"

Install with tzdata. https://github.com/python/tzdata

pip install "tzfpy[tzdata]"

Install via conda, see more in https://github.com/conda-forge/tzfpy-feedstock

conda install -c conda-forge tzfpy

from tzfpy import get_tz, get_tzs get_tz(116.3883, 39.9289) # in (longitude, latitude) order. 'Asia/Shanghai' get_tzs(87.4160, 44.0400) # in (longitude, latitude) order. ['Asia/Shanghai', 'Asia/Urumqi']

Best practices

  1. Always install tzfpy with tzdata extra: pip install tzfpy[tzdata]
  2. Use Python's zoneinfo package(import zoneinfo, akatzdata in PyPI) to handle timezone names, even if you are using arrow:
    examples/tzfpy_with_datetime.py:
    from datetime import UTC as DT_UTC
    from datetime import datetime
    from zoneinfo import ZoneInfo
    from tzfpy import get_tz
    tz = get_tz(139.7744, 35.6812) # Tokyo
    now = datetime.now(DT_UTC)
    now = now.replace(tzinfo=ZoneInfo(tz))
    print(now)

2025-04-29 01:33:56.325194+09:00

examples/tzfpy_with_arrow.py:
from zoneinfo import ZoneInfo
import arrow
from tzfpy import get_tz
tz = get_tz(139.7744, 35.6812) # Tokyo
arrow_now = arrow.now(ZoneInfo(tz))
print(arrow_now.format("YYYY-MM-DD HH:mm:ss ZZZ"))

2025-04-29 01:33:56.325194+09:00

If you are using whenever, since whenever use tzdata internally, so it's compatible with tzfpy:
examples/tzfpy_with_whenever.py:
from whenever import Instant
from tzfpy import get_tz
now = Instant.now()
tz = get_tz(139.7744, 35.6812) # Tokyo
now = now.to_tz(tz)
print(now)

2025-04-29T10:33:28.427784+09:00[Asia/Tokyo]

Performance

Benchmark runs underv1.0.0 on my MacBook Pro with Apple M3 Max.

pytest --benchmark-warmup=on --benchmark-warmup-iterations=100 tests/test_bench.py

-------------------------------------------------------------- benchmark: 1 tests --------------------------------------------------------------
Name (time in ns)                 Min          Max        Mean      StdDev      Median         IQR    Outliers  OPS (Kops/s)  Rounds  Iterations
------------------------------------------------------------------------------------------------------------------------------------------------
test_tzfpy_random_cities     895.7926  11,420.8087  2,597.6093  1,331.8472  2,337.5032  1,587.5907  11611;1000      384.9694   33614          10
------------------------------------------------------------------------------------------------------------------------------------------------

Legend:
  Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile.
  OPS: Operations Per Second, computed as 1 / Mean
Results (2.03s):
         1 passed

Or you can view more benchmark results onGitHub Action summary page.

More benchmarks compared with other packages can be found inringsaturn/tz-benchmark.

Background

tzfpy was originally written in Go named tzf and use CGO compiled to.so to be used by Python. Since v0.11.0 it's rewritten in Rust built on PyO3 and tzf-rs, a tzf's Rust port.

I have written an article about the history of tzf, its Rust port, and its Rust port's Python binding; you can view ithere.

Compare with other packages

Please note that directly compare with other packages is not fair, because they have different use cases and design goals, for example, the precise.

TimezoneFinder

I got lots of inspiration from it. Timezonefinder is a very good package and it's mostly written in Python, so it's easy to use. And it's muchmore widely usedcompared with tzfpy if you care about that.

However, it's slower than tzfpy, especially around the borders, and I have lots of API requests from there. That's the reason I created tzf originally. And then tzf-rs and tzfpy.

pytzwhere

I recommend to read timezonefinder'sComparison to pytzwheresince it's very detailed.

Contributing

Install:

Available commands: build - Build the project using uv fmt - Format the code using ruff lint - Lint the code using ruff sync - Sync and compile the project using uv lock - Lock dependencies using uv upgrade - Upgrade dependencies using uv all - Run lock, sync, fmt, lint, and test test - Run tests using pytest

LICENSE

This project is licensed under the MIT license. The data is licensed under theODbL license, same asevansiroky/timezone-boundary-builder