tempo/datetime

Functions to use with the DateTime type in Tempo.

Examples

import tempo/datetime
import snag

pub fn main() {
  datetime.literal("2024-12-25T06:00:00+05:00")
  |> datetime.format("ddd @ h:mm A, Z")
  // -> "Fri @ 6:00 AM, +05:00"

  datetime.parse("06:21:2024 23:17:07.123Z", "MM:DD:YYYY HH:mm:ss.SSSZ")
  |> snag.map_error(datetime.describe_parse_error)
  |> result.map(datetime.to_string)
  // -> Ok("2024-06-21T23:17:07.123Z")
}
import gleam/list
import tempo/datetime
import tempo/period

pub fn get_every_friday_between(datetime1, datetime2) {
  period.new(datetime1, datetime2)
  |> period.comprising_dates
  |> list.filter(fn(date) {
    date |> date.to_day_of_week == date.Fri
  })
  // -> ["2024-06-21", "2024-06-28", "2024-07-05"]
}

Calendar and range of valid dates

The date part of a datetime is a proleptic Gregorian calendar date, with Gregorian month lengths and the Gregorian leap year rule (divisible by 4, except century years, which must be divisible by 400). Those rules are applied to dates before the calendar’s 1582 adoption too, so early dates do not match what was historically recorded on the Julian calendar. No other calendar system is supported.

Constructing a datetime from calendar parts is limited to years 1000 through 9999, so that a year is always four digits. Arithmetic is not range checked and can carry a value outside that range, where it still compares correctly but no longer formats as valid ISO 8601. See the tempo/date module docs for the details.

Values

pub fn add(
  datetime: tempo.DateTime,
  duration duration_to_add: duration.Duration,
) -> tempo.DateTime

Adds a duration to a datetime.

Examples

datetime.literal("2024-06-12T23:17:00Z")
|> datetime.add(duration |> tempo.offset_get_minutes(3))
// -> datetime.literal("2024-06-12T23:20:00Z")
pub fn apply_offset(
  datetime: tempo.DateTime,
) -> tempo.NaiveDateTime

Applies the offset of a datetime to the date and time values, resulting in a new naive datetime value that represents the original datetime in UTC time.

Examples

datetime.literal("2024-06-21T05:36:11.195-04:00")
|> datetime.apply_offset
// -> naive_datetime.literal("2024-06-21T09:36:11.195")
pub fn as_period(
  start start: tempo.DateTime,
  end end: tempo.DateTime,
) -> tempo.Period

Creates a period between two datetimes, where the start and end times are the equivalent UTC times of the provided datetimes. The specified start and end datetimes will be swapped if the start datetime is later than the end datetime.

Examples

datetime.to_period(
  start: datetime.literal("2024-06-12T23:17:00Z")
  end: datetime.literal("2024-06-16T01:16:12Z"),
)
|> period.as_days
// -> 3
datetime.to_period(
  start: datetime.literal("2024-06-12T23:17:00Z")
  end: datetime.literal("2024-06-16T01:18:12Z"),
)
|> period.format
// -> "3 days, 2 hours, and 1 minute"
pub fn compare(
  a: tempo.DateTime,
  to b: tempo.DateTime,
) -> order.Order

Compares two datetimes.

Examples

datetime.literal("2024-06-21T23:47:00+09:05")
|> datetime.compare(to: datetime.literal("2024-06-21T23:47:00+09:05"))
// -> order.Eq
datetime.literal("2023-05-11T13:30:00-04:00")
|> datetime.compare(to: datetime.literal("2023-05-11T13:15:00Z"))
// -> order.Lt
datetime.literal("2024-06-12T23:47:00+09:05")
|> datetime.compare(to: datetime.literal("2022-04-12T00:00:00"))
// -> order.Gt
pub fn describe_parse_error(
  error: error.DateTimeParseError,
) -> String

Converts a datetime parse error to a human readable error message.

Example

datetime.parse("13:42:11.314-04:00", "YYYY-MM-DDTHH:mm:ss.SSSZ")
|> snag.map_error(with: datetime.describe_parse_error)
// -> snag.error("Invalid date format in datetime: 13:42:11.314-04:00")
pub fn difference(
  from a: tempo.DateTime,
  to b: tempo.DateTime,
) -> duration.Duration

