Packages
tz_extra
0.27.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.1
0.22.0
0.21.1
0.21.0
0.20.1
0.20.0
0.17.0
0.16.7
0.16.6
0.16.5
0.16.3
0.16.2
0.16.1
0.16.0
0.15.1
0.15.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
Time zone-related utilities
Current section
Files
Jump to
Current section
Files
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