Current section
3 Versions
Jump to
Current section
3 Versions
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…