Packages

A timezone data provider for Gleam!

Current section

3 Versions

Jump to

Compare versions

6 files changed
+188 additions
-42 deletions
  @@ -6,14 +6,35 @@ A timezone data provider for Gleam!
6 6
7 7 ```sh
8 8 gleam add gtz
9 - gleam add gtempo@5
9 + # Choose which package to use for other time functionality
10 + gleam add gtempo
11 + gleam add gleam_time
10 12 ```
11 13
12 - This package was written to be used with the [Tempo](https://hexdocs.pm/gtempo/index.html) package, but could be expanded to provide timezone support for other libraries as well! Contributions are welcome! Currently this package is very simple and only supports converting non-naive datetimes to a specific timezone via Tempo; it does not support constructing new datetimes in a specific timezone or assigning a timezone to an existing naive datetime.
14 + This package has functions to be used with the [gtempo](https://hexdocs.pm/gtempo/index.html) package and the [gleam_time](https://hexdocs.pm/gleam_time/index.html) package. Currently this package is very simple: it only supports converting non-naive datetimes to a specific timezone via `gtempo`, and calculating an offset given a timestamp and time zone via the `gleam_time` types. Contributions are welcome!
13 15
14 16 Ambiguous datetimes and DST boundaries are not handled explicitly by this package, but instead rely on the target timezone package's default handling. It seems like the Elixir package prefers the future time and JavaScript prefers the past time for DST boundaries. Once ambiguous datetimes are worked out to be a little more explicit or obvious in this package, there will probably be a v1 release.
15 17
16 - Supports both the Erlang and JavaScript targets.
18 + Supports both the Erlang (via a dependency on the Elixir `tz` and `timex` libraries) and JavaScript (via the native `Intl` API) targets.
19 +
20 + #### Calculating Offsets In a Time Zone
21 + ```gleam
22 + import gleam/time/timestamp
23 + import gtz
24 +
25 + let my_ts = timestamp.from_unix_seconds(1_729_257_776)
26 +
27 + let assert Ok(ny_offset) =
28 + gtz.calculate_offset(my_ts, in: "America/New_York")
29 +
30 + // Now that we have the offset for the timestamp in the desired time zone, we
31 + // can convert it to a calendar date and time
32 + timestamp.to_calendar(my_ts, ny_offset)
33 + // -> #(
34 + // calendar.Date(2024, calendar.October, 18),
35 + // calendar.TimeOfDay(9, 22, 56, 0)
36 + // )
37 + ```
17 38
18 39 #### Converting DateTimes to the Local Timezone
19 40 ```gleam
  @@ -1,5 +1,5 @@
1 1 name = "gtz"
2 - version = "0.2.0"
2 + version = "0.3.0"
3 3
4 4 # Fill out these fields if you intend to generate HTML documentation or publish
5 5 # your project to the Hex package manager.
  @@ -17,6 +17,7 @@ gleam_stdlib = ">= 0.34.0 and < 2.0.0"
17 17 tz = ">= 0.28.1 and < 1.0.0"
18 18 timex = ">= 3.7.11 and < 4.0.0"
19 19 gtempo = ">= 7.0.0 and < 8.0.0"
20 + gleam_time = ">= 1.7.0 and < 2.0.0"
20 21
21 22 [dev-dependencies]
22 23 gleeunit = ">= 1.0.0 and < 2.0.0"
  @@ -1,6 +1,6 @@
1 1 {<<"name">>, <<"gtz">>}.
2 2 {<<"app">>, <<"gtz">>}.
3 - {<<"version">>, <<"0.2.0">>}.
3 + {<<"version">>, <<"0.3.0">>}.
4 4 {<<"description">>, <<"A timezone data provider for Gleam! "/utf8>>}.
5 5 {<<"licenses">>, [<<"Apache-2.0">>]}.
6 6 {<<"build_tools">>, [<<"gleam">>]}.
  @@ -9,16 +9,6 @@
9 9 {<<"Repository">>, <<"https://github.com/jrstrunk/gtz">>}
10 10 ]}.
11 11 {<<"requirements">>, [
12 - {<<"gleam_stdlib">>, [
13 - {<<"app">>, <<"gleam_stdlib">>},
14 - {<<"optional">>, false},
15 - {<<"requirement">>, <<">= 0.34.0 and < 2.0.0">>}
16 - ]},
17 - {<<"timex">>, [
18 - {<<"app">>, <<"timex">>},
19 - {<<"optional">>, false},
20 - {<<"requirement">>, <<">= 3.7.11 and < 4.0.0">>}
21 - ]},
22 12 {<<"gtempo">>, [
23 13 {<<"app">>, <<"gtempo">>},
24 14 {<<"optional">>, false},
  @@ -28,6 +18,21 @@
28 18 {<<"app">>, <<"tz">>},
29 19 {<<"optional">>, false},
30 20 {<<"requirement">>, <<">= 0.28.1 and < 1.0.0">>}
21 + ]},
22 + {<<"gleam_stdlib">>, [
23 + {<<"app">>, <<"gleam_stdlib">>},
24 + {<<"optional">>, false},
25 + {<<"requirement">>, <<">= 0.34.0 and < 2.0.0">>}
26 + ]},
27 + {<<"gleam_time">>, [
28 + {<<"app">>, <<"gleam_time">>},
29 + {<<"optional">>, false},
30 + {<<"requirement">>, <<">= 1.7.0 and < 2.0.0">>}
31 + ]},
32 + {<<"timex">>, [
33 + {<<"app">>, <<"timex">>},
34 + {<<"optional">>, false},
35 + {<<"requirement">>, <<">= 3.7.11 and < 4.0.0">>}
31 36 ]}
32 37 ]}.
33 38 {<<"files">>, [
  @@ -1,6 +1,7 @@
1 1 {application, gtz, [
2 - {vsn, "0.2.0"},
2 + {vsn, "0.3.0"},
3 3 {applications, [gleam_stdlib,
4 + gleam_time,
4 5 gtempo,
5 6 timex,
6 7 tz]},
  @@ -1,7 +1,7 @@
1 1 -module(gtz).
2 - -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
3 -
4 - -export([timezone/1, local_name/0]).
2 + -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
3 + -define(FILEPATH, "src/gtz.gleam").
4 + -export([timezone/1, calculate_offset/2, local_name/0]).
5 5
6 6 -if(?OTP_RELEASE >= 27).
7 7 -define(MODULEDOC(Str), -moduledoc(Str)).
  @@ -13,17 +13,18 @@
13 13
14 14 ?MODULEDOC(" Functions to provide simple timezone support for other Gleam datetime libraries.\n").
15 15
16 - -file("src/gtz.gleam", 22).
16 + -file("src/gtz.gleam", 25).
17 17 ?DOC(
18 - " Constructs a TimeZoneProvider type to be used with the Tempo package. \n"
18 + " Constructs a TimeZoneProvider type to be used with the Tempo package.\n"
19 19 " Returns an error if the timezone is not valid.\n"
20 - " \n"
20 + "\n"
21 21 " ## Examples\n"
22 - " \n"
22 + "\n"
23 23 " ```gleam\n"
24 24 " import tempo/datetime\n"
25 25 "\n"
26 26 " let assert Ok(tz) = gtz.timezone(\"America/New_York\")\n"
27 + "\n"
27 28 " datetime.literal(\"2024-06-21T06:30:02.334Z\")\n"
28 29 " |> datetime.to_timezone(tz)\n"
29 30 " |> datetime.to_string\n"
  @@ -41,7 +42,7 @@ timezone(Name) ->
41 42 {{Year, Month, Day}, {Hour, Minute, Second}} = tempo@naive_datetime:to_tuple(
42 43 Utc_naive_datetime
43 44 ),
44 - _assert_subject = begin
45 + Offset@1 = case begin
45 46 _pipe = 'Elixir.GTZ_FFI':calculate_offset(
46 47 Year,
47 48 Month,
  @@ -51,32 +52,94 @@ timezone(Name) ->
51 52 Second,
52 53 Name
53 54 ),
54 - _pipe@1 = tempo@duration:minutes(_pipe),
55 + _pipe@1 = gleam@time@duration:minutes(_pipe),
55 56 tempo@offset:from_duration(_pipe@1)
56 - end,
57 - {ok, Offset} = case _assert_subject of
58 - {ok, _} -> _assert_subject;
57 + end of
58 + {ok, Offset} -> Offset;
59 59 _assert_fail ->
60 60 erlang:error(#{gleam_error => let_assert,
61 61 message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
62 - value => _assert_fail,
62 + file => <<?FILEPATH/utf8>>,
63 63 module => <<"gtz"/utf8>>,
64 64 function => <<"timezone"/utf8>>,
65 - line => 32})
65 + line => 35,
66 + value => _assert_fail,
67 + start => 1024,
68 + 'end' => 1200,
69 + pattern_start => 1035,
70 + pattern_end => 1045})
66 71 end,
67 - Offset
72 + Offset@1
68 73 end}};
69 74
70 75 false ->
71 76 {error, nil}
72 77 end.
73 78
74 - -file("src/gtz.gleam", 71).
79 + -file("src/gtz.gleam", 73).
80 + ?DOC(
81 + " Calculates the offset of a given timestamp in a specific time zone. Returns\n"
82 + " an error if the time zone is invalid.\n"
83 + "\n"
84 + " This can be combined with the `gleam_time` package to convert timestamps to\n"
85 + " calendar dates in a given time zone.\n"
86 + "\n"
87 + " ## Example\n"
88 + "\n"
89 + " ```gleam\n"
90 + " import gtz\n"
91 + " import gleam/time/timestamp\n"
92 + "\n"
93 + " let my_ts =\n"
94 + " 1_729_257_776\n"
95 + " |> timestamp.from_unix_seconds\n"
96 + "\n"
97 + " let assert Ok(offset) =\n"
98 + " gtz.calculate_offset(my_ts, in: \"America/New_York\")\n"
99 + "\n"
100 + " timestamp.to_calendar(my_ts, offset)\n"
101 + " // -> #(\n"
102 + " // calendar.Date(2024, calendar.October, 18),\n"
103 + " // calendar.TimeOfDay(9, 22, 56, 0)\n"
104 + " // )\n"
105 + " ```\n"
106 + ).
107 + -spec calculate_offset(gleam@time@timestamp:timestamp(), binary()) -> {ok,
108 + gleam@time@duration:duration()} |
109 + {error, nil}.
110 + calculate_offset(Timestamp, Time_zone) ->
111 + case 'Elixir.GTZ_FFI':is_valid_timezone(Time_zone) of
112 + true ->
113 + {{date, Year, Month, Day},
114 + {time_of_day, Hours, Minutes, Seconds, _}} = gleam@time@timestamp:to_calendar(
115 + Timestamp,
116 + {duration, 0, 0}
117 + ),
118 + _pipe@1 = 'Elixir.GTZ_FFI':calculate_offset(
119 + Year,
120 + begin
121 + _pipe = Month,
122 + gleam@time@calendar:month_to_int(_pipe)
123 + end,
124 + Day,
125 + Hours,
126 + Minutes,
127 + Seconds,
128 + Time_zone
129 + ),
130 + _pipe@2 = gleam@time@duration:minutes(_pipe@1),
131 + {ok, _pipe@2};
132 +
133 + false ->
134 + {error, nil}
135 + end.
136 +
137 + -file("src/gtz.gleam", 126).
75 138 ?DOC(
76 139 " Returns the name of the host system's timezone.\n"
77 - " \n"
140 + "\n"
78 141 " ## Examples\n"
79 - " \n"
142 + "\n"
80 143 " ```gleam\n"
81 144 " gtz.local_name()\n"
82 145 " // -> \"Europe/London\"\n"
Loading more files…