Skip to content

Timeline

2022

2022-05-29

Repo https://github.com/ringsaturn/tzf created.

2022-08-01

First version v0.6.0 of tzfpy released, based on Go’s CGO feature.

2022-11-06

Designed tile based index for tzf.

2022-11-20

Release first version of https://github.com/ringsaturn/tzf-rs.

2022-11-21

Replace Go binding with Rust binding via PyO3, released as0.10.0 of tzfpy.

tzfpy moved to its own repo https://github.com/ringsaturn/tzfpy.

2024

2024-04-22

Created https://github.com/ringsaturn/tzf-wasm, which is a WebAssembly version of tzf-rs.

2025

2025-02-21

Created https://github.com/ringsaturn/tzf-swift, which is a Swift version of tzf.

2025-03-24

Release v1.0.0 for tzf, tzf-rs, tzfpy, tzf-wasm, tzf-swift.

tzf-repos’ API is stable now.

2025-05-03

Created https://github.com/ringsaturn/pg-tzf, which is a PostgreSQL extension of tzf-rs.

2026

2026 Spring

Topology-aware simplification implemented in tzf v1.1.0, resolving a long-standing issue (tzf#183) where independent per-polygon RDP simplification created gaps and overlaps at shared timezone borders. The new approach detects shared edges first, simplifies them once, and substitutes the simplified boundary back into both adjacent polygons.

New data distribution repository ringsaturn/tzf-dist introduced, distributing data in the new CompressedTopoTimezones format:

FileSizeDescription
combined-with-oceans.compress.topo.bin~17 MBFull precision
combined-with-oceans.topology.compress.topo.bin~5.4 MBTopology-simplified (lite)
combined-with-oceans.topology.preindex.bin~2 MBTile preindex

The full-precision dataset shrank from ~90 MB to ~17 MB, making it viable to ship as an optional Cargo feature in tzf-rs v1.3.0 (DefaultFinder::new_full()).

YStripes spatial index (ported from tidwall/tg) becomes the default polygon-level index in tzf v1.1.0 (Go) and tzf-rs v1.2.0 (Rust). Single random-city lookup: ~1 µs on Apple M3 Max.

Releases in this wave: tzf v1.1.0, tzf-rs v1.2.0 / v1.3.0, tzfpy v1.2.0 / v1.3.0, tzf-dist v0.0.2026-a, geometry-rs v0.4.1.

Check blog post tzf Spring 2026 Update for more details.

2026-07-19

The TZF embedded binary format specification is marked Final at version 1.0 (see the format reference). It defines the TZFB container: a 64-byte header, a section table, section types 1 to 9, and a CRC32 footer, with geometry stored as chunked zigzag-LEB128 varint streams. The layout allows a reader to answer a query without decoding the whole file.

2026-08-16

Format 1.1 adds the pieces the v2 runtime needs: the profile byte at header offset 48, section type 10 (FUZZY, the tile preindex), and the M profile with section types 12 (FLATPOINTS), 13 (FLATRINGDIR) and 14 (YSTRIPES, assigned and not emitted). Mandatory sections become per-profile.

2026-08-18

The v2 API is specified: five constructors returning F, no options, no exported finder types, and x for surface outside the semantic-versioning promise. The FUZZY fast path lands in the in-place finder the same day, taking its median query on the lite file from 4.7 µs to 542 ns.

2026-08-28

Protobuf is removed from the tzf tree. The pipeline moves to native Go structs with encoding/gob intermediates, which are build-internal and never distributed. The module becomes github.com/ringsaturn/tzf/v2 in place, with nov2/ subdirectory. tzf-rs is ported to the .tzb runtime in the same window; its .tzm loader is measured, found to save about 3 ms of open time at higher memory, and removed, so tzf-rs consumes the E profile only.

tzf-dist stops publishing the protobuf artifact set and ships lite.tzb,lite.tzm and full.tzb. The v1 data line is frozen at its last protobuf release.

Measured against the protobuf path on an Apple M3 Max with the 2026c dataset:

PathOpenResidentQuery
Go, full protobuf (v1)288 ms~153 MB~290 ns
Go, full .tzb expanded78.5 ms~145 MB~300 ns
Go, lite .tzm memory image7.7 ms~12 MB heap + 10 MB read-only298 ns
Go, lite .tzb in place1.7 ms~3 MB~6 µs
Rust, lite protobuf (v1)71 ms77.8 MiB peak RSS316 ns
Rust, lite .tzb18 ms cold44.1 MiB peak RSS260 ns

2026-09

v2 releases for tzf (Go), tzf-rs (Rust) and tzfpy (Python). tzf-dist tagged the first .tzb/.tzm artifact set, v0.0.2026-c-tzb1, on 2026-09-10; tzf v2.0.0 followed the same day, and tzf-rs 2.0.0 and tzfpy 2.0.0 on 2026-09-11. The Go module path gains the /v2 suffix, tzf-rs reaches 2.0.0, and tzfpy binds tzf-rs 2.0. tzf-wasm 2.0.0 followed on 2026-09-11 on top of tzf-rs 2.0.0, and tzf-web serves it. tzf-swift 2.0.0 followed on 2026-09-12 as a protobuf-free port that reads lite.tzb directly, with DefaultFinder and a low-memoryEmbeddedFinder. The v1 lines (tzf v1.2.x, tzf-rs 1.3.x, tzfpy 1.3.x, tzf-swift 1.2.x) remain available and frozen at their last protobuf data release. The independently maintained tzf-rb builds on the v1 line at this date.

2026-09-14

tzf-dist v0.0.2026-c-tzb2: the same 2026c boundaries with the .tzb artifacts encoded at 64-point chunks instead of 256 (topo2embed -chunk 64, now the encoder default). The format is unchanged; lite.tzb is 4.18 MB andfull.tzb 15.26 MB. tzf v2.1.1 and tzf-rs 2.1.1 (each also tagged 2.1.0 with the same content) require it and rewrite the in-place query walk: validation moves to open time, chunk blocks and groups that cannot contain the point are skipped on their bounding boxes and endpoints, and the preindex probe visits only the zoom levels that carry keys; tzf-rs also builds per-group latitude stripes at open. Results are unchanged. In the 2026-09-14 tz-benchmark snapshot the Go NewEmbeddedFinder edge-city p50 went from 8,959 ns to 1,000 ns and the Rust EmbeddedFinder edge-city mean from 4,779.81 ns to 666.11 ns; the in-place finders now hold a small open-time index (about 30 KB in Go, 0.2 MiB live in Rust) instead of under 1 KB. tzfpy 2.1.0b2 is a pre-release on tzf-rs 2.1.1: lite wheels on TestPyPI and GitHub Releases, and the experimental +full wheels (tzf-rs EmbeddedFinder over full.tzb) on tzfpy’s own index; its edge-city median went from 6,250 ns to 1,167 ns. PyPI stays at tzfpy 2.0.0; tzf-wasm and tzf-swift stay at 2.0.0.

Last updated on