Current section

Files

Jump to
tz_extra lib compiler.ex
Raw

lib/compiler.ex

defmodule TzExtra.Compiler do
@moduledoc false
require TzExtra.IanaFileParser
import TzExtra.Helper
alias TzExtra.IanaFileParser
def compile() do
countries = IanaFileParser.countries()
time_zones = IanaFileParser.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()
link_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)
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}
)
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()
quoted = [
for time_zone <- all_time_zones do
canonical_time_zone = link_canonical_map[time_zone]
countries_time_zones =
Enum.filter(countries_time_zones, &(&1.time_zone == canonical_time_zone))
if length(countries_time_zones) > 0 do
quote do
def for_time_zone(unquote(time_zone)) do
{:ok, unquote(Macro.escape(countries_time_zones))}
end
end
else
quote do
def for_time_zone(unquote(time_zone)) do
{:error, :time_zone_not_linked_to_country}
end
end
end
end,
quote do
def for_time_zone(_) do
{:error, :time_zone_not_found}
end
end,
for %{code: country_code} <- countries do
countries_time_zones =
Enum.filter(countries_time_zones, &(&1.country.code == country_code))
country_code_atom = String.to_atom(country_code)
quote do
def for_country_code(unquote(country_code)) do
{:ok, unquote(Macro.escape(countries_time_zones))}
end
def for_country_code(unquote(country_code_atom)) do
{:ok, unquote(Macro.escape(countries_time_zones))}
end
end
end,
quote do
def for_country_code(_) do
{:error, :country_not_found}
end
end
]
module = :"Elixir.TzExtra.CountryTimeZone"
Module.create(module, quoted, Macro.Env.location(__ENV__))
:code.purge(module)
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(link_canonical_map))[time_zone_identifier] ||
raise "time zone identifier \"#{time_zone_identifier}\" not found"
end
def civil_time_zone_identifiers(opts \\ []) do
include_aliases = Keyword.get(opts, :include_aliases, false)
if include_aliases do
unquote(Macro.escape(civil_time_zones_with_links))
else
unquote(Macro.escape(civil_time_zones))
end
end
def time_zone_identifiers(opts \\ []) do
include_aliases = Keyword.get(opts, :include_aliases, false)
if include_aliases do
unquote(Macro.escape(all_time_zones))
else
unquote(Macro.escape(canonical_time_zones))
end
end
def countries_time_zones() do
unquote(Macro.escape(countries_time_zones))
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