Rust (tzf-rs)
安装
cargo add tzf-rstzf-rs 2.0 不再使用 protobuf。它读取由 ringsaturn/tzf-dist 发布的 TZF 嵌入式二进制格式(.tzb);默认的 bundled feature 把 lite 文件(~4 MB)携带在 crate 内。格式本身参见嵌入式二进制格式。
两个查找器
| 类型 | 数据 | 峰值 RSS | 查询(随机城市 / 边界城市) |
|---|---|---|---|
DefaultFinder | lite .tzb 展开为多边形,带 FUZZY 快速路径 | ~47 MiB | 221 ns / 475 ns |
EmbeddedFinder | lite .tzb 原地查询 | ~10 MiB | 293 ns / 666 ns |
数据来自 tz-benchmark 的 2026-09-14 快照,在 Apple M3 Max 上针对 2026c 数据集测得,对象为 tzf-dist 0.0.2026-c-tzb2 上的 tzf-rs 2.1.1;该测试环境中 Rust 运行时的基线为 5.8 MiB。tzf-rs 2.1 重写了 EmbeddedFinder 的查询遍历(打开时校验、按 chunk 块和端点奇偶跳过、每个 group 的纬度条带、只探测带键的预索引缩放级别),并切换到 64 点 chunk 的数据;结果不变,边界城市一项在 2.0.0 上为 4.78 µs。
DefaultFinder::new() 约需 13 ms 打开,EmbeddedFinder::new() 约需 2 ms。计数型分配器记录到 EmbeddedFinder 保留 0.2 MiB 堆内存:其数据是 &'static 的嵌入切片,另加打开时构建的 chunk 跳过块和纬度条带索引(lite 上约 100 KB)。EmbeddedFinder 适用于内存受限目标,其边界城市查询耗时约为 DefaultFinder 的 1.4 倍。其余场景参见选择查找器。
复用查找器
构造过程会加载并校验整个文件,因此进程构建一个查找器并复用。用 LazyLock 静态变量持有它,不需要额外依赖:
use std::sync::LazyLock;
use tzf_rs::DefaultFinder;
static FINDER: LazyLock<DefaultFinder> = LazyLock::new(DefaultFinder::new);
fn main() {
// 坐标采用 (经度,纬度) 顺序。
println!("{:?}", FINDER.get_tz_name(116.3883, 39.9289));
println!("{:?}", FINDER.get_tz_names(116.3883, 39.9289));
}lazy_static 同样可用。LazyLock 自 Rust 1.80 起进入标准库。
查询
两个查找器提供相同的四个方法:
finder.get_tz_name(lng, lat) // -> &str,无匹配时为空
finder.get_tz_names(lng, lat) // -> Vec<&str>,按字典序排序
finder.timezonenames() // -> Vec<&str>
finder.data_version() // -> &str,例如 "2026c"
get_tz_name预索引优先:FUZZY 瓦片预索引可以在不做点在多边形内判定的情况下回答大部分查询。get_tz_names在两个查找器中都走多边形精确路径,不查询预索引。它适用于点可能属于多个时区的场景;共享边界上的点属于所有与之相接的多边形。
使用自行提供的字节
use tzf_rs::{DefaultFinder, EmbeddedFinder};
let data = std::fs::read("lite.tzb")?;
let finder = DefaultFinder::from_tzb(&data)?; // 加载期间借用,随后展开
static DATA: &[u8] = include_bytes!("../data/lite.tzb");
let finder = EmbeddedFinder::from_tzb(DATA)?; // 不复制;查询时原地读取
EmbeddedFinder::from_tzb 接受 impl Into<Cow<'static, [u8]>>,因此来自 include_bytes! 的 &'static [u8] 无需复制即可接管,持有所有权的 Vec<u8> 同样可以传入。两个构造函数都返回 Result<_, tzf_rs::Error>:文件在打开时会做 CRC 校验和结构校验,数据损坏时返回错误,而不产生空的查找器。
Error 带有 #[non_exhaustive],包含 Malformed、Profile、NoFuzzy 和 Index 四个变体。传入 .tzm 内存镜像字节时返回 Profile:tzf-rs 只读取 .tzb profile,M profile 由 Go 实现读取。
Cargo feature
| Feature | 默认 | 作用 |
|---|---|---|
bundled | 是 | 嵌入 tzf-dist 的 lite .tzb(~4 MB),启用 new() |
clap | 是 | 构建 tzf 命令行程序 |
full | 否 | 仅 git 提供的完整精度 .tzb(~14 MB),启用 new_full() |
export-geojson | 否 | GeoJSON 导出方法 |
full 与 bundled 互斥;同时启用时 crate 会触发 compile_error!。完整数据集超出 crates.io 的体积限制,因此从 git 引用:
[dependencies]
tzf-rs = { git = "https://github.com/ringsaturn/tzf-rs", rev = "v{X}.{Y}.{Z}", features = ["full"], default-features = false }use tzf_rs::DefaultFinder;
let finder = DefaultFinder::new_full();
println!("{}", finder.get_tz_name(139.767125, 35.681236));在库构建中去掉命令行程序:cargo build --no-default-features --features bundled。
GeoJSON 导出
启用 export-geojson 后,两个查找器都提供四个导出方法:
let world = finder.to_geojson(); // BoundaryFile
let tokyo = finder.get_tz_geojson("Asia/Tokyo"); // Option<BoundaryFile>
let tiles = finder.get_tz_preindex_geojson("Asia/Tokyo"); // Option<BoundaryFile>
let all_tiles = finder.to_preindex_geojson(); // Option<BoundaryFile>
预索引导出方法返回命名某个时区的 FUZZY 瓦片的包围矩形,即 get_tz_name 直接由预索引作答、无需回退到点在多边形内判定的区域。文件不含 FUZZY 区段时返回 None。
从 v1 迁移
| v1 | v2 |
|---|---|
DefaultFinder::new() | DefaultFinder::new(),调用处不变 |
DefaultFinder::new_full() | DefaultFinder::new_full(),调用处不变 |
Finder(仅多边形) | DefaultFinder;get_tz_names 仍为多边形精确路径 |
FuzzyFinder(仅瓦片) | 已移除;预索引是每个查找器内部的快速路径 |
Finder::from_compressed_topo(pb) | DefaultFinder::from_tzb(bytes) |
FuzzyFinder::from_pb(pb) | 已移除,无替代 |
FinderOptions / new_with_options | 已移除;YStripes 始终启用 |
finder.finder.get_tz_geojson(...) | finder.get_tz_geojson(...) |
FuzzyFinder 的瓦片包围盒 GeoJSON | 由 get_tz_preindex_geojson / to_preindex_geojson 取代 |
| — | 新增:EmbeddedFinder,原地查询且内存占用低 |
行为变化:
get_tz_names的结果现按字典序排序。DefaultFinder的get_tz_name由覆盖该点的预索引瓦片作答,与 v1DefaultFinder的语义一致。此前用 v1Finder获取多边形精确多结果的代码改调用get_tz_names。- 字节构造函数返回
Result,不再回退为空查找器。 - GeoJSON 导出不再包含 protobuf 展开时保留的重复连接顶点(长度为零的线段)。查询结果不受影响。
- 随 protobuf 一并移除的还有:
Finder::from_pb、Finder::from_compressed_topo、FuzzyFinder::from_pb、pbgen模块、prost依赖和revert_timezones。
v1 系列(tzf-rs 1.3.x)仍然可用,并冻结在最后一个数据版本上:v2 产物发布后,tzf-dist 不再发布 protobuf 产物,因此获取更新后的边界数据需要迁移到 v2。
集成示例
- HTTP 服务:
racemap/rust-tz-service在 Axum Web 服务器中封装 tzf-rs。 - Redis 协议:
ringsaturn/redizone是基于 tzf-rs 构建的 Redis 兼容服务器。 - PostgreSQL:
ringsaturn/pg-tzf把 tzf-rs 作为数据库扩展提供。