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 [the current 27] leap seconds depends on the host’s dataset. If you need more control over where the time zone data comes from, how ambiguous dates are handled, how leap seconds are accounted for, or the designation of the given time in the zone, use the very nice tzif and/or zones packages directly.

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

Data for an IANA time zone. Build with build, then hand to to_calendar or from_calendar.

Examples

gtz.build("Asia/Kolkata")
// -> Ok(TimeZone)
pub opaque type TimeZone

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. In bare environments where the host has no zoneinfo, the prebuilt zones database is used as a fallback. 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 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),
// )
Search Document