defmodule TzExtra.Compiler do
@moduledoc false
require TzExtra.IanaFileParser
import TzExtra.Helper
alias TzExtra.IanaFileParser
@utc_time_zone_id "Etc/UTC"
def compile() do
countries = IanaFileParser.countries()
time_zones = IanaFileParser.time_zones()
get_time_zone_links_for_canonical_fun =
fn canonical ->
time_zones[canonical]
end
countries_time_zones =
IanaFileParser.time_zones_with_country(countries)
|> add_time_zone_links(get_time_zone_links_for_canonical_fun)
|> add_offset_data()
|> Enum.sort_by(&{&1.country && normalize_string(&1.country.name), &1.utc_offset, &1.time_zone})
[utc_data] = add_offset_data([%{coordinates: nil, country: nil, time_zone: @utc_time_zone_id, time_zone_links: []}])
countries_time_zones_with_utc = [utc_data | countries_time_zones]
canonical_time_zones =
time_zones
|> Map.keys()
|> Enum.uniq()
|> Enum.sort()
all_time_zones =
time_zones
|> Enum.map(fn {canonical, links} -> [canonical | links] end)
|> List.flatten()
|> Enum.uniq()
|> Enum.sort()
|> Enum.sort()
civil_time_zones =
countries_time_zones
|> Enum.map(&(&1.time_zone))
|> Enum.uniq()
|> Enum.sort()
civil_time_zones_with_links =
countries_time_zones
|> Enum.map(&([&1.time_zone | &1.time_zone_links]))
|> List.flatten()
|> Enum.uniq()
|> Enum.sort()
alias_canonical_map =
time_zones
|> Enum.reduce(%{}, fn {canonical, links}, map ->
Enum.reduce(links, map, fn link, map ->
Map.put(map, link, canonical)
end)
|> Map.put(canonical, canonical)
end)
contents = [
quote do
def iana_version() do
unquote(Tz.iana_version())
end
def get_canonical_time_zone_identifier(time_zone_identifier) do
unquote(Macro.escape(alias_canonical_map))[time_zone_identifier] ||
raise "time zone identifier \"#{time_zone_identifier}\" not found"
end
def civil_time_zone_identifiers(opts \\ []) do
prepend_utc = Keyword.get(opts, :prepend_utc, false)
include_alias = Keyword.get(opts, :include_alias, false)
time_zones =
if include_alias do
unquote(Macro.escape(civil_time_zones_with_links))
else
unquote(Macro.escape(civil_time_zones))
end
if prepend_utc do
[unquote(@utc_time_zone_id) | time_zones]
else
time_zones
end
end
def time_zone_identifiers(opts \\ []) do
include_alias = Keyword.get(opts, :include_alias, false)
if include_alias do
unquote(Macro.escape(all_time_zones))
else
unquote(Macro.escape(canonical_time_zones))
end
end
def countries_time_zones(opts \\ []) do
prepend_utc = Keyword.get(opts, :prepend_utc, false)
if prepend_utc do
unquote(Macro.escape(countries_time_zones_with_utc))
else
unquote(Macro.escape(countries_time_zones))
end
end
def countries() do
unquote(Macro.escape(countries))
end
end
]
module = :"Elixir.TzExtra"
Module.create(module, contents, Macro.Env.location(__ENV__))
:code.purge(module)
end
# defp add_offset_data(time_zones) do
# %{
# coordinates: nil,
# country: nil,
# dst_offset: 0,
# dst_zone_abbr: "UTC",
# pretty_dst_offset: "+00:00",
# pretty_utc_offset: "+00:00",
# time_zone: "UTC",
# time_zone_links: [],
# utc_offset: 0,
# zone_abbr: "UTC"
# }
# end
defp add_offset_data(time_zones) do
Enum.map(time_zones, fn %{time_zone: time_zone_id} = time_zone ->
{:ok, periods} = Tz.PeriodsProvider.periods(time_zone_id)
{utc_offset, dst_offset, zone_abbr, dst_zone_abbr} =
case hd(periods) do
{_, {utc_offset, std_offset, zone_abbr}, _, nil} ->
{utc_offset, utc_offset + std_offset, zone_abbr, zone_abbr}
{_, {utc_offset, std_offset, zone_abbr}, {_, prev_std_offset, prev_zone_abbr}, _} ->
dst_offset = utc_offset + max(std_offset, prev_std_offset)
{zone_abbr, dst_zone_abbr} =
cond do
std_offset < prev_std_offset ->
{zone_abbr, prev_zone_abbr}
std_offset > prev_std_offset ->
{prev_zone_abbr, zone_abbr}
end
{utc_offset, dst_offset, zone_abbr, dst_zone_abbr}
end
time_zone
|> Map.put(:utc_offset, utc_offset)
|> Map.put(:dst_offset, dst_offset)
|> Map.put(:pretty_utc_offset, offset_to_string(utc_offset))
|> Map.put(:pretty_dst_offset, offset_to_string(dst_offset))
|> Map.put(:zone_abbr, zone_abbr)
|> Map.put(:dst_zone_abbr, dst_zone_abbr)
end)
end
defp add_time_zone_links(countries_time_zones, get_time_zone_links_for_canonical_fun) do
Enum.map(countries_time_zones, fn %{time_zone: time_zone_id} = time_zone ->
Map.put(time_zone, :time_zone_links, get_time_zone_links_for_canonical_fun.(time_zone_id))
end)
end
end