-
-
Notifications
You must be signed in to change notification settings - Fork 1k
/
timezones.ex
60 lines (49 loc) · 1.96 KB
/
timezones.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
defmodule Plausible.Timezones do
@spec options(DateTime.t()) :: [{:key, String.t()}, {:value, String.t()}, {:offset, integer()}]
def options(now \\ DateTime.utc_now()) do
Tzdata.zone_list()
|> Enum.reduce([], fn timezone_code, acc -> build_option(timezone_code, acc, now) end)
|> Enum.sort_by(& &1[:offset], :desc)
end
@spec to_utc_datetime(NaiveDateTime.t(), String.t()) :: DateTime.t()
def to_utc_datetime(naive_date_time, timezone) do
case Timex.to_datetime(naive_date_time, timezone) do
%DateTime{} = tz_dt ->
Timex.Timezone.convert(tz_dt, "UTC")
%Timex.AmbiguousDateTime{after: after_dt} ->
Timex.Timezone.convert(after_dt, "UTC")
{:error, {:could_not_resolve_timezone, _, _, _}} ->
Timex.Timezone.convert(naive_date_time, "UTC")
end
end
@spec to_date_in_timezone(Date.t() | NaiveDateTime.t() | DateTime.t(), String.t()) :: Date.t()
def to_date_in_timezone(dt, timezone) do
to_datetime_in_timezone(dt, timezone) |> Timex.to_date()
end
@spec to_datetime_in_timezone(Date.t() | NaiveDateTime.t() | DateTime.t(), String.t()) ::
DateTime.t()
def to_datetime_in_timezone(dt, timezone) do
dt |> Timex.to_datetime("UTC") |> Timex.Timezone.convert(timezone)
end
defp build_option(timezone_code, acc, now) do
case Timex.Timezone.get(timezone_code, now) do
%Timex.TimezoneInfo{} = timezone_info ->
offset_in_minutes = timezone_info |> Timex.Timezone.total_offset() |> div(-60)
hhmm_formatted_offset =
timezone_info
|> Timex.TimezoneInfo.format_offset()
|> String.slice(0..-4//1)
option = [
key: "(GMT#{hhmm_formatted_offset}) #{timezone_code}",
value: timezone_code,
offset: offset_in_minutes
]
[option | acc]
error ->
Sentry.capture_message("Failed to fetch timezone",
extra: %{code: timezone_code, error: inspect(error)}
)
acc
end
end
end