{"id":13782349,"url":"https://github.com/ringsaturn/tzf-rs","last_synced_at":"2026-04-30T03:04:49.079Z","repository":{"id":63523284,"uuid":"498415703","full_name":"ringsaturn/tzf-rs","owner":"ringsaturn","description":"Get timezone via longitude\u0026latitude in Rust in a fast way","archived":false,"fork":false,"pushed_at":"2026-04-24T10:11:06.000Z","size":631,"stargazers_count":104,"open_issues_count":2,"forks_count":8,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-24T12:11:38.105Z","etag":null,"topics":["latitude","longitude","rust","timezone","timezone-library","timezone-picker","tzf"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/tzf-rs","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ringsaturn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"polar":null,"custom":["https://redcross.org.ua/en/donate/"]}},"created_at":"2022-05-31T16:34:06.000Z","updated_at":"2026-04-24T11:02:48.000Z","dependencies_parsed_at":"2026-03-02T03:07:58.642Z","dependency_job_id":null,"html_url":"https://github.com/ringsaturn/tzf-rs","commit_stats":{"total_commits":222,"total_committers":6,"mean_commits":37.0,"dds":"0.36036036036036034","last_synced_commit":"dd08e0ad82a82e4fc8683f094556c8a11f6a0137"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"purl":"pkg:github/ringsaturn/tzf-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringsaturn%2Ftzf-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringsaturn%2Ftzf-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringsaturn%2Ftzf-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringsaturn%2Ftzf-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ringsaturn","download_url":"https://codeload.github.com/ringsaturn/tzf-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ringsaturn%2Ftzf-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32269467,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T09:15:33.318Z","status":"ssl_error","status_checked_at":"2026-04-25T09:15:31.997Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["latitude","longitude","rust","timezone","timezone-library","timezone-picker","tzf"],"created_at":"2024-08-03T18:01:35.573Z","updated_at":"2026-04-25T17:04:27.254Z","avatar_url":"https://github.com/ringsaturn.png","language":"Rust","funding_links":["https://redcross.org.ua/en/donate/"],"categories":["Rust","Watchlist"],"sub_categories":[],"readme":"# tzf-rs: a fast timezone finder for Rust. [![Rust](https://github.com/ringsaturn/tzf-rs/actions/workflows/rust.yml/badge.svg)](https://github.com/ringsaturn/tzf-rs/actions/workflows/rust.yml) [![Documentation](https://docs.rs/tzf-rs/badge.svg)](https://docs.rs/tzf-rs) [![Crates.io Version](https://img.shields.io/crates/v/tzf-rs)](https://crates.io/crates/tzf-rs) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fringsaturn%2Ftzf-rs.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fringsaturn%2Ftzf-rs?ref=badge_shield)\n\n![Time zone map of the world](https://github.com/ringsaturn/tzf/blob/gh-pages/docs/tzf-social-media.png?raw=true)\n\n\u003e [!NOTE]\n\u003e\n\u003e This package uses simplified shape data so it is not entirely accurate around\n\u003e the border.\n\n- Released documentation: [docs.rs/tzf-rs](https://docs.rs/tzf-rs)\n- Latest documentation(not released yet):\n  [ringsaturn.github.io/tzf-rs](https://ringsaturn.github.io/tzf-rs/tzf_rs/)\n- Try it online: [tzf-web](https://ringsaturn.github.io/tzf-web/)\n\n## Build options\n\nBy default, the binary is built as well. If you don't want/need it, you can omit\nthe default features and build like this:\n\n```bash\ncargo build --no-default-features\n```\n\nOr add in the below way:\n\n```bash\ncargo add tzf-rs --no-default-features\n```\n\n## Best Practices\n\nIt's expensive to init tzf-rs's `Finder`/`FuzzyFinder`/`DefaultFinder`, so\nplease consider reusing instances or creating one as a global variable. Below is\na global variable example:\n\n```rust\nuse lazy_static::lazy_static;\nuse tzf_rs::DefaultFinder;\n\nlazy_static! {\n    static ref FINDER: DefaultFinder = DefaultFinder::new();\n}\n\nfn main() {\n    // Please note coords are lng-lat.\n    print!(\"{:?}\\n\", FINDER.get_tz_name(116.3883, 39.9289));\n    print!(\"{:?}\\n\", FINDER.get_tz_names(116.3883, 39.9289));\n}\n```\n\nFor reuse,\n[`racemap/rust-tz-service`](https://github.com/racemap/rust-tz-service) provides\na good example.\n\nA Redis protocol demo could be used here:\n[`ringsaturn/redizone`](https://github.com/ringsaturn/redizone).\n\n### Setup 100% Accurate Lookup\n\n\u003e [!NOTE]\n\u003e\n\u003e The built-in full data feature is introduced in `v1.3.0`.\n\u003e\n\u003e By default, tzf-rs uses a simplified shape data. If you need 100% accurate\n\u003e lookup, you can use the following code to setup:\n\u003e\n\u003e **This setup requeires more time and memory to build the `DefaultFinder`.**\n\n```toml\ntzf-rs = { git =  \"https://github.com/ringsaturn/tzf-rs\", rev = \"v{X}.{Y}.{Z}\", features = [\"full\"], default-features = false }\n```\n\n```rust,ignore\nuse tzf_rs::DefaultFinder;\n\nfn main() {\n    let finder = DefaultFinder::new_full();\n    println!(\"{}\", finder.timezonenames().len());\n    let tz_name = finder.get_tz_name(139.767125, 35.681236);\n    println!(\"tz_name: {}\", tz_name);\n}\n```\n\n## Advanced Usage - Toggle YStripes Index\n\n\u003e [!NOTE]\n\u003e\n\u003e This feature is introduced `v1.2.0` and is **enabled by default**, since the\n\u003e build time is not significantly increased, but the query time is significantly\n\u003e decreased. If you want to disable it, please use `FinderOptions::NoIndex`\n\u003e explicitly. Below is the code example to disable it:\n\u003e\n\u003e ```rust\n\u003e use tzf_rs::{DefaultFinder, FinderOptions};\n\u003e\n\u003e fn main() {\n\u003e     let default_finder = DefaultFinder::new_with_options(FinderOptions::no_index());\n\u003e     println!(\"{}\", default_finder.get_tz_name(139.767125, 35.681236));\n\u003e }\n\u003e ```\n\nYStripes needs more time and memory than NoIndex, below is data from my machine\nto build the `DefaultFinder` with currently supported index modes:\n\n| Index mode    | Build time (ms) | Memory usage (MiB) |\n| ------------- | --------------: | -----------------: |\n| No index      |             ~40 |                ~70 |\n| YStripes only |             ~50 |               ~110 |\n\nFor the performance comparison of different index modes, please see the\n[Performance](#performance) section below.\n\n## Advanced Usage - Export GeoJSON\n\n\u003e [!NOTE]\n\u003e\n\u003e This feature is designed for **data visualization purposes** and I can't\n\u003e guarantee the performance when using it in high-performance scenarios. Please\n\u003e do proper performance tests and necessary optimizations before using it in\n\u003e high performace production, for example caching the exported GeoJSON data or\n\u003e push to CDN.\n\nIt's a common use case make some visualization of timezone boundaries. For this\npurpose, tzf-rs provides methods to export the preindex tile data or specific\ntimezone polygons as GeoJSON format.\n\nTo enable this feature, you need to build tzf-rs with `export-geojson` feature:\n\n```toml\n# Please note that \u003e= 1.1.1 is required to have full GeoJSON functionality.\ntzf-rs = { version = \"{version}\", features = [\"export-geojson\"]}\n```\n\nThen you can use the following methods:\n\n```rust\n// examples/query_tokyo.rs\nuse tzf_rs::DefaultFinder;\n\nfn main() {\n    let default_finder = DefaultFinder::new();\n    let lng = 139.6917;\n    let lat = 35.6895;\n\n    let tz_name = default_finder.get_tz_name(lng, lat).to_owned();\n    println!(\n        \"The timezone at longitude {}, latitude {} is: {}\",\n        lng, lat, tz_name\n    );\n\n    // Get the Polygon boundary for the timezone\n    if let Some(boundary_file) = default_finder.finder.get_tz_geojson(\u0026tz_name) {\n        // It's GeoJSON Feature Collection, and the features contains \"MultiPolygon\" geometry for the timezone.\n        println!(\"Found GeoJSON feature for timezone: {}\", tz_name);\n        let mut polygons: usize = 0;\n        for feature in boundary_file.features {\n            polygons += feature.geometry.coordinates.len();\n        }\n        println!(\n            \"Total number of polygons in feature collection: {}\",\n            polygons\n        );\n    }\n\n    // Get the Index polygon boundary for the timezone\n    if let Some(index_boundary_file) = default_finder.fuzzy_finder.get_tz_geojson(\u0026tz_name) {\n        // It's GeoJSON Feature, and the geometry contains \"MultiPolygon\" for the timezone index.\n        // But the Polygons are actually map tiles.\n        println!(\"Found Index GeoJSON feature for timezone: {}\", tz_name);\n        let mut polygons: usize = 0;\n        for polygon in index_boundary_file.geometry.coordinates {\n            polygons += polygon.len();\n        }\n        println!(\n            \"Total number of tile polygons in index feature: {}\",\n            polygons\n        );\n    }\n}\n```\n\n```bash\ncargo run --example query_tokyo --features export-geojson\n```\n\n```console\nThe timezone at longitude 139.6917, latitude 35.6895 is: Asia/Tokyo\nFound GeoJSON feature for timezone: Asia/Tokyo\nTotal number of polygons in feature collection: 24\nFound Index GeoJSON feature for timezone: Asia/Tokyo\n```\n\nFor now, tzf-rs' binding in Wasm, named\n[tzf-wasm](https://github.com/ringsaturn/tzf-wasm), has exported this feature\nand it has been deployed to the [tzf-web](https://ringsaturn.github.io/tzf-web/)\nfor online usage.\n\n## Performance\n\nThe tzf-rs package is intended for high-performance geospatial query services,\nsuch as weather forecasting APIs. Most queries can be returned within a very\nshort time, averaging around 1,500 nanoseconds.\n\nHere is what has been done to improve performance:\n\n1. Using the simplified dataset by default.\n2. Using pre-indexing to handle most queries takes approximately 500\n   nanoseconds.\n3. Using a finely-tuned Ray Casting algorithm package\n   [`ringsaturn/geometry-rs`](https://github.com/ringsaturn/geometry-rs) to\n   verify whether a polygon contains a point.\n   - Using YStripes to accerate polygon queries. This polygon index works when\n     the pre-indexing missing, especially for queries around the border.\n\nThat's all. There are no black magic tricks inside the tzf-rs.\n\nBelow is a benchmark run on my MacBook Pro with Apple M3 Max:\n\nTopology-Simplified (bundled):\n\n| Target        | Dataset                        | Scenario      | Median estimate (µs) | Approx throughput (ops/s) | Avg peak RSS (MiB) |\n| ------------- | ------------------------------ | ------------- | -------------------: | ------------------------: | -----------------: |\n| Finder        | topology-simplified            | YStripes only |               1.2296 |                   813,273 |             103.30 |\n| Finder        | topology-simplified            | No index      |               6.5402 |                   152,901 |              51.68 |\n| DefaultFinder | topology-simplified + preindex | YStripes only |               1.1383 |                   878,503 |             125.98 |\n| DefaultFinder | topology-simplified + preindex | No index      |               2.2514 |                   444,168 |              77.79 |\n\nFull-Precision (full):\n\n| Target               | Dataset                   | Scenario      | Median estimate (µs) | Approx throughput (ops/s) | Avg peak RSS (MiB) |\n| -------------------- | ------------------------- | ------------- | -------------------: | ------------------------: | -----------------: |\n| Finder (full)        | full-precision            | YStripes only |               2.0852 |                   479,570 |             561.08 |\n| Finder (full)        | full-precision            | No index      |              37.6980 |                    26,527 |             252.54 |\n| DefaultFinder (full) | full-precision + preindex | YStripes only |               1.3488 |                   741,400 |             584.30 |\n| DefaultFinder (full) | full-precision + preindex | No index      |              11.2750 |                    88,692 |             278.63 |\n\nThe `FuzzyFinder` is not included in the benchmark, since it's query time is\nconsistent.\n\n\u003cdetails\u003e\n\u003csummary\u003eDefaultFinder's Benchmark charts (click to expand)\u003c/summary\u003e\n\nViolin plot:\n\n![](https://raw.githubusercontent.com/ringsaturn/tzf-rs/refs/heads/main/assets/violin.svg)\n\nNo Index:\n\n![](https://raw.githubusercontent.com/ringsaturn/tzf-rs/refs/heads/main/assets/no_index.pdf.svg)\n\nYStripes only:\n\n![](https://raw.githubusercontent.com/ringsaturn/tzf-rs/refs/heads/main/assets/ystripes_only.pdf.svg)\n\n\u003c/details\u003e\n\nYou can view more details from latest benchmark from\n[GitHub Actions logs](https://github.com/ringsaturn/tzf-rs/actions/workflows/rust.yml).\n\n## References\n\nI have written an article about the history of `tzf`, its Rust port, and its\nRust port's Python binding; you can view it\n[here](https://blog.ringsaturn.me/en/posts/2023-01-31-history-of-tzf/).\n\n- Original Go repo: [`ringsaturn/tzf`](https://github.com/ringsaturn/tzf)\n- Binary timezone data:\n  [`ringsaturn/tzf-rel`](https://github.com/ringsaturn/tzf-rel)\n- Geometry: use\n  [`ringsaturn/geometry-rs`](https://github.com/ringsaturn/geometry-rs) which is\n  [`tidwall/geometry`](https://github.com/tidwall/geometry)'s Rust port.\n- Continuous Benchmark compared with other packages:\n  [`ringsaturn/tz-benchmark`](https://github.com/ringsaturn/tz-benchmark)\n\n### Bindings\n\n- Ruby, see [`HarlemSquirrel/tzf-rb`](https://github.com/HarlemSquirrel/tzf-rb)\n- Python, see [`ringsaturn/tzfpy`](https://github.com/ringsaturn/tzfpy)\n- Wasm, see [`ringsaturn/tzf-wasm`](https://github.com/ringsaturn/tzf-wasm)\n- PostgreSQL extension, see\n  [`ringsaturn/pg-tzf`](https://github.com/ringsaturn/pg-tzf)\n\n## Command line\n\nThe binary helps in debugging tzf-rs and using it in (scripting) languages\nwithout bindings. Either specify the coordinates as parameters to get a single\ntime zone, or to look up multiple coordinates efficiently specify the ordering\nand pipe them to the binary one pair of coordinates per line.\n\n```shell\ntzf --lng 116.3883 --lat 39.9289\necho -e \"116.3883 39.9289\\n116.3883, 39.9289\" | tzf --stdin-order lng-lat\n```\n\nIf you are using Nixpkgs, you can install the `tzf` command line tool, please\nsee more in\n[Nixpkgs](https://search.nixos.org/packages?channel=unstable\u0026type=packages\u0026query=tzf-rs).\n\n## LICENSE\n\nThis project is licensed under the [MIT license](./LICENSE) and\n[Anti CSDN License](./LICENSE_ANTI_CSDN.md)[^anti_csdn]. The data is licensed\nunder the\n[ODbL license](https://github.com/ringsaturn/tzf-rel/blob/main/LICENSE), same as\n[`evansiroky/timezone-boundary-builder`](https://github.com/evansiroky/timezone-boundary-builder)\n\n[^anti_csdn]: This license is to prevent the use of this project by CSDN, has no\n    effect on other use cases.\n\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fringsaturn%2Ftzf-rs.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fringsaturn%2Ftzf-rs?ref=badge_large)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fringsaturn%2Ftzf-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fringsaturn%2Ftzf-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fringsaturn%2Ftzf-rs/lists"}