gtz
Simple Gleam time zone conversions for all targets, built on top of tzif.
Converting an ambiguous calendar time to a timestamp
assumes the first occurrence of the ambiguous time. Accounting for leap
seconds depends on the dataset. If you need more control over how
ambiguous dates are handled or the designation of the given time in the
zone, use the tzif package directly.
The build function reads the host’s own time zone data. When the host
has none, or when it is somewhere other than where the host usually stores
it, supply a database yourself with build_from using the very nice
tzif or zones packages. If you are running this in a bare environment
with no time zone data available, the zones package is recommended.
import gleam/time/timestamp
import gtz
let zone_name = gtz.local_name() // "Asia/Kolkata"
let assert Ok(zone) = gtz.build(zone_name)
timestamp.from_unix_seconds(1_729_257_776)
|> gtz.to_calendar(zone)
// -> #(
// calendar.Date(2024, calendar.October, 18),
// calendar.TimeOfDay(18, 52, 56, 0),
// duration.seconds(19_800),
// )
Types
Values
pub fn build(name: String) -> Result(TimeZone, Nil)
Build a TimeZone from an IANA zone name such as "America/New_York".
Returns an error if the name is not a zone the host recognizes.
On the Erlang target, the operating system’s TZif database at
/usr/share/zoneinfo is read once and memoized into a persistent term. A
host with no zoneinfo tree there, such as a scratch container image, has no
zone to recognize and every name fails; use build_from and provide your own
database from the very nice tzif or zones packages instead. On
JavaScript, information for the given zone is derived from the host’s
native Temporal and Intl APIs.
Examples
gtz.local_name() |> gtz.build
// -> Ok(TimeZone)
gtz.build("Asia/Kolkata")
// -> Ok(TimeZone)
gtz.build("America/NewYork") // "New_York" is the correct name here
// -> Error(Nil)
pub fn build_from(
name: String,
database db: database.TzDatabase,
) -> Result(TimeZone, Nil)
Build a TimeZone from an IANA zone name and a tzif database of your own.
Returns an error if the database holds no usable data for the name.
Use this when working in a bare environment (such as a scratch container
image on the Erlang target), a host that keeps its zoneinfo somewhere
other than /usr/share/zoneinfo, or you need precise control over the
dataset.
The zones package ships a prebuilt database and needs no files at all,
which suits bare environments. tzif itself can load from any directory,
which suits a non-standard location or a database you compiled yourself.
Examples
import zones
let assert Ok(zone) = gtz.build_from("Asia/Kolkata", zones.database())
// -> Ok(TimeZone)
import tzif/database
let assert Ok(db) = database.load_from_path("/opt/zoneinfo")
let assert Ok(zone) = gtz.build_from("Asia/Kolkata", db)
// -> Ok(TimeZone)
pub fn from_calendar(
date: calendar.Date,
time: calendar.TimeOfDay,
zone: TimeZone,
) -> Result(timestamp.Timestamp, Nil)
Convert a date and time of day in zone to a timestamp. When you know
the offset, always prefer the timestamp.from_calendar in gleam_time
over this function.
When the time is ambiguous in the given time zone because of an offset
change such as daylight saving time, the timestamp that corresponds with
the first occurrence of that time is returned. When the time does not exist
in the given time zone because of an offset change such as daylight saving
time, Error(Nil) is returned.
Examples
let assert Ok(zone) = gtz.build("Australia/Lord_Howe")
// Clocks go back half an hour at 02:00, so 01:30 to 01:59 happens twice
gtz.from_calendar(
calendar.Date(2025, calendar.April, 6),
calendar.TimeOfDay(1, 45, 0, 0),
zone,
)
// -> Ok(Timestamp(1_743_864_300, 0)), the earlier of the two timestamps
// Clocks go forward from 02:00 to 02:30, so 02:15 never happens
gtz.from_calendar(
calendar.Date(2025, calendar.October, 5),
calendar.TimeOfDay(2, 15, 0, 0),
zone,
)
// -> Error(Nil)
pub fn local_name() -> String
Returns the name of the host system’s time zone.
Examples
gtz.local_name()
// -> "Pacific/Auckland"
On the Erlang target the zone is read from the operating system, in order:
the TZ environment variable, the symlink target of /etc/localtime, then
/etc/timezone or /etc/sysconfig/clock. If none of those yield an IANA
zone name, "UTC" is returned. On JavaScript the host’s Intl API is used.
pub fn to_calendar(
timestamp: timestamp.Timestamp,
zone: TimeZone,
) -> #(calendar.Date, calendar.TimeOfDay, duration.Duration)
Convert a timestamp to the equivalent date, time of day, and UTC offset in the given time zone.
Examples
let assert Ok(zone) = gtz.build("Asia/Kathmandu")
// Not every zone is a whole number of hours from UTC
timestamp.from_unix_seconds(1_729_257_776)
|> gtz.to_calendar(zone)
// -> #(
// calendar.Date(2024, calendar.October, 18),
// calendar.TimeOfDay(19, 7, 56, 0),
// duration.seconds(20_700),
// )