Returns the difference between two datetimes as a duration between their equivalent UTC times.

Examples

datetime.literal("2024-06-12T23:17:00Z")
|> datetime.difference(
  to: datetime.literal("2024-06-16T01:16:12Z"),
)
|> duration.as_days
// -> 3
datetime.literal("2024-06-12T23:17:00Z")
|> datetime.difference(
  to: datetime.literal("2024-06-16T01:18:12Z"),
)
|> duration.format
// -> "3 days, 2 hours, and 1 minutes"
pub fn drop_offset(
  datetime: tempo.DateTime,
) -> tempo.NaiveDateTime

Drops the time of a datetime, leaving the date and time values unchanged.

Examples

datetime.literal("2024-06-13T13:42:11.195Z")
|> datetime.drop_offset
// -> naive_datetime.literal("2024-06-13T13:42:11")
pub fn drop_time(datetime: tempo.DateTime) -> tempo.DateTime

Drops the time of a datetime, leaving the date value unchanged.

Examples

datetime.literal("2024-06-18T13:42:11.195Z")
|> datetime.drop_time
// -> naive_datetime.literal("2024-06-18T00:00:00Z")
pub fn format(
  datetime: tempo.DateTime,
  in format: tempo.DateTimeFormat,
) -> String

Formats a datetime value into a string using the provided format.

All calendar directives render proleptic Gregorian calendar values, and the month and day names are the English Gregorian ones.

Examples

datetime.literal("2024-06-21T13:42:11.314-04:00")
|> datetime.format(tempo.Custom("ddd @ h:mm A (z)"))
// -> "Fri @ 1:42 PM (-04)"
datetime.literal("2024-06-03T09:02:01-04:00")
|> datetime.format(tempo.Custom("YY YYYY M MM MMM MMMM D DD d dd ddd"))
// -----------:---------------> "24 2024 6 06 Jun June 3 03 1 Mo Mon"
datetime.literal("2024-06-03T09:02:01.014920202-00:00")
|> datetime.format(tempo.Custom("dddd SSS SSSS SSSSS Z ZZ z"))
// -> "Monday 014 014920 014920202 -00:00 -0000 Z"
datetime.literal("2024-06-03T13:02:01-04:00")
|> datetime.format(tempo.Custom("H HH h hh m mm s ss a A [An ant]"))
// --------------------------> "13 13 1 01 2 02 1 01 pm PM An ant"
pub fn from_dynamic_string(
  dynamic_string: dynamic.Dynamic,
) -> Result(tempo.DateTime, List(decode.DecodeError))

Checks if a dynamic value is a valid datetime string, and returns the datetime if it is.

Examples

dynamic.string("2024-06-13T13:42:11.195Z")
|> datetime.from_dynamic_string
// -> Ok(datetime.literal("2024-06-13T13:42:11.195Z"))
dynamic.string("24-06-13,13:42:11.195")
|> datetime.from_dynamic_string
// -> Error([
//   decode.DecodeError(
//     expected: "tempo.DateTime",
//     found: "Invalid format: 24-06-13,13:42:11.195",
//     path: [],
//   ),
// ])
pub fn from_dynamic_unix_micro_utc(
  dynamic_ts: dynamic.Dynamic,
) -> Result(tempo.DateTime, List(decode.DecodeError))

Checks if a dynamic value is a valid unix timestamp in microseconds, and returns the datetime if it is.

Examples

dynamic.int(1_718_629_314_334_734)
|> datetime.from_dynamic_unix_micro_utc
// -> Ok(datetime.literal("2024-06-17T13:01:54.334734Z"))
dynamic.string("hello")
|> datetime.from_dynamic_unix_micro_utc
// -> Error([
//   decode.DecodeError(
//     expected: "Int",
//     found: "String",
//     path: [],
//   ),
// ])
pub fn from_dynamic_unix_milli_utc(
  dynamic_ts: dynamic.Dynamic,
) -> Result(tempo.DateTime, List(decode.DecodeError))

Checks if a dynamic value is a valid unix timestamp in milliseconds, and returns the datetime if it is.

Examples

