Current section

Files

Jump to
tz lib update_periodically.ex
Raw

lib/update_periodically.ex

defmodule Tz.UpdatePeriodically do
use GenServer
require Logger
alias Tz.Compiler
alias Tz.PeriodsProvider
alias Tz.HTTP.HTTPClient
alias Tz.HTTP.HTTPResponse
def start_link(_) do
unless Code.ensure_loaded?(Mint.HTTP) do
raise "Add mint to mix.exs to enable automatic time zone data updates"
end
GenServer.start_link(__MODULE__, %{})
end
def init(state) do
schedule_work()
{:ok, state}
end
def handle_info(:work, state) do
Logger.debug("Tz is checking for IANA time zone database updates")
if maybe_update_tz_database() == :updated do
Logger.info("Tz is recompiling time zone periods...")
Code.compiler_options(ignore_module_conflict: true)
Compiler.compile()
Code.compiler_options(ignore_module_conflict: false)
Logger.info("Tz compilation done")
end
schedule_work()
{:noreply, state}
end
defp schedule_work() do
Process.send_after(self(), :work, 24 * 60 * 60 * 1000) # In 24 hours
end
defp maybe_update_tz_database() do
case fetch_iana_tz_version() do
{:ok, latest_version} ->
if latest_version != PeriodsProvider.version() do
case update_tz_database(latest_version) do
:ok ->
delete_tz_database(PeriodsProvider.version())
:updated
_ -> :error
end
end
:error ->
Logger.error("Tz failed to read the latest version of the IANA time zone database")
:no_update
end
end
defp fetch_iana_tz_version() do
case HTTPClient.request("GET", "/time-zones/tzdb/version", hostname: "data.iana.org") do
%HTTPResponse{body: body, status_code: 200} ->
{:ok, body |> List.first() |> String.trim()}
_ ->
:error
end
end
defp update_tz_database(version) do
case download_tz_database(version) do
{:ok, content} ->
extract_tz_database(version, content)
:ok
:error ->
Logger.error("Tz failed to download the latest archived IANA time zone database (version #{version})")
:error
end
end
defp download_tz_database(version) do
Logger.info("Tz is downloading the latest IANA time zone database (version #{version})...")
case HTTPClient.request("GET", "/time-zones/releases/tzdata#{version}.tar.gz", hostname: "data.iana.org") do
%HTTPResponse{body: body, status_code: 200} ->
Logger.info("Tz download done")
{:ok, body}
_ ->
:error
end
end
defp extract_tz_database(version, content) do
tmp_archive_path = Path.join(:code.priv_dir(:tz), "tzdata#{version}.tar.gz")
tz_data_dir = "tzdata#{version}"
:ok = File.write!(tmp_archive_path, content)
:ok = :erl_tar.extract(tmp_archive_path, [:compressed, {:cwd, Path.join(:code.priv_dir(:tz), tz_data_dir)}])
:ok = File.rm!(tmp_archive_path)
end
defp delete_tz_database(version) do
Path.join(:code.priv_dir(:tz), "tzdata#{version}")
|> File.rm_rf!()
end
end