## Changelog for Tz_World

## Tz_World v2.5.0

This is the changelog for Tz_World v2.5.0 released on September 24th, 2026.

### Enhancements

* `TzWorld.Backend.SpatialIndex` lookups are about 1,500 times faster and the loaded data takes 152 MB rather than 530 MB, with identical results. Shapes are compiled into packed rings with banded edge indexes, in parallel, and loading at startup is about five times faster on an 8-core machine.

* `TzWorld.reload_timezone_data/0` now waits up to two minutes for the `SpatialIndex` backend to rebuild rather than thirty seconds, so a reload on a slow machine no longer times out.

### Bug Fixes

* `TzWorld.timezone_at/2` and `TzWorld.all_timezones_at/2` return `{:error, :invalid_point}` for a malformed or out-of-range point instead of raising. An out-of-range `Geo.Point`, which previously found no time zone, returns the same error.

* Setting `TZWORLD_UNSAFE_HTTPS` to `false` or `nil` no longer turns off certificate verification for downloads. Verification is now turned off only when it is set to another value, such as `true`.

* The `:cacertfile` configured for `:tz_world` is now used to verify downloads. It was being read from the `:ex_cldr` configuration instead.

* `TzWorld.Downloader.current_release/0` reads the installed data file rather than raising when no backend is running. An invalid `TZWORLD_HTTP_TIMEOUT` or `TZWORLD_HTTP_CONNECTION_TIMEOUT` falls back to the default rather than raising.

* The README documented an `:httpc_opts` option that was never read. It now documents `:https_proxy`, which is.

* The `TzWorld.Backend` behaviour now declares `version/0`, which `TzWorld.version/0` has always called. A custom backend without it now gets a compile-time warning rather than failing at run time.

## Tz_World v2.4.0

This is the changelog for Tz_World v2.4.0 released on September 22nd, 2026.

### Breaking Changes

* `mix tz_world.update` now accepts `--backends` and defaults to `spatial_index`, so it no longer builds the `timezones-geodata.dets` cache unless `dets` or `ets` is named. Applications using `TzWorld.Backend.DetsWithIndexCache` or `TzWorld.Backend.EtsWithIndexCache` must pass `--backends dets` (or `ets`) when updating their data.

### Deprecations

* `TzWorld.Backend.DetsWithIndexCache` and `TzWorld.Backend.EtsWithIndexCache` are deprecated and will be removed in the next release. Use `TzWorld.Backend.SpatialIndex`, which reads the `.tzw1` data directly and needs no DETS cache; starting either deprecated backend now logs an info message.

## Tz_World v2.3.0

This is the changelog for Tz_World v2.3.0 released on April 29th, 2026.

### Breaking Changes

* `TzWorld.reload_timezone_data/0` now returns `{:ok, [{backend, result}, ...]}` or `{:error, [{backend, reason}, ...]}` instead of a bare list. Callers that pattern-matched the previous return shape need updating.

* Adds `:telemetry ~> 1.0` as a runtime dependency.

### Enhancements

* `mix tz_world.update` fails fast with a clean error and exit 1 when the configured `:data_dir` does not exist. Pass `--force` to create it on the fly.

* `TzWorld.reload_timezone_data/0` now actually works in every backend configuration: backends that aren't running are skipped instead of crashing the caller with `:noproc`. Reload also runs the backends in dependency-correct order (DETS before ETS).

* On-disk `.tzw1` and `.dets` files are now written atomically (write-temp + rename in the same directory). Concurrent readers in another BEAM never observe a half-written file, even while `mix tz_world.update` is running.

* Reload emits `[:tz_world, :reload, :start | :stop | :exception]` and `[:tz_world, :reload, :backend, :start | :stop | :exception]` telemetry events with `:duration` measurements and `:backend` / `:result` metadata.

* `mix tz_world.update` no longer starts the `SpatialIndex` backend internally; the version check reads the on-disk header directly. This removes the misleading "started without timezone data — run `mix tz_world.update`" warning that the update task itself used to log.

### Bug Fixes

* Fixed `TzWorld.Backend.EtsWithIndexCache` retaining stale entries across reloads. Shapes removed upstream (or whose bounding-box keys changed) now actually disappear from the cache.

* Fixed `TzWorld.Backend.SpatialIndex` reload briefly exposing an inconsistent index. The three persistent-term entries are packed into a single key so reload is an atomic swap.

* Fixed `TzWorld.Backend.DetsWithIndexCache` reload failing with `:incompatible_arguments` when another opener held a reference to the live DETS file.