dynamic.int(1_718_629_314_334)
|> datetime.from_dynamic_unix_milli_utc
// -> Ok(datetime.literal("2024-06-17T13:01:54.334Z"))
dynamic.string("hello")
|> datetime.from_dynamic_unix_milli_utc
// -> Error([
//   decode.DecodeError(
//     expected: "Int",
//     found: "String",
//     path: [],
//   ),
// ])
pub fn from_dynamic_unix_utc(
  dynamic_ts: dynamic.Dynamic,
) -> Result(tempo.DateTime, List(decode.DecodeError))

Checks if a dynamic value is a valid unix timestamp in seconds, and returns the datetime representation if it is.

Examples

dynamic.int(1_718_629_314)
|> datetime.from_dynamic_unix_utc
// -> Ok(datetime.literal("2024-06-17T13:01:54Z"))
dynamic.string("hello")
|> datetime.from_dynamic_unix_utc
// -> Error([
//   decode.DecodeError(
//     expected: "Int",
//     found: "String",
//     path: [],
//   ),
// ])
pub fn from_string(
  datetime: String,
) -> Result(tempo.DateTime, error.DateTimeParseError)

Parses a datetime string in the format YYYY-MM-DDThh:mm:ss.sTZD, YYYYMMDDThhmmss.sTZD, YYYY-MM-DD hh:mm:ss.sTZD, YYYYMMDD hhmmss.sTZD, YYYY-MM-DD, YYYY-M-D, YYYY/MM/DD, YYYY/M/D, YYYY.MM.DD, YYYY.M.D, YYYY_MM_DD, YYYY_M_D, YYYY MM DD, YYYY M D, or YYYYMMDD.

The date part is read as a proleptic Gregorian calendar date and must have a year between 1000 and 9999, with a day that exists in that Gregorian month. The offset must be between -12:00 and +14:00.

Examples

datetime.from_string("20240613T230400.009+00:00")
// -> datetime.literal("2024-06-13T23:04:00.009Z")
pub fn from_string_fast(
  datetime: String,
) -> Result(tempo.DateTime, error.DateTimeParseError)

Deprecated: This was experimental and not meant to be in the public API. The regular parse functions are just as fast now.

pub fn from_timestamp(
  timestamp: timestamp.Timestamp,
) -> tempo.DateTime

Converts a core gleam time timestamp type to a datetime.

pub fn from_unix_micro(unix_ts: Int) -> tempo.DateTime

Returns the UTC datetime of a unix timestamp in microseconds.

Examples

datetime.from_unix_micro(1_718_629_314_334_734)
// -> datetime.literal("2024-06-17T13:01:54.334734Z")
pub fn from_unix_milli(unix_ts: Int) -> tempo.DateTime

Returns the UTC datetime of a unix timestamp in milliseconds.

Examples

datetime.from_unix_milli(1_718_629_314_334)
// -> datetime.literal("2024-06-17T13:01:54.334Z")
pub fn from_unix_seconds(unix_ts: Int) -> tempo.DateTime

Returns the UTC datetime of a unix timestamp.

Examples

datetime.from_unix_seconds(1_718_829_191)
// -> datetime.literal("2024-06-17T12:59:51Z")
pub fn get_calendar_date(
  datetime: tempo.DateTime,
) -> calendar.Date

Gets the core gleam time package calendar date of a datetime.

Examples

datetime.literal("2024-06-21T13:42:11.195Z")
|> datetime.get_calendar_date
// -> calendar.Date(2024, calendar.June, 21)
pub fn get_calendar_time_of_day(
  datetime: tempo.DateTime,
) -> calendar.TimeOfDay

Gets the core gleam time package calendar time of day of a datetime.

Examples

datetime.literal("2024-06-21T13:42:11.195Z")
|> datetime.get_calendar_time_of_day
// -> calendar.TimeOfDay(13, 42, 11, 195_000_000)
pub fn get_date(datetime: tempo.DateTime) -> tempo.Date

Gets the date of a datetime.

Examples

datetime.literal("2024-06-21T13:42:11.195Z")
|> datetime.get_date
// -> date.literal("2024-06-21")
pub fn get_offset(datetime: tempo.DateTime) -> tempo.Offset

Gets the offset of a datetime.

Examples

datetime.literal("2024-06-12T13:42:11.195-04:00")
|> datetime.get_offset
// -> offset.literal("+04:00")
pub fn get_time(datetime: tempo.DateTime) -> tempo.Time

