gleam/time/timestamp

Welcome to the timestamp module! This module and its Timestamp type are what you will be using most commonly when working with time in Gleam.

A timestamp represents a moment in time, represented as an amount of time since the calendar time 00:00:00 UTC on 1 January 1970, also known as the Unix epoch.

Wall clock time and monotonicity

Time is very complicated, especially on computers! While they generally do a good job of keeping track of what the time is, computers can get out-of-sync and start to report a time that is too late or too early. Most computers use “network time protocol” to tell each other what they think the time is, and computers that realise they are running too fast or too slow will adjust their clock to correct it. When this happens it can seem to your program that the current time has changed, and it may have even jumped backwards in time!

This measure of time is called wall clock time, and it is what people commonly think of when they think of time. It is important to be aware that it can go backwards, and your program must not rely on it only ever going forwards at a steady rate. For example, for tracking what order events happen in.

This module uses wall clock time. If your program needs time values to always increase you will need a monotonic time instead. It’s uncommon that you would need monotonic time, one example might be if you’re making a benchmarking framework.

The exact way that time works will depend on what runtime you use. The Erlang documentation on time has a lot of detail about time generally as well as how it works on the BEAM, it is worth reading. https://www.erlang.org/doc/apps/erts/time_correction.

Converting to local time

Timestamps don’t take into account time zones, so a moment in time will have the same timestamp value regardless of where you are in the world. To convert them to local time you will need to know the offset for the time zone you wish to use, likely from a time zone database. See the gleam/time/calendar module for more information.

Types

The main time type, which you should favour over other types such as calendar time types. It is efficient, unambiguous, and it is not possible to construct an invalid timestamp.

The most common situation in which you may need a different time data structure is when you need to display time to human for them to read. When you need to do this convert the timestamp to calendar time when presenting it, but internally always keep the time as a timestamp.

pub opaque type Timestamp

Functions

pub fn add(timestamp: Timestamp, duration: Duration) -> Timestamp

Add a duration to a timestamp.

Examples

add(from_unix_seconds(1000), duration.seconds(5))
// -> from_unix_seconds(1005)
pub fn compare(left: Timestamp, right: Timestamp) -> Order

Compare one timestamp to another, indicating whether the first is further into the future (greater) or further into the past (lesser) than the second.

Examples

compare(from_unix_seconds(1), from_unix_seconds(2))
// -> order.Lt
pub fn difference(left: Timestamp, right: Timestamp) -> Duration

Calculate the difference between two timestamps.

This is effectively substracting the first timestamp from the second.

Examples

difference(from_unix_seconds(1), from_unix_seconds(5))
// -> duration.seconds(4)
pub fn from_calendar(
  date date: Date,
  time time: TimeOfDay,
  offset offset: Duration,
) -> Timestamp

Create a Timestamp from a human-readable calendar time.

Examples

timestamp.from_calendar(
  date: calendar.Date(2024, calendar.December, 25),
  time: calendar.TimeOfDay(12, 30, 50, 0),
  offset: calendar.utc_offset,
)
|> timestamp.to_rfc3339(calendar.utc_offset)
// -> "2024-12-25T12:30:50Z"
pub fn from_unix_seconds(seconds: Int) -> Timestamp

Create a timestamp from a number of seconds since 00:00:00 UTC on 1 January 1970.

pub fn from_unix_seconds_and_nanoseconds(
  seconds seconds: Int,
  nanoseconds nanoseconds: Int,
) -> Timestamp

Create a timestamp from a number of seconds and nanoseconds since 00:00:00 UTC on 1 January 1970.

JavaScript int limitations

Remember that JavaScript can only perfectly represent ints between positive and negative 9,007,199,254,740,991! If you only use the nanosecond field then you will almost certainly not get the date value you want due to this loss of precision. Always use seconds primarily and then use nanoseconds for the final sub-second adjustment.

pub fn parse_rfc3339(input: String) -> Result(Timestamp, Nil)

Parses an RFC 3339 formatted time string into a Timestamp.

Examples

let assert Ok(ts) = timestamp.parse_rfc3339("1970-01-01T00:00:01Z")
timestamp.to_unix_seconds_and_nanoseconds(ts)
// -> #(1, 0)

Parsing an invalid timestamp returns an error.

let assert Error(Nil) = timestamp.parse_rfc3339("1995-10-31")

Notes

  • Follows the grammar specified in section 5.6 Internet Date/Time Format of RFC 3339 https://datatracker.ietf.org/doc/html/rfc3339#section-5.6.
  • The T and Z characters may alternatively be lower case t or z, respectively.
  • Full dates and full times must be separated by T or t, not any other character such as a space ( ).
  • Leap seconds rules are not considered. That is, any timestamp may specify digts 00 - 60 for the seconds.
  • Any part of a fractional second that cannot be represented in the nanosecond precision is tructated. That is, for the time string, "1970-01-01T00:00:00.1234567899Z", the fractional second .1234567899 will be represented as 123_456_789 in the Timestamp.
pub fn system_time() -> Timestamp

Get the current system time.

Note this time is not unique or monotonic, it could change at any time or even go backwards! The exact behaviour will depend on the runtime used. See the module documentation for more information.

On Erlang this uses erlang:system_time/1. On JavaScript this uses Date.now.

pub fn to_calendar(
  timestamp: Timestamp,
  offset: Duration,
) -> #(Date, TimeOfDay)

Convert a Timestamp to calendar time, suitable for presenting to a human to read.

If you want a machine to use the time value then you should not use this function and should instead keep it as a timestamp. See the documentation for the gleam/time/calendar module for more information.

Examples

timestamp.from_unix_seconds(0)
|> timestamp.to_calendar(calendar.utc_offset)
// -> #(Date(1970, January, 1), TimeOfDay(0, 0, 0, 0))
pub fn to_rfc3339(
  timestamp: Timestamp,
  offset: Duration,
) -> String

Convert a timestamp to a RFC 3339 formatted time string, with an offset supplied as an additional argument.

The output of this function is also ISO 8601 compatible so long as the offset not negative. Offsets have at-most minute precision, so an offset with higher precision will be rounded to the nearest minute.

If you are making an API such as a HTTP JSON API you are encouraged to use Unix timestamps instead of this format or ISO 8601. Unix timestamps are a better choice as they don’t contain offset information. Consider:

  • UTC offsets are not time zones. This does not and cannot tell us the time zone in which the date was recorded. So what are we supposed to do with this information?
  • Users typically want dates formatted according to their local time zone. What if the provided UTC offset is different from the current user’s time zone? What are we supposed to do with it then?
  • Despite it being useless (or worse, a source of bugs), the UTC offset creates a larger payload to transfer.

They also uses more memory than a unix timestamp. The way they are better than Unix timestamp is that it is easier for a human to read them, but this is a hinderance that tooling can remedy, and APIs are not primarily for humans.

Examples

to_rfc3339(from_unix_seconds(1000), 0)
// -> "1970-01-01T00:00:00Z"
pub fn to_unix_seconds(timestamp: Timestamp) -> Float

Convert the timestamp to a number of seconds since 00:00:00 UTC on 1 January 1970.

There may be some small loss of precision due to Timestamp being nanosecond accurate and Float not being able to represent this.

pub fn to_unix_seconds_and_nanoseconds(
  timestamp: Timestamp,
) -> #(Int, Int)

Convert the timestamp to a number of seconds and nanoseconds since 00:00:00 UTC on 1 January 1970. There is no loss of precision with this conversion on any target.

Search Document