## Tz_World v2.2.0

This is the changelog for Tz_World v2.2.0released on April 28th, 2026. For older changelogs please consult the release tag on [GitHub](https://github.com/kipcole9/tz_world/tags).

### Bug Fixes

* `mix tz_world.update` now produces a single clear error instead of a long `File.Error` stack trace when the configured `:data_dir` does not exist. To create the directory automatically, pass `--force`. This affects users who set `:data_dir` to a path outside the package's `priv/` (e.g. an application-level data directory shared across releases).

### Migration

* If you set `config :tz_world, data_dir: "..."` to a directory that is not part of the build artifacts, run `mix tz_world.update --force` once on first install (or any install where the directory may not yet exist). The default `:data_dir` (the package's `priv/`) always exists and is unaffected.

## Tz_World v2.1.0

This is the changelog for Tz_World v2.1.0 released on April 27th, 2026. For older changelogs please consult the release tag on [GitHub](https://github.com/kipcole9/tz_world/tags).

### Enhancements

THe primary focus in this release is peak memory used during the `mix tz_world.update` process. With the changes in this release, peak memory has been reduced from about 920Mb to about 70Mb.

* On-disk data format upgraded; existing installations must run `mix tz_world.update` once after upgrading.

* The :jason dependency has been removed in favour of the built-in :json module (with :json_polyfill on OTP < 27)."

## Tz_World v2.0.0

This is the changelog for Tz_World v2.0.0 released on April 27th, 2026. For older changelogs please consult the release tag on [GitHub](https://github.com/kipcole9/tz_world/tags).

### Enhancements

* Adds `TzWorld.Backend.SpatialIndex`, a new backend that resolves timezones using a pure-Elixir R-tree (Sort-Tile-Recursive packed) held in `:persistent_term`. Lookups bypass the GenServer mailbox and read directly from `:persistent_term`, eliminating the linear bounding-box scan used by the previous backends.

* `TzWorld.Backend.SpatialIndex` is now the default backend, and is the first entry in the default backend precedence list. Existing applications that do not pin `:default_backend` will pick it up automatically.

* Benchmarked against the previous backends across a categorised fixture set (dense regions, sparse/large zones, small/thin zones, ocean points with no match, points adjacent to the international date line):

  | Input category    | Speedup vs. the next-fastest existing backend |
  | ----------------- | --------------------------------------------- |
  | `ocean` (no-match)| 18.2×                                         |
  | `sparse_or_large` | 1.64×                                         |
  | `dense` (cities)  | 1.43×                                         |
  | random uniform    | 1.42×                                         |
  | `small_or_thin`   | 1.08×                                         |

  The largest wins are on no-match queries: previously, points in the open ocean caused a full scan of every shape's bounding box; the R-tree exits at the root.

* Adds `TzWorld.SpatialIndex`, a small standalone R-tree implementation (build + stab) used by the new backend.

* Adds `TzWorld.TimezoneFixtures`, a categorised set of coordinate fixtures used by both the test suite and the benchmark.

### Breaking Changes

* Removes `TzWorld.Backend.Memory`. It is strictly dominated by `TzWorld.Backend.SpatialIndex` on every measured workload (same memory profile, faster lookups, no GenServer round-trip).

* Removes `TzWorld.Backend.Ets`. Without an in-memory bbox index, every lookup on a no-match point performed a full ETS scan via match-spec — orders of magnitude slower than the index-cache variant or the new `SpatialIndex` backend. `TzWorld.Backend.EtsWithIndexCache` remains for users who want ETS-backed shape storage.

* Removes `TzWorld.Backend.Dets`. Same reasoning as `Backend.Ets`. `TzWorld.Backend.DetsWithIndexCache` remains for users who want disk-backed shape storage.

### Migration

* Applications that did not configure `:default_backend` need no changes — `TzWorld.Backend.SpatialIndex` is selected automatically.

* Applications that had `config :tz_world, default_backend: TzWorld.Backend.Memory` (or `Ets`, `Dets`) should switch to `TzWorld.Backend.SpatialIndex`, or to one of the remaining cache-backed alternatives (`EtsWithIndexCache`, `DetsWithIndexCache`).

* Supervision trees that explicitly start `TzWorld.Backend.Memory`, `TzWorld.Backend.Ets`, or `TzWorld.Backend.Dets` should switch to `TzWorld.Backend.SpatialIndex` (or another remaining backend).

## Tz_World v1.4.2

This is the changelog for Tz_World v1.4.2 released on January 19th, 2026.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Bug Fixes

* Fix absoluate path error in the zip file generation. For security reasons, the contents of a zip file should not have an absolute path. THis is now corrected.

* Fix compiler warning on Elixir 1.20.

## Tz_World v1.4.1

This is the changelog for Tz_World v1.4.1 released on October 21st, 2024.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Bug Fixes

* Don't include `:wx` and `:observer` in `:extra_applications` since this propogates and `:wx` may not be compiled in the target runtime. Thanks to @mayel for the report. Fixes #43.

## Tz_World v1.4.0

This is the changelog for Tz_World v1.4.0 released on September 29th, 2024.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Enhancements

* Adds support for easier configuration of default custom backends.  In previous releases, the default backend was resolved by only considering the built-in backends. From this release, a custom backend can be configured in `config.exs` or `runtime.exs`. If so configured, that backend will be the default for calls to `TzWorld.timezone_at/1`. For example:

```elixir
config :tz_world,
  default_backend: MyTzWorldBackend
```

* Adds a `--trace` flag to `mix tz_world.update`. This flag will trigger additional logging during the update process including memory utilization on the BEAM.

* Adds some memory use optimizations during the download process. Relates to #38 but likely does not fully solve this issue.

* Add support for [geo 4.0](https://github.com/felt/geo).

## Tz_World v1.3.3

This is the changelog for Tz_World v1.3.3 released on May 27th, 2024.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Bug Fixes

* Fixes compiler warnings for Elixir 1.17.

## Tz_World v1.3.2

This is the changelog for Tz_World v1.3.2 released on December 2nd 2023.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Bug Fixes

* Fixes compiler warnings for Elixir 1.16.

## Tz_World v1.3.1

This is the changelog for Tz_World v1.3.1 released on August 17th, 2023.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Bug Fixes

Thanks to @mjquinlan2000 for the report of issues on Elixir 1.15 and OTP 26.

* Always send a `User-Agent` header to the Github API to avoid 403 responses.

* Add `:ssl` to `:extra_applications` to support Elixir 1.15 and OTP 26.

* Update `TzWorld.Downloader.get_url/1` to follow the [erlef security guidelines](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/ssl).

## Tz_World v1.3.0

This is the changelog for Tz_World v1.3.0 released on April 5th, 2023.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Enhancements

* Add httpc [set_options/1](https://www.erlang.org/doc/man/httpc.html#set_options-1) support. Thanks to @gabrielgiordan for the PR (and the PR for fixing CI).

## Tz_World v1.2.0

This is the changelog for Tz_World v1.2.0 released on October 12th, 2022.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Bug Fixes

* Fix `TzWorld.Backend.Dets` to not raise an exception if there is no timezone data available.

### Enhancements

* Adds options to `mix tz_world.update` mix task:
  * `--include_oceans` will download a 10% larger geojson data set that covers the worlds oceans
  * `--force` will force a data update, even if the data is the latest release. This can be used
    to switch between data that includes oceans and that which does not.
  * Thanks to @lguminski for the feedback and suggestion.

## Tz_World v1.1.0

This is the changelog for Tz_World v1.1.0 released on August 26th, 2022.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Enhancements

* Replace `Application.get_env/2` with `Application.compile_env/2` to remove warnings on Elixir 1.14. Now requires Elixir 1.10 as a minimum version.

## Tz_World v1.0.0

This is the changelog for Tz_World v1.0.0 released on October 19th, 2021.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Enhancements

* Update to version 1.0.0 since the API has been stable for a year.

## Tz_World v0.7.1

This is the changelog for Tz_World v0.7.1 released on November 6th, 2020.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Bug Fixes

* Don't use tests for the external data version since that changes outside of the code lifesycle

## Tz_World v0.7.0

This is the changelog for Tz_World v0.7.0 released on October 10th, 2020.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Bug Fixes

* Add `:inets` and `:public_key` to `:extra_applications` in `mix.exs` to make Elixir 1.11 happy.

## Tz_World v0.6.0

This is the changelog for Tz_World v0.6.0 released on June 10th, 2020.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Bug Fixes

* Honour the configuration for `:data_dir`. Thanks to @superhawk610. Fixes #12

* Be more resilient if the `:dets` file is not in place

## Tz_World v0.5.0

This is the changelog for Tz_World v0.5.0 released on May 23rd, 2020.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Bug Fixes

* Move compile time configuration of the data directory to runtime and remove hard-coded default path

* Start `:inets` and `:ssl` applications in the downloader mix task

* Add certificate verification when downloading updates to the geo data

### Enhancements

* Document the `:data_dir` and `:cacertfile` configuration options it the README.md file

* The backends `:dets` and `:dets_with_index_cache` now open the `:dets` file as `access: :read` which prevents errors if the file is abnormally closed.

## Tz_World v0.4.0

This is the changelog for Tz_World v0.4.0 released on May 12th, 2020.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

* Adds configurable backends. Each backend is a GenServer that must be added to an applications supervision tree or started manually.

### Breaking change

* When specifying a `lng`, `lat` to `TzWorld.timezone_at/2` the coordinates must be wrapped in a tuple. For example `TzWorld.timezone_at({3.2, 45.32})` making it consistent with the `Geo.Point` and `Geo.PointZ` strategies.

### Configurable backends

* `TzWorld.Backend.Memory` which retains all data in memory for fast (but *not* fastest) performance at the expense of using approximately 1Gb of memory
* `TzWorld.Backend.Dets` which uses Erlang's `:dets` data store. This uses negligible memory at the expense of slow access times (approximaltey 500ms in testing)
* `TzWorld.Backend.DetsWithIndexCache` which balances memory usage and performance. This backend is recommended in most situations since its performance is similar to `TzWorld.Backend.Memory` (about 5% slower in testing) and uses about 25Mb of memory
* `TzWorld.Backend.Ets` which uses `:ets` for storage. With the default settings of `:compressed` for the `:ets` table its memory consumption is about 512Mb but with access that is over 20 times slower than `TzWorld.Backend.DetsWithIndexCache`
* `TzWorld.Backend.EtsWithIndexCache` which uses `:ets` for storage with an additional in-memory cache of the bounding boxes. This still uses about 512Mb but is faster than any of the other backends by about 40%

### Enhancements

* Add `TzWorld.all_timezones_at/2` to return all timezones for a given location.  In rare cases, usually disputed territory, multiple timezones may be declared for overlapping regions. `TzWorld.all_timezones_at/2` returns a (potentially empty) list of all time zones known for a given point.  *Futher testing of this function is required and will be completed before version 1.0*.

## Tz_World v0.3.0

This is the changelog for Tz_World v0.3.0 released on December 4th, 2019.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Breaking Changes

* Changes the error return from `{:error, :timezone_not_found}` to `{:error, :time_zone_not_found}` since both Elixir and Tzdata use `time_zone`.

### Enhancements

* Allows both `%Geo.Point{}` and `%Geo.PointZ{}` as parameters to `TzWorld.timezone_at/1`

## Tz_World v0.2.0

This is the changelog for Tz_World v0.2.0 released on November 28th, 2019.  For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Breaking Changes

* Requires OTP 21 and Elixir 1.6 or later due to the use of GenServer's `handle_continue/2`

* `timezone_at/1` returns tagged tuples `{:ok, result}` or `{:error, reason}`. There can be at least two reasons for an error: no data file is available or no timezone is found. These return `{:error, :enoent}` and `{:error, :timezone_not_found}` respectively

* The timezone geojson data is no longer included in the package. Run `mix tz_world.update` to install or update it.

* No longer uses Ecto or PostGIS for calculations.

### Enhancements

* Updated to latest shape data. Takes the geo JSON shape data directly from [timezone-boundary-builder releases](https://github.com/evansiroky/timezone-boundary-builder/releases)

* Conforms TzWorker to the modern `child_spec/1` including using `handle_continue/2` to load the data file if it exists.

* Updated dependencies including [geo](https://hex.pm/packages/geo) to allow `1.x`, `2.x` or `3.x`

* Added `Jason` as an optional dependency to facilitate decoding the GeoJSON from `timezone_boundary_builder`

* The timezone geojson data is no longer included in the package. Its size isn't supported on hex and it bloats the repo too. A mix task `tz_world.update` downloads and processes the data. The function `TzWorld.Downloader.update_release/0` can be called at any time to look for a new release, download it and load it into the running server with no downtime.

* `timezone_at/1` supports simple `lng`, `lat` arguments as well as `%Geo.Point{}` structs

* Added `CHANGELOG.md`

* Added SRID to the GeoJSON

* Updated package and ran dialyzer

* Added a config option :data_dir specifies the location of the compressed etf. Default is `./priv`

* Updated README, package and docs