Gets the time of a datetime.

Examples

datetime.literal("2024-06-21T13:42:11.195Z")
|> datetime.get_time
// -> time.literal("13:42:11.195")
pub fn get_timezone_name(
  datetime: tempo.DateTime,
) -> option.Option(String)

Gets the name of the timezone the datetime is in.

Example

datetime.literal("2024-06-21T06:30:02.334Z")
|> datetime.get_timezone_name
// -> None
import tempo/time_zone
let assert Ok(tz) = time_zone.new("Europe/London")
datetime.to_timezone(my_datetime, tz)
|> datetime.get_timezone_name
// -> Some("Europe/London")
pub fn is_earlier(
  a: tempo.DateTime,
  than b: tempo.DateTime,
) -> Bool

Checks if the first datetime is earlier than the second datetime.

Examples

datetime.literal("2024-06-21T23:47:00+09:05")
|> datetime.is_earlier(
  than: datetime.literal("2024-06-21T23:47:00+09:05"),
)
// -> False
datetime.literal("2023-05-11T13:30:00-04:00")
|> datetime.is_earlier(
  than: datetime.literal("2023-05-11T13:15:00Z"),
)
// -> True
pub fn is_earlier_or_equal(
  a: tempo.DateTime,
  to b: tempo.DateTime,
) -> Bool

Checks if the first datetime is earlier or equal to the second datetime.

Examples

datetime.literal("2024-06-21T23:47:00+09:05")
|> datetime.is_earlier_or_equal(
  to: datetime.literal("2024-06-21T23:47:00+09:05"),
)
// -> True
datetime.literal("2024-07-15T23:40:00-04:00")
|> datetime.is_earlier_or_equal(
  to: datetime.literal("2023-05-11T13:15:00Z"),
)
// -> False
pub fn is_equal(a: tempo.DateTime, to b: tempo.DateTime) -> Bool

Checks if the first datetime is equal to the second datetime.

Examples

datetime.literal("2024-06-21T09:44:00Z")
|> datetime.is_equal(
  to: datetime.literal("2024-06-21T05:44:00-04:00"),
)
// -> True
datetime.literal("2024-06-21T09:44:00Z")
|> datetime.is_equal(
  to: datetime.literal("2024-06-21T09:44:00.045Z"),
)
// -> False
pub fn is_later(
  a: tempo.DateTime,
  than b: tempo.DateTime,
) -> Bool

Checks if the first datetime is later than the second datetime.

Examples

datetime.literal("2024-06-21T23:47:00+09:05")
|> datetime.is_later(
  than: datetime.literal("2024-06-21T23:47:00+09:05"),
)
// -> False
datetime.literal("2023-05-11T13:00:00+04:00")
|> datetime.is_later(
  than: datetime.literal("2023-05-11T13:15:00.534Z"),
)
// -> True
pub fn is_later_or_equal(
  a: tempo.DateTime,
  to b: tempo.DateTime,
) -> Bool

Checks if the first datetime is later or equal to the second datetime.

Examples

datetime.literal("2016-01-11T03:47:00+09:05")
|> datetime.is_later_or_equal(
  to: datetime.literal("2024-06-21T23:47:00+09:05"),
)
// -> False
datetime.literal("2024-07-15T23:40:00-04:00")
|> datetime.is_later_or_equal(
  to: datetime.literal("2023-05-11T13:15:00Z"),
)
// -> True
pub fn literal(datetime: String) -> tempo.DateTime

Create a new datetime value from a string literal, but will panic if the string is invalid. Accepted formats are YYYY-MM-DDThh:mm:ss.sTZD or YYYYMMDDThhmmss.sTZD/// The date part is read as a proleptic Gregorian calendar date and must have a year between 1000 and 9999, with a day that exists in that Gregorian month. The time part must be a valid time of day, and the offset must be between -12:00 and +14:00.

Useful for declaring datetime literals that you know are valid within your program.

Examples

datetime.literal("2024-06-13T23:04:00.009+10:00")
|> datetime.to_string
// -> "2024-06-13T23:04:00.009+10:00"
pub fn new(
  date date: tempo.Date,
  time time: tempo.Time,
  offset offset: tempo.Offset,
) -> tempo.DateTime

