tempo/time
Functions to use with the Time type in Tempo. The time values are wall
time values unless explicitly stated otherwise.
Examples
import tempo/time
pub fn is_past_5pm() {
tempo.is_time_later(than: time.literal("17:00"))
}
import tempo/time
pub fn get_enthusiastic_time() {
time.literal("13:42")
|> time.format(tempo.CustomTime(
"[The hour is:] HH, [wow! And even better the minute is:] mm!"
))
// -> "The hour is: 13, wow! And even better the minute is: 42!"
}
Calendar and range of valid times
A Time is a time of day on its own, with no date attached, so nothing in
this module depends on the Gregorian calendar or on any other, and there
is no range of valid dates.
The range of valid times is 00:00:00.000000 through 23:59:59.999999, with two additions allowed by ISO 8601:
24:00:00exactly, as the end-of-day marker. Any non-zero minute, second or microsecond alongside hour 24 is rejected.23:59:60, a leap second. Leap seconds can be constructed and parsed, and compare correctly against other times, but this package otherwise ignores leap seconds and will not preserve one through a conversion.
Sub-second precision is microseconds, so the microsecond component runs from 0 to 999_999. Values with more precision than that are rejected rather than rounded.
Types
pub type Boundary {
Boundary(time: tempo.Time, inclusive: Bool)
}
Constructors
-
Boundary(time: tempo.Time, inclusive: Bool)
Values
pub fn add(
a: tempo.Time,
duration b: duration.Duration,
) -> tempo.Time
Adds a duration to a time.
Example
time.literal("08:42:53")
|> time.add(duration.mintues(36))
// -> time.literal("09:18:53")
pub fn compare(a: tempo.Time, to b: tempo.Time) -> order.Order
Compares two time values.
Example
time.literal("13:42:11")
|> time.compare(to: time.literal("13:42:11"))
// -> order.Eq
time.literal("15:32:01")
|> time.compare(to: time.literal("13:42:11"))
// -> order.Gt
time.literal("13:10:11")
|> time.compare(to: time.literal("13:42:11"))
// -> order.Lt
pub fn describe_out_of_bounds_error(
error: error.TimeOutOfBoundsError,
) -> String
Converts a time out of bounds error to a human readable error message.
Example
time.new(23, 59, 60)
|> snag.map_error(with: time.describe_out_of_bounds_error)
// -> snag.error("Second out of bounds in time: 60")
pub fn describe_parse_error(
error: error.TimeParseError,
) -> String
Converts a time parse error to a human readable error message.
Example
time.parse("13 42 11", "HH:mm:ss")
|> snag.map_error(with: time.describe_parse_error)
// -> snag.error("Invalid time format: "13 42 11"")
pub fn difference(
from a: tempo.Time,
to b: tempo.Time,
) -> duration.Duration
Gets the difference between two times as a duration. Always prefer using
duration.start_monotonic and duration.stop_monotonic to record live
time passing, as it is more precise.
Example
time.literal("23:42:11.435")
|> time.difference(from: time.literal("23:42:09.743"))
|> duration.as_milliseconds
// -> 1692
time.literal("13:30:11")
|> time.difference(from: time.literal("13:55:13"))
|> duration.as_minutes
// -> -25
pub fn difference_abs(
a: tempo.Time,
from b: tempo.Time,
) -> duration.Duration
Gets the absolute difference between two times as a duration.
Example
time.literal("23:42:11.435")
|> time.difference_abs(from: time.literal("23:42:09.743"))
|> duration.as_milliseconds
// -> 1692
time.literal("13:30:11")
|> time.difference_abs(from: time.literal("13:55:13"))
|> duration.as_minutes
// -> 25
pub const end_of_day: tempo.Time
The end of the last second of the day.
Example
time.end_of_day
|> time.to_string
// "24:00:00.000000"
tempo.DateTime(date.literal("2024-06-21"), time.end_of_day, offset.utc)
|> datetime.to_string
// "2024-06-21T24:00:00.000000Z"
pub fn format(
time: tempo.Time,
in format: tempo.TimeFormat,
) -> String
Formats a time value using the provided format.
Example
time.literal("13:42:11")
|> time.format(tempo.ISO8601TimeMilli)
// -> "13:42:11.000"
time.literal("13:42:11.314")
|> time.format("h:mm A")
// -> "1:42 PM"
time.literal("09:02:01.014920202")
|> time.format("HH:mm:ss SSS SSSS SSSSS")
// -> "09:02:01 014 014920 014920202"
naive_datetime.literal("13:02:01")
|> naive_datetime.format("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_calendar_time_of_day(
time: calendar.TimeOfDay,
) -> Result(tempo.Time, error.TimeOutOfBoundsError)
Converts a core gleam time time of day to a tempo time.
pub fn from_duration(duration: duration.Duration) -> tempo.Time
Converts a duration to the equivalent time of day, assuming the duration epoch is “00:00:00”. Durations longer than 24 hours will be wrapped to fit within a 24 hour representation.
Example
duration.seconds(58)
|> time.from_duration
|> time.to_second_precision
|> time.to_string
// -> "00:00:58"
duration.minutes(17)
|> time.from_duration
|> time.to_string
// -> "00:17:00.000000000"
duration.hours(25)
|> time.from_duration
|> time.to_string
// -> "01:00:00.000000000"
duration.microseconds(-3_000_000)
|> time.from_duration
|> time.to_string
// -> "23:59:57.000000"
pub fn from_string(
time_str: String,
) -> Result(tempo.Time, error.TimeParseError)
Converts a string to a time value. Accepted formats are hh:mm:ss.s,
hhmmss.s, hh:mm:ss, hhmmss, hh:mm, or hhmm.
Valid times run from 00:00:00 through 23:59:59, plus 24:00:00 as the
end-of-day marker and 23:59:60 as a leap second. A sub-second value
finer than microseconds is rejected rather than rounded.
Example
time.from_string("00:00:00.000300")
// -> Ok(time.literal("00:00:00.000300"))
time.from_string("34:54:16")
// -> Error(tempo_error.TimeOutOfBounds)
pub fn from_tuple(
time: #(Int, Int, Int),
) -> Result(tempo.Time, error.TimeOutOfBoundsError)
Converts a tuple of hours, minutes, and seconds to a time value. Useful for using with another time library.
Example
#(13, 42, 11)
|> time.from_tuple
// -> time.literal("13:42:11")
pub fn from_tuple_microsecond(
time: #(Int, Int, Int, Int),
) -> Result(tempo.Time, error.TimeOutOfBoundsError)
Converts a tuple of hours, minutes, seconds, and microseconds to a time value. Useful for using with another time library.
Example
#(13, 42, 11, 872000)
|> time.from_tuple_microsecond
// -> time.literal("13:42:11.872")
pub fn get_hour(time: tempo.Time) -> Int
Gets the hour value of a time.
Example
time.literal("13:42:11")
|> time.get_hour
// -> 13
pub fn get_microsecond(time: tempo.Time) -> Int
Gets the microsecond value of a time.
Example
time.literal("13:42:11.123")
|> time.get_microsecond
// -> 123000
pub fn get_minute(time: tempo.Time) -> Int
Gets the minute value of a time.
Example
time.literal("13:42:11")
|> time.get_minute
// -> 42
pub fn get_second(time: tempo.Time) -> Int
Gets the second value of a time.
Example
time.literal("13:42:11")
|> time.get_second
// -> 11
pub fn is_between(
time: tempo.Time,
start: Boundary,
and end: Boundary,
) -> Bool
Checks if a time is between two boundaries.
Example
time.literal("13:42:11")
|> time.is_between(
Boundary(time.literal("05:00:00"), inclusive: True),
and: Boundary(time.literal("15:00:00"), inclusive: False),
)
// -> True
pub fn is_earlier(a: tempo.Time, than b: tempo.Time) -> Bool
Checks if the first time is earlier than the second time.
Example
time.literal("13:42:11")
|> time.is_earlier(than: time.literal("13:42:12"))
// -> True
time.literal("13:42:11")
|> time.is_earlier(than: time.literal("13:42:11"))
// -> False
time.literal("13:22:15")
|> time.is_earlier(than: time.literal("07:42:11"))
// -> False
pub fn is_earlier_or_equal(
a: tempo.Time,
to b: tempo.Time,
) -> Bool
Checks if the first time is earlier or equal to the second time.
Example
time.literal("13:42:11")
|> time.is_earlier_or_equal(to: time.literal("13:42:12"))
// -> True
time.literal("13:42:11")
|> time.is_earlier_or_equal(to: time.literal("13:42:11.000"))
// -> True
time.literal("13:22:15")
|> time.is_earlier_or_equal(to: time.literal("07:42:12"))
// -> False
pub fn is_equal(a: tempo.Time, to b: tempo.Time) -> Bool
Checks if the first time is equal to the second time.
Example
time.literal("13:42:11.000")
|> time.is_equal(to: time.literal("13:42:11"))
// -> True
time.literal("13:42:11.002")
|> time.is_equal(to: time.literal("13:42:11"))
// -> False
pub fn is_later(a: tempo.Time, than b: tempo.Time) -> Bool
Checks if the first time is later than the second time.
Example
time.literal("13:22:15")
|> time.is_later(than: time.literal("07:42:11"))
// -> True
time.literal("13:42:11")
|> time.is_later(than: time.literal("13:42:12"))
// -> False
time.literal("13:42:11")
|> time.is_later(than: time.literal("13:42:11"))
// -> False
pub fn is_later_or_equal(a: tempo.Time, to b: tempo.Time) -> Bool
Checks if the first time is earlier or equal to the second time.
Example
time.literal("13:22:15")
|> time.is_later_or_equal(to: time.literal("07:42:12"))
// -> True
```gleam
time.literal("13:42")
|> time.is_later_or_equal(to: time.literal("13:42:00.000"))
// -> True
time.literal("13:42:11")
|> time.is_later_or_equal(to: time.literal("13:42:12"))
// -> False
pub fn is_outside(
time: tempo.Time,
start: Boundary,
and end: Boundary,
) -> Bool
Checks if a time is outside of two boundaries.
Example
time.literal("13:42:11")
|> time.is_outside(
time.Boundary(time.literal("05:00:00"), inclusive: True),
and: time.Boundary(time.literal("15:00:00"), inclusive: False),
)
// -> False
pub fn left_in_day(time: tempo.Time) -> tempo.Time
Converts a time to the equivalent time left in the day.
Example
time.literal("23:59:03") |> time.left_in_day
// -> time.literal("00:00:57")
time.literal("08:05:20") |> time.left_in_day
// -> time.literal("15:54:40")
pub fn literal(time: String) -> tempo.Time
Creates a new time value from a string literal, but will panic if
the string is invalid. Accepted formats are
hh:mm:ss.s, hhmmss.s, hh:mm:ss, hhmmss, hh:mm, or hhmm.
Useful for declaring time literals that you know are valid within your program.
Valid times run from 00:00:00 through 23:59:59, plus 24:00:00 as the
end-of-day marker and 23:59:60 as a leap second. Anything else panics,
including a sub-second value finer than microseconds.
Example
case
time.now_local()
|> time.is_later(than: time.literal("11:50:00"))
{
True -> "We are late!"
False -> "No rush :)"
}
pub fn new(
hour: Int,
minute: Int,
second: Int,
) -> Result(tempo.Time, error.TimeOutOfBoundsError)
Creates a new time value with second precision.
Valid times run from 00:00:00 through 23:59:59, plus 24:00:00 as the
end-of-day marker and 23:59:60 as a leap second.
Example
time.new(13, 42, 11)
// -> Ok(time.literal("13:42:11"))
time.new(53, 42, 61)
// -> Error(tempo_error.TimeOutOfBounds)
pub fn new_micro(
hour: Int,
minute: Int,
second: Int,
microsecond: Int,
) -> Result(tempo.Time, error.TimeOutOfBoundsError)
Creates a new time value with microsecond precision. The microsecond component must be between 0 and 999_999, which is the finest precision this package represents.
Valid times run from 00:00:00 through 23:59:59, plus 24:00:00 as the
end-of-day marker and 23:59:60 as a leap second.
Example
time.new_micro(13, 42, 11, 20)
// -> Ok(time.literal("13:42:11.000020"))
time.new_micro(13, 42, 11, 200_000)
// -> Ok(time.litteral("13:42:11.200000"))
time.new_micro(13, 42, 11, 7_500_000)
|> result.map_error(time.describe_out_of_bounds_error)
// -> Error("Subsecond value out of bounds in time: 13:42:11.7500000")
pub fn new_milli(
hour: Int,
minute: Int,
second: Int,
millisecond: Int,
) -> Result(tempo.Time, error.TimeOutOfBoundsError)
Creates a new time value with millisecond precision. The millisecond component must be between 0 and 999.
Valid times run from 00:00:00 through 23:59:59, plus 24:00:00 as the
end-of-day marker and 23:59:60 as a leap second.
Example
time.new_milli(13, 42, 11, 20)
// -> Ok(time.literal("13:42:11.020"))
time.new_milli(13, 42, 11, 200)
// -> Ok(time.literal("13:42:11.200"))
time.new_milli(13, 42, 11, 7_500)
// -> Error(tempo_error.TimeOutOfBounds)
pub fn parse(
str: String,
in format: tempo.TimeFormat,
) -> Result(tempo.Time, error.TimeParseError)
Parses a time string in the provided format. Always prefer using
this over parse_any. All parsed formats must have an hour and a second.
Values can be escaped by putting brackets around them, like “[Hello!] HH”.
Available directives: 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), A (AM/PM), a (am/pm),
Example
time.parse("2024/06/08, 13:42:11", "YYYY/MM/DD")
// -> Ok(time.literal("13:42:11"))
time.parse("January 13, 2024", "MMMM DD, YYYY")
|> result.map_error(time.describe_parse_error)
// -> Error("Invlid time format: January 13, 2024")
time.parse("Hi! 12 2 am", "[Hi!] h m a")
// -> Ok(time.literal("00:02:00"))
pub fn parse_any(
str: String,
) -> Result(tempo.Time, error.TimeParseError)
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. Will leave off any time offset values present.
Example
time.parse_any("2024.06.21 01:32 PM -04:00")
// -> Ok(time.literal("13:32:00"))
time.parse_any("2024.06.21")
// -> Error(tempo.ParseMissingTime)
pub fn since(
time time: tempo.Time,
since since: tempo.Time,
) -> duration.Duration
Returns a duration representing the time since the first time to the given time.
Example
time.literal("23:54:00")
|> time.since(time.literal("13:30:04"))
|> duration.as_hours
// -> 10
time.literal("12:30:54")
|> time.since(time.literal("22:00:00"))
|> duration.as_milliseconds
// -> 0
pub const start_of_day: tempo.Time
The first second of the day.
Example
time.start_of_day
|> time.to_string
// "00:00:00.000000"
tempo.DateTime(date.literal("2024-06-21"), time.start_of_day, offset.utc)
|> datetime.to_string
// "2024-06-21T00:00:00.000000Z"
pub fn subtract(
a: tempo.Time,
duration b: duration.Duration,
) -> tempo.Time
Subtracts a duration from a time.
Example
time.literal("13:42:02")
|> time.subtract(duration.hours(2))
// -> time.literal("11:42:02")
pub fn to_calendar_time_of_day(
time: tempo.Time,
) -> calendar.TimeOfDay
Converts a tempo time to a time of day type in the core gleam time package.
pub fn to_duration(time: tempo.Time) -> duration.Duration
Converts a time to duration, assuming the duration epoch is “00:00:00”.
Example
time.literal("00:00:00.000300")
|> time.to_duration
|> duration.as_microseconds
// -> 300
time.literal("00:03:06")
|> time.to_duration
|> duration.as_milliseconds
// -> 186_000
pub fn to_string(time: tempo.Time) -> String
Converts a time value to a string in the format hh:mm:ss.s with
millisecond precision. If a different precision is needed, use the format
function.
Example
time.to_string(my_time)
// -> "21:53:03.534"
pub fn to_tuple(time: tempo.Time) -> #(Int, Int, Int)
Returns a time value as a tuple of hours, minutes, and seconds. Useful for using with another time library.
Example
time.literal("13:42:11")
|> time.to_tuple
// -> #(13, 42, 11)
pub fn to_tuple_microsecond(
time: tempo.Time,
) -> #(Int, Int, Int, Int)
Returns a time value as a tuple of hours, minutes, seconds, and microseconds. Useful for using with another time library.
Example
time.literal("13:42:11.872")
|> time.to_tuple_microsecond
// -> #(13, 42, 11, 872000)
pub fn until(
time: tempo.Time,
until: tempo.Time,
) -> duration.Duration
Returns a duration representing the time left from the first time until a given time.
Example
time.literal("23:54:00")
|> time.until(time.literal("23:59:04"))
|> duration.as_seconds
// -> 304
time.literal("23:59:03")
|> time.until(time.literal("22:00:00"))
|> duration.as_milliseconds
// -> 0