Heretic compares a connection's measured round-trip time with a distance bound derived from its claimed timezone. Our old distance calculation combined a geographic box with a circle around a reference city. The circle's radius was supposed to enclose the zone.
It did not always do so. In the pinned polygon dataset, a point belonging to Europe/Paris lies about 1,002.94 km from the reference point. The table's radius was 900 km. Placing a synthetic sensor at that point made the old Rust calculation report a minimum distance of 102.94 km to a zone containing the sensor itself.
The box contribution was zero. The result still came out positive because the calculation selected the larger of the two bounds.
We removed the radius contribution in ruleset 14. This article follows the mathematical counterexample and the checks behind that correction. The distances below are computed from a geographic fixture; they are not measured network paths or observed live false positives.
What taking the maximum requires
Let Z be the timezone territory, e the sensor location, a the zone's reference point and r its proposed enclosing radius. If every point z in Z satisfies d(a,z) <= r, the triangle inequality gives:
d(e,z) >= d(e,a) - d(a,z) >= d(e,a) - rThat produces a lower bound on distance to the territory. Separately, if a geographic box B contains Z, the distance to the nearest point of B cannot exceed the distance to the nearest point of Z.
The old calculation combined those claims:
circle_bound = max(0, distance(sensor, reference_point) - radius)
box_bound = max(0, distance(sensor, box) - 50 km)
zone_bound = max(circle_bound, box_bound)Taking the maximum is justified only when both inputs are valid lower bounds. An oversized box can weaken its contribution without making it an overestimate. An undersized radius can make its contribution too large. Once the circle produces that overestimate, the maximum preserves it even when the box is correct.
Testing containment instead of the reference point
We pinned the comprehensive, non-ocean GeoJSON archive from timezone-boundary-builder release 2026c. Its boundaries derive from OpenStreetMap data. The selected witnesses are attributed to OpenStreetMap contributors and Timezone Boundary Builder, under ODbL 1.0. We matched the table's identifiers to its polygons and resolved missing names using IANA tzdata 2026c links. The scan covered all 254 table entries, including aliases that reuse geometry. Those entries are not 254 independent territories.
The first check asked whether any polygon vertex lay outside the proposed circle. One outside vertex is enough to disprove enclosure. A scan with no outside vertex would require further work to establish containment of the continuous geometry.
We then compiled the actual Rust distance implementation and placed a synthetic sensor at selected dataset vertices. A point in the selected zone must have zero distance to that zone. These five cases failed that requirement under the old formula:
| Zone | Stored radius | Reference point to witness | Old reported minimum at witness |
|---|---|---|---|
| Europe/Paris | 900 km | 1,002.94 km | 102.94 km |
| Europe/Moscow | 2,000 km | 3,022.93 km | 1,022.93 km |
| Europe/Warsaw | 500 km | 521.48 km | 21.48 km |
| Asia/Shanghai | 4,300 km | 4,393.77 km | 93.77 km |
| Pacific/Fiji | 600 km | 672.50 km | 72.50 km |
The Paris witness is longitude 9.630964, latitude 41.433664. It lies inside the table's box. Subtracting the stored radius from the reference-point distance explains the erroneous minimum directly: 1,002.94 - 900 = 102.94 km.
A second check kept the sensor at the Warsaw reference coordinate used by the existing tests. In 21 entries, including aliases, the old bound exceeded the distance to a known contained vertex. That also disproves a lower bound: the nearest point in a set cannot be farther away than a particular point already in the set. This check did not require us to find the exact nearest point of the polygon.
The replacement's premise
The corrected calculation uses box_bound alone. We checked the retained boxes against the selected GeoJSON representation, including wrapped longitude intervals. Every ring was closed, every vertex lay within its rectangle, and each ring required only one constant longitude shift to fit the rectangle.
RFC 7946 section 3.1.1 defines GeoJSON segments by coordinate interpolation. A rectangle is convex in those coordinates, so containing both endpoints contains their linear segment. A constant shift preserves that interpolation. For valid bounded polygon interiors, the enclosing rectangle also contains the interior. Those checks covered 950 rings across the mapped entries, including repeated alias geometry.
This establishes containment of that dataset representation. It does not establish every geographic boundary as ground truth, validate all browser-default timezone mappings, or prove that a spherical surface-distance model describes every possible propagation path. Those are separate premises. A timezone claim also does not authenticate the browser's location; a terminating proxy changes which TCP endpoint answers the handshake.
What the regression and replay established
The new Rust regression fails under the old formula and passes under the corrected one. An isolated candidate also cleared all 254 saved reference-coordinate witness checks. The full Rust test suites passed after the correction.
Replaying 1,839 historical records produced identical before-and-after results, including the geometry finding counts. That limits what we can claim about observed impact. It does not repair the invalid inequality demonstrated by the geographic counterexample. The geographic counterexample tests a premise that those historical requests did not expose.
Ruleset 14 retains the finding IDs and tiers. The legacy radius field remains descriptive metadata, while the reported lower distance uses the box contribution with its existing margin. We also changed the probe explanation and the website illustration so they describe the same calculation.
For a similar bound in another detector, test the enclosure itself. Checking that the reference city lies inside a box, or that familiar routes produce expected results, does not test whether an entire claimed region fits inside every container used by a maximum.
Reproduce the contained-point failure
The Python reproduction package contains the five witnesses above and a Berlin control. It uses only the standard library:
python3 reproduce.pyThis is an independent implementation of the arithmetic, separate from the internal Rust regression. It first checks that each point lies inside its stored rectangle, then computes the circle contribution. In this restricted case the box distance is exactly zero:
circle = max(0, reference_to_witness_km - radius_km)
old_minimum = max(circle, 0)
corrected_minimum = 0All five old minima match the table. Berlin gives zero under both formulas. A zero result for that one Berlin point does not establish enclosure of the entire zone.
To check the coordinates against the actual features, download the pinned 2026c comprehensive non-ocean archive and pass its path:
python3 reproduce.py /path/to/timezones.geojson.zipThe script verifies the archive hash and each witness's membership in the named feature's vertex list. The package records the resulting output and includes separate code and data licenses. It covers the six contained-point examples; the full 254-entry scan and historical traffic replay described above were internal checks.