Create a new datetime from a date, time, and offset. The date carries the proleptic Gregorian calendar year, month and day; see the tempo/date module docs for its 1000 to 9999 year range.

Examples

datetime.new(
  date.literal("2024-06-13"),
  time.literal("23:04:00.009"),
  offset.literal("+10:00"),
)
// -> datetime.literal("2024-06-13T23:04:00.009+10:00")
pub fn parse(
  str: String,
  in format: tempo.DateTimeFormat,
) -> Result(tempo.DateTime, error.DateTimeParseError)

Parses a datetime string in the provided format. Always prefer using this over parse_any. All parsed formats must have all parts of a datetime (date, time, offset). Use the other modules for parsing lesser date time values.

The date part is read as a proleptic Gregorian calendar date, with month names matched against the English Gregorian ones. The parsed year must land between 1000 and 9999, and the day must exist in that Gregorian month. A YY two-digit year is resolved into the hundred years leading up to the current date, so "99" parses as 1999 rather than 2099.

Values can be escaped by putting brackets around them, like “[Hello!] YYYY”.

Available directives: YY (two-digit year), YYYY (four-digit year), M (month), MM (two-digit month), MMM (short month name), MMMM (full month name), D (day of the month), DD (two-digit day of the month), H (hour), HH (two-digit hour), h (12-hour clock hour), hh (two-digit 12-hour clock hour), m (minute), mm (two-digit minute), s (second), ss (two-digit second), SSS (millisecond), SSSS (microsecond), Z (offset from UTC), ZZ (offset from UTC with no “:”), z (short offset from UTC “-04”, “Z”), zz (full offset from UTC as “-04:00” or “Z” if UTC), A (AM/PM), a (am/pm).

Example

datetime.parse("2024/06/08, 13:42:11, -04:00", "YYYY/MM/DD, HH:mm:ss, Z")
// -> Ok(datetime.literal("2024-06-08T13:42:11-04"))
datetime.parse("January 13, 2024. 3:42:11Z", "MMMM DD, YYYY. H:mm:ssz")
// -> Ok(datetime.literal("2024-01-13T03:42:11Z"))
datetime.parse("Hi! 2024 11 13 12 2 am Z", "[Hi!] YYYY M D h m a z")
// -> Ok(datetime.literal("2024-11-13T00:02:00Z"))
pub fn parse_any(
  str: String,
) -> Result(tempo.DateTime, error.DateTimeParseError)

Tries to parse a given date string without a known format. It will not parse two digit years and will assume the month always comes before the day in a date.

Example

parse_any.parse_any("2024.06.21 01:32 PM -0400")
// -> Ok(datetime.literal("2024-06-21T13:32:00-04:00"))
parse_any.parse_any("2024.06.21 01:32 PM")
// -> Error(tempo.ParseMissingOffset)
pub fn subtract(
  datetime: tempo.DateTime,
  duration duration_to_subtract: duration.Duration,
) -> tempo.DateTime

Subtracts a duration from a datetime.

Examples

datetime.literal("2024-06-21T23:17:00Z")
|> datetime.subtract(duration.days(3))
// -> datetime.literal("2024-06-18T23:17:00Z")
pub fn time_left_in_day(datetime: tempo.DateTime) -> tempo.Time

Gets the time left in the day.

Does not account for leap seconds like the rest of the package.

Examples

naive_datetime.literal("2015-06-30T23:59:03Z")
|> naive_datetime.time_left_in_day
// -> time.literal("00:00:57")
naive_datetime.literal("2024-06-18T08:05:20-04:00")
|> naive_datetime.time_left_in_day
// -> time.literal("15:54:40")
pub fn to_calendar_parts(
  datetime: tempo.DateTime,
) -> #(calendar.Date, calendar.TimeOfDay, duration.Duration)

Returns the date, time, and offset parts of a datetime as a tuple of gleam_time types. This is useful for interop and accessing the underlying values of a datetime.

Example

let #(
  calendar.Date(year:, month:, day:),
  calendar.TimeOfDay(hour:, minute:, second:, nanosecond:),
  offset,
) = datetime.to_calendar_parts(my_datetime)
pub fn to_local_imprecise(
  datetime: tempo.DateTime,
) -> tempo.DateTime

