コンテンツにスキップ

Rust (tzf-rs)

インストール

cargo add tzf-rs

tzf-rs 2.0 は protobuf を使用しません。ringsaturn/tzf-dist が公開する TZF 埋め込みバイナリ形式(.tzb)を読み込みます。デフォルトの bundled feature は lite ファイル(約 4 MB)を crate 内に含みます。形式そのものについては埋め込みバイナリ形式を参照してください。

2 つの Finder

データピーク RSSクエリ(ランダム都市 / 境界都市)
DefaultFinderlite .tzb をポリゴンに展開、FUZZY 高速パス約 47 MiB221 ns / 475 ns
EmbeddedFinderlite .tzb をインプレースで参照約 10 MiB293 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 のクエリ走査を書き直し(オープン時の検証、チャンクブロックと端点パリティによるスキップ、グループごとの緯度ストライプ、キーを持つズームレベルに限定したプレインデックス探索)、64 点チャンクのデータに移行しました。結果は変わらず、境界都市の値は 2.0.0 では 4.78 µs でした。

DefaultFinder::new() のオープン時間は約 13 ms、EmbeddedFinder::new() は約 2 ms です。カウント機能付きアロケータは EmbeddedFinder のヒープ保持量を 0.2 MiB と報告します。データは &'static の埋め込みスライスで、それに加えてオープン時に構築するチャンクのスキップブロックと緯度ストライプのインデックス(lite で約 100 KB)を保持するためです。EmbeddedFinder が該当するのはメモリ制約のある環境で、境界都市の検索時間は DefaultFinder の約 1.4 倍です。その他のケースについては Finder の選択を参照してください。

Finder の再利用

構築時にファイル全体を読み込んで検証するため、プロセスは 1 つの Finder を構築して再利用します。LazyLock の static は、依存関係を追加せずにこれを保持します。

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 も同じ 4 つのメソッドを公開します。

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 タイルプレインデックスが大部分のクエリを point-in-polygon なしで解決します。
  • get_tz_names はどちらの Finder でもポリゴンによる厳密な判定を行い、プレインデックスを参照しません。地点が複数のタイムゾーンに属する可能性がある場合に該当します。共有境界上の地点は、接するすべてのポリゴンに属します。

独自のバイト列を使用する

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_tzbimpl Into<Cow<'static, [u8]>> を受け取ります。include_bytes! による &'static [u8] はコピーせずにそのまま使用され、所有権を持つ Vec<u8> も受け付けます。どちらのコンストラクタも Result<_, tzf_rs::Error> を返します。ファイルはオープン時に CRC と構造の検証を受けるため、不正なデータは空の Finder ではなくエラーとして現れます。

Error#[non_exhaustive] で、MalformedProfileNoFuzzyIndex のバリアントを持ちます。.tzm メモリイメージのバイト列は Profile を返します。tzf-rs が読み込むのは .tzb プロファイルのみで、M プロファイルは Go の実装が読み込みます。

Cargo feature

Featureデフォルト効果
bundled有効tzf-dist の lite .tzb(約 4 MB)を埋め込み、new() を有効化します
clap有効tzf CLI バイナリをビルドします
full無効git 限定の完全精度 .tzb(約 14 MB)。new_full() を有効化します
export-geojson無効GeoJSON エクスポートのメソッド

fullbundled と排他です。両方を有効にすると 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));

ライブラリとしてのビルドで CLI バイナリを外す場合は cargo build --no-default-features --features bundled を使用します。

GeoJSON エクスポート

export-geojson を有効にすると、どちらの Finder も 4 つのエクスポート関数を公開します。

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 が point-in-polygon にフォールバックせずプレインデックスから応答する領域です。FUZZY セクションを持たないファイルでは None を返します。

v1 からの移行

v1v2
DefaultFinder::new()DefaultFinder::new() — 呼び出し箇所の変更は不要
DefaultFinder::new_full()DefaultFinder::new_full() — 呼び出し箇所の変更は不要
Finder(ポリゴンのみ)DefaultFinderget_tz_names はポリゴンによる厳密な判定を維持します
FuzzyFinder(タイルのみ)削除。プレインデックスはすべての Finder 内部の高速パスになりました
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 のタイル境界 GeoJSONget_tz_preindex_geojson / to_preindex_geojson に置き換え
追加:インプレースで低メモリの EmbeddedFinder

動作の変更点は次の通りです。

  • get_tz_names の結果が辞書順にソートされるようになりました。
  • DefaultFinderget_tz_name はカバーするプレインデックスタイルから応答します。これは v1 の DefaultFinder の動作と一致します。v1 の Finder でポリゴンによる厳密な複数結果を取得していたコードは get_tz_names を呼び出します。
  • バイト列を受け取るコンストラクタは、空の Finder へのフォールバックではなく Result を返します。
  • GeoJSON のエクスポートは、protobuf の展開時に保持されていた接合点の重複頂点(長さ 0 のセグメント)を含みません。クエリ結果に影響はありません。
  • protobuf とともに削除されたもの:Finder::from_pbFinder::from_compressed_topoFuzzyFinder::from_pbpbgen モジュール、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 をデータベース拡張として公開します。
最終更新日