Packages
tz_extra
0.45.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
time_zones = IanaFileParser.time_zones()
canonical_time_zones =
time_zones
|> Map.keys()
|> Enum.uniq()
|> Enum.sort()
countries =
IanaFileParser.countries()
|> localize_country_name()
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] |> Enum.sort()
end
countries_time_zones =
IanaFileParser.time_zones_with_country(countries)
|> add_time_zone_links(get_time_zone_links_for_canonical_fun)
|> add_offset_data()
|> add_location_name()
|> add_id()
|> Enum.sort_by(
&{&1.country && normalize_string(&1.country.name), &1.utc_to_std_offset, &1.time_zone_id}
)
{countries, countries_time_zones} =
add_canonical_time_zone_ids(countries, countries_time_zones)
civil_time_zones =
countries_time_zones
|> Enum.map(& &1.time_zone_id)
|> Enum.uniq()
|> Enum.sort()
civil_time_zones_with_links =
countries_time_zones
|> Enum.map(&[&1.time_zone_id | &1.time_zone_alias_ids])
|> List.flatten()
|> Enum.uniq()
|> Enum.sort()
quoted = [
for canonical_time_zone <- canonical_time_zones do
filtered_countries_time_zones =
Enum.filter(countries_time_zones, &(&1.time_zone_id == canonical_time_zone))
if length(filtered_countries_time_zones) > 0 do
quote do
def by_time_zone(unquote(canonical_time_zone)) do
{:ok, unquote(Macro.escape(filtered_countries_time_zones))}
end
end
else
quote do
def by_time_zone(unquote(canonical_time_zone)) do
{:error, :time_zone_not_linked_to_country}
end
end
end
end,
quote do
def by_time_zone(link_time_zone) do
if canonical_time_zone = unquote(Macro.escape(link_canonical_map))[link_time_zone] do
countries_time_zones_for_link =
Enum.filter(
unquote(Macro.escape(countries_time_zones)),
&(&1.time_zone_id == canonical_time_zone)
)
if countries_time_zones_for_link do
{:ok, countries_time_zones_for_link}
else
{:error, :time_zone_not_linked_to_country}
end
else
{:error, :time_zone_not_found}
end
end
end,
for %{code: country_code} <- countries do
filtered_countries_time_zones =
Enum.filter(countries_time_zones, &(&1.country.code == country_code))
country_code_atom = String.to_atom(country_code)
quote do
def by_country_code(unquote(country_code)) do
{:ok, unquote(Macro.escape(filtered_countries_time_zones))}
end
def by_country_code(unquote(country_code_atom)) do
by_country_code(unquote(country_code))
end
end
end,
quote do
def by_country_code(_) do
{:error, :country_not_found}
end
end,
for %{id: id} = country_time_zone <- countries_time_zones do
quote do
def by_id(unquote(id)) do
{:ok, unquote(Macro.escape(country_time_zone))}
end
end
end,
quote do
def by_id(_) do
{:error, :country_time_zone_id_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 utc_time_zone_id(), do: "Etc/UTC"
def canonical_time_zone_id(time_zone_id) do
if time_zone_id = unquote(Macro.escape(link_canonical_map))[time_zone_id] do
{:ok, time_zone_id}
else
{:error, :time_zone_not_found}
end
end
def canonical_time_zone_id!(time_zone_id) do
case canonical_time_zone_id(time_zone_id) do
{:ok, time_zone_id} ->
time_zone_id
{:error, :time_zone_not_found} ->
raise "time zone identifier \"#{time_zone_id}\" not found"
end
end
def civil_time_zone_ids(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_ids(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 time_zone_id_exists?(time_zone_id) do
time_zone_ids(include_aliases: true)
|> Enum.any?(&(&1 == time_zone_id))
end
def country_code_exists?(country_code) do
countries()
|> Enum.any?(&(&1.code == country_code))
end
def round_datetime(%DateTime{} = datetime, step_in_seconds, mode)
when mode in [:floor, :ceil] do
utc_datetime = DateTime.to_unix(datetime, :second)
rounded_unix_time =
case mode do
:floor ->
div(utc_datetime, step_in_seconds) * step_in_seconds
:ceil ->
div(utc_datetime + step_in_seconds - 1, step_in_seconds) * step_in_seconds
end
rounded_datetime = DateTime.from_unix!(rounded_unix_time)
DateTime.shift_zone!(rounded_datetime, datetime.time_zone)
end
def new_resolved_datetime!(%Date{} = date, %Time{} = time, time_zone, opts) do
ambiguous = Keyword.fetch!(opts, :ambiguous)
gap = Keyword.fetch!(opts, :gap)
case DateTime.new(date, time, time_zone, Tz.TimeZoneDatabase) do
{:ok, dt} ->
dt
{:error, :time_zone_not_found} ->
raise "time zone not found"
{:ambiguous, first, second} ->
case ambiguous do
:first -> first
:second -> second
end
{:gap, just_before, just_after} ->
case gap do
:just_before -> just_before
:just_after -> just_after
end
end
end
def countries_time_zones(country_or_time_zone) do
country_or_time_zone = to_string(country_or_time_zone)
if String.length(country_or_time_zone) == 2 do
:"Elixir.TzExtra.CountryTimeZone".by_country_code(country_or_time_zone)
else
:"Elixir.TzExtra.CountryTimeZone".by_time_zone(country_or_time_zone)
end
end
def countries_time_zones!(country_or_time_zone) do
case countries_time_zones(country_or_time_zone) do
{:ok, countries_time_zones} ->
countries_time_zones
{:error, _} ->
raise "country time zone not found"
end
end
def country_time_zone(id) do
:"Elixir.TzExtra.CountryTimeZone".by_id(id)
end
def country_time_zone!(id) do
case country_time_zone(id) do
{:ok, country_time_zone} ->
country_time_zone
{:error, _} ->
raise "country time zone not found"
end
end
def countries_time_zones() do
unquote(Macro.escape(countries_time_zones))
end
def countries() do
unquote(Macro.escape(countries))
end
def utc_datetime_range(%DateTime{} = start_dt, %DateTime{} = end_dt, step_in_seconds) do
start_unix_time = DateTime.to_unix(start_dt, :second)
end_unix_time = DateTime.to_unix(end_dt, :second)
Enum.map(start_unix_time..end_unix_time//step_in_seconds, &DateTime.from_unix!(&1))
end
def shifts_clock?(time_zone_id) when time_zone_id != nil do
case Tz.PeriodsProvider.periods(time_zone_id) do
{:error, :time_zone_not_found} ->
raise "invalid time zone #{time_zone_id}"
{:ok, [{utc_secs, _, _, nil} | _]} ->
hardcoded_dst_future_periods? =
DateTime.from_gregorian_seconds(utc_secs).year >
Tz.PeriodsProvider.compiled_at().year + 20
hardcoded_dst_future_periods?
{:ok, [{_, _, _, _} | _]} ->
true
end
end
def utc_offset_id(%DateTime{} = datetime, mode \\ :standard)
when mode in [:standard, :pretty] do
"UTC" <> offset_to_string(datetime.utc_offset + datetime.std_offset, mode)
end
def next_clock_shift_in_year_span(%DateTime{} = datetime) do
case Tz.PeriodsProvider.next_period(datetime) do
{from, _, _, _} ->
first_datetime_in_next_period =
DateTime.from_gregorian_seconds(from)
|> DateTime.shift_zone!(datetime.time_zone, Tz.TimeZoneDatabase)
year_difference = DateTime.diff(first_datetime_in_next_period, datetime, :day) / 365
if year_difference > 1 do
:no_shift
else
clock_shift = clock_shift(datetime, first_datetime_in_next_period)
offset = datetime.utc_offset + datetime.std_offset
next_offset =
first_datetime_in_next_period.utc_offset +
first_datetime_in_next_period.std_offset
{
clock_shift,
first_datetime_in_next_period,
div(next_offset - offset, 60)
}
end
nil ->
:no_shift
end
end
def clock_shift(datetime1, datetime2) do
if DateTime.compare(datetime1, datetime2) == :gt do
raise "first datetime must be earlier than or equal to second datetime"
end
offset1 = datetime1.utc_offset + datetime1.std_offset
offset2 = datetime2.utc_offset + datetime2.std_offset
cond do
offset1 < offset2 -> :forward
offset1 > offset2 -> :backward
offset1 == offset2 -> :no_shift
end
end
end,
for %{code: country_code} <- countries do
{:ok, time_zones_for_country} =
:"Elixir.TzExtra.CountryTimeZone".by_country_code(country_code)
country_code_atom = String.to_atom(country_code)
quote do
def country_time_zone(unquote(country_code), time_zone_id) do
case canonical_time_zone_id(time_zone_id) do
{:error, _} = error ->
error
{:ok, canonical_time_zone_id} ->
country_time_zone =
Enum.find(
unquote(Macro.escape(time_zones_for_country)),
&(&1.time_zone_id == canonical_time_zone_id)
)
if country_time_zone do
{:ok, country_time_zone}
else
{:error, :time_zone_not_found_for_country}
end
end
end
def country_time_zone(unquote(country_code_atom), time_zone_id) do
country_time_zone(unquote(country_code), time_zone_id)
end
end
end,
quote do
def country_time_zone(_, _) do
{:error, :country_not_found}
end
def country_time_zone!(country_code, time_zone_id) do
case country_time_zone(country_code, time_zone_id) do
{:ok, country_time_zone} ->
country_time_zone
{:error, _} ->
raise "no time zone data found for country #{country_code} and time zone ID #{time_zone_id}"
end
end
end
]
module = :"Elixir.TzExtra"
Module.create(module, contents, Macro.Env.location(__ENV__))
:code.purge(module)
end
defp add_offset_data(time_zones) do
Enum.map(time_zones, fn %{time_zone_id: time_zone_id} = time_zone ->
{:ok, periods} = Tz.PeriodsProvider.periods(time_zone_id)
{utc_to_std_offset, utc_to_dst_offset, time_zone_abbr, dst_time_zone_abbr} =
case hd(periods) do
{utc_secs, {utc_to_std_offset, std_offset, time_zone_abbr},
{_, prev_std_offset, prev_zone_abbr}, nil} ->
hardcoded_dst_future_periods? =
DateTime.from_gregorian_seconds(utc_secs).year > Date.utc_today().year + 20
if hardcoded_dst_future_periods? do
utc_to_dst_offset = utc_to_std_offset + max(std_offset, prev_std_offset)
utc_to_std_offset = utc_to_std_offset + min(std_offset, prev_std_offset)
{time_zone_abbr, dst_time_zone_abbr} =
cond do
std_offset < prev_std_offset ->
{time_zone_abbr, prev_zone_abbr}
std_offset > prev_std_offset ->
{prev_zone_abbr, time_zone_abbr}
end
{utc_to_std_offset, utc_to_dst_offset, time_zone_abbr, dst_time_zone_abbr}
else
{utc_to_std_offset, utc_to_std_offset + std_offset, time_zone_abbr, time_zone_abbr}
end
{_, {utc_to_std_offset, std_offset, time_zone_abbr},
{_, prev_std_offset, prev_zone_abbr}, _} ->
utc_to_dst_offset = utc_to_std_offset + max(std_offset, prev_std_offset)
utc_to_std_offset = utc_to_std_offset + min(std_offset, prev_std_offset)
{time_zone_abbr, dst_time_zone_abbr} =
cond do
std_offset < prev_std_offset ->
{time_zone_abbr, prev_zone_abbr}
std_offset > prev_std_offset ->
{prev_zone_abbr, time_zone_abbr}
end
{utc_to_std_offset, utc_to_dst_offset, time_zone_abbr, dst_time_zone_abbr}
end
time_zone
|> Map.put(:utc_to_std_offset, utc_to_std_offset)
|> Map.put(:utc_to_dst_offset, utc_to_dst_offset)
|> Map.put(:utc_to_std_offset_id, "UTC" <> offset_to_string(utc_to_std_offset, :standard))
|> Map.put(:utc_to_dst_offset_id, "UTC" <> offset_to_string(utc_to_dst_offset, :standard))
|> Map.put(
:pretty_utc_to_std_offset_id,
"UTC" <> offset_to_string(utc_to_std_offset, :pretty)
)
|> Map.put(
:pretty_utc_to_dst_offset_id,
"UTC" <> offset_to_string(utc_to_dst_offset, :pretty)
)
|> Map.put(:time_zone_abbr, time_zone_abbr)
|> Map.put(:dst_time_zone_abbr, dst_time_zone_abbr)
end)
end
defp add_id(countries_time_zones) do
countries_time_zones
|> Enum.map(fn country_time_zone ->
id = country_time_zone.time_zone_id <> "_" <> country_time_zone.country.code
slugified_country_name = slugify(country_time_zone.country.name)
slugify_city_or_region = fn time_zone_id ->
time_zone_id
|> String.split("/")
|> List.last()
|> slugify()
end
slugified_time_zone_id = slugify_city_or_region.(country_time_zone.time_zone_id)
country_has_multiple_canonical_time_zones? =
Enum.count(countries_time_zones, &(&1.country.code == country_time_zone.country.code)) > 1
slug = slugified_country_name <> "-" <> slugified_time_zone_id
short_slug =
if country_has_multiple_canonical_time_zones? do
slug
else
slugified_country_name
end
title =
country_time_zone.country.name <> ", " <> country_time_zone.pretty_time_zone_location
short_title =
if country_has_multiple_canonical_time_zones? do
title
else
country_time_zone.country.name
end
country_time_zone
|> Map.put(:id, id)
|> Map.put(:short_slug, short_slug)
|> Map.put(:slug, slug)
|> Map.put(:short_title, short_title)
|> Map.put(:title, title)
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_id: time_zone_id} = time_zone ->
Map.put(
time_zone,
:time_zone_alias_ids,
get_time_zone_links_for_canonical_fun.(time_zone_id)
)
end)
end
defp add_location_name(countries_time_zones) do
Enum.map(countries_time_zones, fn %{time_zone_id: time_zone_id} = time_zone ->
pretty_time_zone_location =
time_zone_id
|> String.split("/")
|> List.last()
|> String.split("_")
|> Enum.map(&String.capitalize/1)
|> Enum.join(" ")
Map.put(time_zone, :pretty_time_zone_location, pretty_time_zone_location)
end)
end
defp localize_country_name(countries) do
Enum.map(countries, fn country ->
local_names = local_country_names(country.code)
search_text = country.code <> country.name <> Enum.join(local_names)
search_text = String.downcase(search_text)
country
|> Map.put(:local_names, local_names)
|> Map.put(:search_text, search_text)
|> Map.put(:slug, slugify(country.name))
end)
end
defp add_canonical_time_zone_ids(countries, countries_time_zones) do
countries =
Enum.map(countries, fn country ->
time_zone_ids =
countries_time_zones
|> Enum.filter(&(&1.country.code == country.code))
|> Enum.map(& &1.time_zone_id)
Map.put(country, :canonical_time_zone_ids, time_zone_ids)
end)
countries_time_zones =
Enum.map(countries_time_zones, fn country_time_zone ->
country = Enum.find(countries, &(&1.code == country_time_zone.country.code))
Map.put(country_time_zone, :country, country)
end)
{countries, countries_time_zones}
end
defp slugify(string) do
string
|> String.normalize(:nfd)
|> String.downcase()
|> String.replace("_", "-")
|> String.replace("'", "-")
|> String.replace("&", "-and-")
|> String.replace(~r/[^a-z\s-]/u, "")
|> String.replace(~r/\s+/, "-")
|> String.replace(~r/-+/, "-")
|> String.trim("-")
end
defp local_country_names(country_code) do
local_countries_names =
%{
"AF" => ["افغانستان"],
"AX" => ["Åland"],
"AL" => ["Shqipëri"],
"DZ" => ["الجزائر"],
"AD" => ["Andorra"],
"AO" => ["Angola"],
"AI" => ["Anguilla"],
"AQ" => ["Antarctica"],
"AG" => ["Antigua & Barbuda"],
"AR" => ["Argentina"],
"AM" => ["Հայաստան"],
"AW" => ["Aruba"],
"AU" => ["Australia"],
"AT" => ["Österreich"],
"AZ" => ["Azərbaycan"],
"BS" => ["Bahamas"],
"BH" => ["البحرين"],
"BD" => ["বাংলাদেশ"],
"BB" => ["Barbados"],
"BY" => ["Беларусь"],
"BE" => ["België", "Belgique"],
"BZ" => ["Belize"],
"BJ" => ["Bénin"],
"BM" => ["Bermuda"],
"BT" => ["འབྲུག"],
"BO" => ["Bolivia", "Buliwya", "Wuliwya"],
"BA" => ["Bosna i Hercegovina"],
"BW" => ["Botswana"],
"BV" => ["Bouvetøya"],
"BR" => ["Brasil"],
"GB" => ["United Kingdom"],
"IO" => ["British Indian Ocean Territory"],
"BN" => ["Brunei"],
"BG" => ["България"],
"BF" => ["Burkina Faso"],
"BI" => ["Uburundi"],
"KH" => ["កម្ពុជា"],
"CM" => ["Cameroun", "Cameroon"],
"CA" => ["Canada"],
"CV" => ["Cabo Verde"],
"BQ" => ["Caribisch Nederland"],
"KY" => ["Cayman Islands"],
"CF" => ["Ködörösêse tî Bêafrîka"],
"TD" => ["Tchad", "تشاد"],
"CL" => ["Chile"],
"CN" => ["中国"],
"CX" => ["Christmas Island"],
"CC" => ["Cocos (Keeling) Islands"],
"CO" => ["Colombia"],
"KM" => ["Komori", "جزر القمر", "Comores"],
"CD" => ["République démocratique du Congo"],
"CG" => ["République du Congo"],
"CK" => ["Cook Islands"],
"CR" => ["Costa Rica"],
"CI" => ["Côte d'Ivoire"],
"HR" => ["Hrvatska"],
"CU" => ["Cuba"],
"CW" => ["Curaçao"],
"CY" => ["Κύπρος", "Kıbrıs"],
"CZ" => ["Česká republika"],
"DK" => ["Danmark"],
"DJ" => ["جيبوتي", "Djibouti"],
"DM" => ["Dominica"],
"DO" => ["República Dominicana"],
"TL" => ["Timor Lorosa'e"],
"EC" => ["Ecuador"],
"EG" => ["مصر"],
"SV" => ["El Salvador"],
"GQ" => ["Guinea Ecuatorial"],
"ER" => ["ኤርትራ", "إرتريا"],
"EE" => ["Eesti"],
"SZ" => ["Eswatini"],
"ET" => ["ኢትዮጵያ"],
"FK" => ["Falkland Islands"],
"FO" => ["Føroyar"],
"FJ" => ["Fiji"],
"FI" => ["Suomi"],
"FR" => ["France"],
"GF" => ["Guyane"],
"PF" => ["Polynésie française"],
"TF" => ["Terres australes et antarctiques françaises"],
"GA" => ["Gabon"],
"GM" => ["Gambia"],
"GE" => ["საქართველო"],
"DE" => ["Deutschland"],
"GH" => ["Ghana"],
"GI" => ["Gibraltar"],
"GR" => ["Ελλάδα"],
"GL" => ["Kalaallit Nunaat"],
"GD" => ["Grenada"],
"GP" => ["Guadeloupe"],
"GU" => ["Guåhån"],
"GT" => ["Guatemala"],
"GG" => ["Guernsey"],
"GN" => ["Guinée"],
"GW" => ["Guiné-Bissau"],
"GY" => ["Guyana"],
"HT" => ["Haïti", "Ayiti"],
"HM" => ["Heard Island & McDonald Islands"],
"HN" => ["Honduras"],
"HK" => ["香港"],
"HU" => ["Magyarország"],
"IS" => ["Ísland"],
"IN" => ["भारत", "Bharat", "இந்தியா"],
"ID" => ["Indonesia"],
"IR" => ["ایران"],
"IQ" => ["العراق"],
"IE" => ["Éire", "Ireland"],
"IM" => ["Ellan Vannin", "Isle of Man"],
"IL" => ["ישראל"],
"IT" => ["Italia"],
"JM" => ["Jamaica"],
"JP" => ["日本"],
"JE" => ["Jersey"],
"JO" => ["الأردن"],
"KZ" => ["Қазақстан", "Казахстан"],
"KE" => ["Kenya"],
"KI" => ["Kiribati"],
"KP" => ["조선"],
"KR" => ["한국"],
"KW" => ["الكويت"],
"KG" => ["Кыргызстан"],
"LA" => ["ລາວ"],
"LV" => ["Latvija"],
"LB" => ["لبنان"],
"LS" => ["Lesotho"],
"LR" => ["Liberia"],
"LY" => ["ليبيا"],
"LI" => ["Liechtenstein"],
"LT" => ["Lietuva"],
"LU" => ["Lëtzebuerg", "Luxembourg", "Luxemburg"],
"MO" => ["澳門", "Macau"],
"MG" => ["Madagasikara", "Madagascar"],
"MW" => ["Malawi"],
"MY" => ["Malaysia"],
"MV" => ["ދިވެހިރާއްޖޭގެ ޖުމްހޫރިއްޔާ"],
"ML" => ["Mali"],
"MT" => ["Malta"],
"MH" => ["M̧ajeļ", "Marshall Islands"],
"MQ" => ["Martinique"],
"MR" => ["موريتانيا"],
"MU" => ["Maurice", "Moris"],
"YT" => ["Mayotte"],
"MX" => ["México"],
"FM" => ["Micronesia"],
"MD" => ["Moldova"],
"MC" => ["Monaco"],
"MN" => ["Монгол улс"],
"ME" => ["Crna Gora", "Црна Гора"],
"MS" => ["Montserrat"],
"MA" => ["المغرب"],
"MZ" => ["Moçambique"],
"MM" => ["မြန်မာ"],
"NA" => ["Namibia"],
"NR" => ["Nauru"],
"NP" => ["नेपाल"],
"NL" => ["Nederland"],
"NC" => ["Nouvelle-Calédonie"],
"NZ" => ["New Zealand", "Aotearoa"],
"NI" => ["Nicaragua"],
"NE" => ["Niger"],
"NG" => ["Nigeria"],
"NU" => ["Niue"],
"NF" => ["Norfolk Island"],
"MK" => ["Северна Македонија"],
"MP" => ["Northern Mariana Islands"],
"NO" => ["Norge", "Noreg"],
"OM" => ["عمان"],
"PK" => ["پاکستان"],
"PW" => ["Palau"],
"PS" => ["فلسطين"],
"PA" => ["Panamá"],
"PG" => ["Papua Niugini", "Papua New Guinea"],
"PY" => ["Paraguay"],
"PE" => ["Perú"],
"PH" => ["Pilipinas"],
"PN" => ["Pitcairn"],
"PL" => ["Polska"],
"PT" => ["Portugal"],
"PR" => ["Puerto Rico"],
"QA" => ["قطر"],
"RE" => ["La Réunion"],
"RO" => ["România"],
"RU" => ["Россия"],
"RW" => ["Rwanda"],
"AS" => ["Amerika Samoa"],
"WS" => ["Samoa"],
"SM" => ["San Marino"],
"ST" => ["São Tomé e Príncipe"],
"SA" => ["السعودية"],
"SN" => ["Sénégal"],
"RS" => ["Србија"],
"SC" => ["Sesel", "Seychelles"],
"SL" => ["Sierra Leone"],
"SG" => ["新加坡", "சிங்கப்பூர்", "Singapore"],
"SK" => ["Slovensko"],
"SI" => ["Slovenija"],
"SB" => ["Solomon Islands"],
"SO" => ["Soomaaliya", "الصومال"],
"ZA" => ["South Africa", "Suid-Afrika", "Afrika Borwa"],
"GS" => ["South Georgia & the South Sandwich Islands"],
"SS" => ["جنوب السودان"],
"ES" => ["España"],
"LK" => ["ශ්රී ලංකා", "இலங்கை"],
"BL" => ["Saint-Barthélemy"],
"SH" => ["Saint Helena"],
"KN" => ["Saint Kitts & Nevis"],
"LC" => ["Saint Lucia"],
"SX" => ["Sint Maarten"],
"MF" => ["Saint-Martin"],
"PM" => ["Saint-Pierre et Miquelon"],
"VC" => ["Saint Vincent"],
"SD" => ["السودان"],
"SR" => ["Suriname"],
"SJ" => ["Svalbard og Jan Mayen"],
"SE" => ["Sverige"],
"CH" => ["Schweiz", "Suisse", "Svizzera"],
"SY" => ["سوريا"],
"TW" => ["臺灣"],
"TJ" => ["Тоҷикистон"],
"TZ" => ["Tanzania"],
"TH" => ["ไทย"],
"TG" => ["Togo"],
"TK" => ["Tokelau"],
"TO" => ["Tonga"],
"TT" => ["Trinidad & Tobago"],
"TN" => ["تونس"],
"TR" => ["Türkiye"],
"TM" => ["Türkmenistan"],
"TC" => ["Turks & Caicos Is"],
"TV" => ["Tuvalu"],
"UM" => ["US minor outlying islands"],
"UG" => ["Uganda"],
"UA" => ["Україна"],
"AE" => ["الإمارات"],
"US" => ["United States"],
"UY" => ["Uruguay"],
"UZ" => ["O'zbekiston"],
"VU" => ["Vanuatu"],
"VA" => ["Città del Vaticano", "Status Civitatis Vaticanæ"],
"VE" => ["Venezuela"],
"VN" => ["Việt Nam"],
"VG" => ["Virgin Islands"],
"VI" => ["Virgin Islands"],
"WF" => ["Wallis-et-Futuna"],
"EH" => ["الصحراء الغربية"],
"YE" => ["اليمن"],
"ZM" => ["Zambia"],
"ZW" => ["Zimbabwe"]
}
local_names = local_countries_names[country_code]
unless local_names do
raise "local country names not found for country #{country_code}"
end
local_names
end
end