Converts a datetime to the equivalent local datetime. Prefer to either design your application to not need this, or add an external time zone provider to use with the to_timezone function.

Conversion is based on the host’s current offset. We can not be sure the current host offset is applicable to the given datetime, and so an imprecise conversion will be performed. The imprecise conversion can be inaccurate to the degree the local offset changes throughout the year. For example, in North America where Daylight Savings Time is observed with a one-hour time shift, the imprecise conversion can be off by up to an hour, depending on the time of year.

If the date of the given datetime matches the date of the host, then the conversion will actually be precise all but during the hour(s) when the time zone offset is shifting.

Examples

datetime.literal("2024-06-21T09:57:11.195Z")
|> datetime.to_local_imprecise
// -> datetime.literal("2024-06-21T05:57:11.195-04:00")
datetime.literal("1998-08-23T09:57:11.195Z")
|> datetime.to_local_imprecise
// -> datetime.literal("1998-08-23T05:57:11.195-04:00")
pub fn to_offset(
  datetime: tempo.DateTime,
  offset: tempo.Offset,
) -> tempo.DateTime

Converts a datetime to the equivalent time in an offset.

Examples

datetime.literal("2024-06-21T05:36:11.195-04:00")
|> datetime.to_offset(offset.literal("+10:00"))
// -> datetime.literal("2024-06-21T19:36:11.195+10:00")
pub fn to_string(datetime: tempo.DateTime) -> String

Returns a string representation of a datetime value in the ISO 8601 format with millisecond precision. If a different precision is needed, use the format function. If serializing to send outside of Gleam and then parse back into a datetime value, use the serialize function.

Examples

datetime.to_string(my_datetime)
// -> "2024-06-21T05:22:22.009534Z"
pub fn to_timestamp(
  datetime: tempo.DateTime,
) -> timestamp.Timestamp

Converts a datetime to a core gleam time timestamp type.

pub fn to_timezone(
  datetime: tempo.DateTime,
  tz: tempo.TimeZoneProvider,
) -> tempo.DateTime

Converts a datetime to the specified time zone with a tempo.TimeZoneProvider to supply time zone information, which the tempo/time_zone module provides. This function converts the datetime one time, with the zone information not stored in the datetime value itself. Because the zone information is not stored in the datetime value, if you add or subtract time from the resulting datetime, this function should be called again to update the new datetime value for the given zone.

Example

import tempo/time_zone
let assert Ok(tz) = time_zone.new("America/New_York")
datetime.literal("2024-06-21T06:30:02.334Z")
|> datetime.to_timezone(tz)
|> datetime.to_string
// -> "2024-01-03T02:30:02.334-04:00"
import tempo/time_zone
let assert Ok(local_tz) = time_zone.local_name() |> time_zone.new
datetime.from_unix_seconds(1_729_257_776)
|> datetime.to_timezone(local_tz)
|> datetime.to_string
// -> "2024-10-18T14:22:56.000+01:00"
pub fn to_unix_micro(datetime: tempo.DateTime) -> Int

Returns the UTC unix timestamp in microseconds of a datetime.

Examples

datetime.literal("2024-06-17T13:01:54.334734Z")
|> datetime.to_unix_micro
// -> 1_718_629_314_334_734
pub fn to_unix_milli(datetime: tempo.DateTime) -> Int

Returns the UTC unix timestamp in milliseconds of a datetime.

Examples

datetime.literal("2024-06-17T13:01:54.334Z")
|> datetime.to_unix_milli
// -> 1_718_629_314_334
pub fn to_unix_seconds(datetime: tempo.DateTime) -> Int

Returns the UTC unix timestamp of a datetime.

Examples

datetime.literal("2024-06-17T12:59:51Z")
|> datetime.to_unix_seconds
// -> 1_718_829_191
pub fn to_utc(datetime: tempo.DateTime) -> tempo.DateTime

Converts a datetime to the equivalent UTC time.

Examples

datetime.literal("2024-06-21T05:36:11.195-04:00")
|> datetime.to_utc
// -> datetime.literal("2024-06-21T09:36:11.195Z")
pub const unix_epoch: tempo.DateTime

Starting point of unix timestamps

Search Document