kex docs Standard Library

Time

Calendar dates, wall-clock times, and instants.

Three civil types, each a plain record:

Date      a calendar day, no time and no zone       (2026-07-30)
Time      a time of day, no date and no zone        (14:03:00)
DateTime  both, plus a fixed offset from UTC        (2026-07-30T14:03:00+02:00)

Two span types connect them, and which one you want depends on whether the calendar gets a say:

Duration  fixed elapsed time, a count of seconds   (36.hours, 10.days)
Period    a calendar step, resolved by the calendar (1.months, 2.years)

36.hours is always 129600 seconds; 1.months is however long that particular month turns out to be. So date + 1.months clamps January 31st to the last day of February, while date + 30.days counts thirty days.

A time Measure such as 5.second is a third thing and deliberately NOT a Duration: a Measure describes a measurement, a Duration describes elapsed time. The plural 5.seconds builds the Duration.

Values are built through their own module and used through functions:

let due = Date.of(2026, 7, 30).try           # Date
due.weekday.name                             # "Thursday"
(due + 10.days).iso                          # "2026-08-09"
(due + 1.months).iso                         # "2026-08-30"
Time.now().iso                            # e.g. "14:03:00"

Anything that reads the clock is replaceable: Time.Clock is a capability, and with Time.Clock = Mock.Clock { at: ... } pins it for a block.

Zones are fixed offsets: UTC, an explicit +02:00, or whatever this machine's zone resolves to at a given instant. Named IANA zones and their DST rules are not modeled: Time.now() asks the host for the offset in effect at that moment, so it is right now, but it cannot say what the offset WILL be for some future local time.

The records and the two ADTs stay at file level so make blocks, callers, and every module here can see them.

type Weekday

Weekday names in ISO order (Monday is day 1).

Variants

  • Monday
  • Tuesday
  • Wednesday
  • Thursday
  • Friday
  • Saturday
  • Sunday

type TimeError

A field out of range, or text that is not a date/time.

Variants

  • InvalidDate(Integer, Integer, Integer)
  • InvalidTime(Integer, Integer, Integer)
  • InvalidFormat(String)

record Date

A calendar day: a year, a month and a day, with no time and no zone.

let due = Date.of(2026, 7, 30).try
due.iso              # => "2026-07-30"
due.weekday.name     # => "Thursday"
(due + 10.days).iso  # => "2026-08-09"

Build one with Date.of, which validates, rather than with the record literal, which does not.

Fields

month
Integer

Implements Inspectable, Showable.

Functions

inspectValue Inspectable

inspectValue(colors: Bool) -> String

Renders the date structurally, for debugging output.

Parameters
colors
whether to include ANSI color escapes

Returns the rendered date

showValue Showable

showValue : String

Renders the date in ISO form, so interpolation and IO.printLine show 2026-07-30.

Returns the ISO 8601 date

Examples
"due ${Date.of(2026, 7, 30).try}"   # => "due 2026-07-30"

epochDay

epochDay : Integer

Days since 1970-01-01, negative before it.

The date's identity as a number: comparison and day arithmetic go through it.

Returns days since the Unix epoch

Examples
Date.of(1970, 1, 1).try.epochDay    # => 0
Date.of(2026, 7, 30).try.epochDay   # => 20664

weekday

weekday : Weekday

The day of the week this date falls on.

Returns the day of the week

Examples
Date.of(2026, 7, 30).try.weekday        # => Thursday
Date.of(2026, 7, 30).try.weekday.name   # => "Thursday"

dayOfYear

dayOfYear : Integer

The day's position in its year, counting from 1 on January 1st.

Returns the day of the year

Examples
Date.of(2026, 1, 1).try.dayOfYear    # => 1
Date.of(2026, 7, 30).try.dayOfYear   # => 211

leapYear?

leapYear? : Bool

Returns true when this date falls in a leap year.

Returns true in a leap year

Examples
Date.of(2024, 1, 1).try.leapYear?   # => true
Date.of(2026, 1, 1).try.leapYear?   # => false

daysInMonth

daysInMonth : Integer

The number of days in THIS date's month.

No Result: a Date's month is in range by construction, unlike Time.daysInMonth's two loose integers.

Returns the length of this month

Examples
Date.of(2024, 2, 1).try.daysInMonth   # => 29
Date.of(2026, 7, 1).try.daysInMonth   # => 31

+

+(span: Duration) -> Date

Advances the date by a fixed span, whole days only.

A Duration with a sub-day remainder truncates toward zero, so +date + 36.hours+ advances exactly one day. Use a Period when the calendar should get a say.

Parameters
span
the elapsed span to add

Returns the later date

Examples
(Date.of(2026, 7, 30).try + 10.days).iso   # => "2026-08-09"
(Date.of(2026, 7, 30).try + 36.hours).iso  # => "2026-07-31"

-

-(span: Duration) -> Date

Moves the date back by a fixed span, whole days only.

Parameters
span
the elapsed span to subtract

Returns the earlier date

Examples
(Date.of(2026, 7, 30).try - 10.days).iso   # => "2026-07-20"

addDays

addDays(count: Integer) -> Date

The date count days later. A negative count moves backwards.

Parameters
count
how many days to add

Returns the shifted date

Examples
Date.of(2026, 7, 30).try.addDays(1).iso    # => "2026-07-31"
Date.of(2026, 7, 30).try.addDays(-1).iso   # => "2026-07-29"

addWeeks

addWeeks(count: Integer) -> Date

The date count weeks later. A negative count moves backwards.

Parameters
count
how many weeks to add

Returns the shifted date

Examples
Date.of(2026, 7, 30).try.addWeeks(2).iso   # => "2026-08-13"

addMonths

addMonths(count: Integer) -> Date

No date.tomorrow/date.yesterday functions: on BEAM a make-block function flattens onto the same name as the Date.tomorrow()/Date.yesterday() module functions above and one of the two has to win. The module functions win: Date.tomorrow() is the spelling people reach for, and date.addDays(1) already says the rest.

The date count calendar months later, with the day clamped into the target month.

One month after January 31st is the last day of February, not March 3rd. A negative count moves backwards.

Parameters
count
how many months to add

Returns the shifted date

Examples
Date.of(2026, 1, 31).try.addMonths(1).iso    # => "2026-02-28"
Date.of(2026, 7, 30).try.addMonths(-1).iso   # => "2026-06-30"

addYears

addYears(count: Integer) -> Date

The date count calendar years later, with the day clamped: February 29th plus one year is February 28th.

Parameters
count
how many years to add

Returns the shifted date

Examples
Date.of(2024, 2, 29).try.addYears(1).iso   # => "2025-02-28"

startOfMonth

startOfMonth : Date

The first day of this date's month.

Returns the first of the month

Examples
Date.of(2026, 7, 30).try.startOfMonth.iso   # => "2026-07-01"

endOfMonth

endOfMonth : Date

The last day of this date's month, whatever its length.

Returns the last of the month

Examples
Date.of(2026, 7, 30).try.endOfMonth.iso   # => "2026-07-31"
Date.of(2024, 2, 1).try.endOfMonth.iso    # => "2024-02-29"

startOfYear

startOfYear : Date

January 1st of this date's year.

Returns the first of the year

Examples
Date.of(2026, 7, 30).try.startOfYear.iso   # => "2026-01-01"

endOfYear

endOfYear : Date

December 31st of this date's year.

Returns the last of the year

Examples
Date.of(2026, 7, 30).try.endOfYear.iso   # => "2026-12-31"

startOfWeek

startOfWeek : Date

The Monday of this date's week.

The week runs Monday to Sunday, matching the ISO weekday numbering weekday.number reports.

Returns the Monday of this week

Examples
Date.of(2026, 7, 30).try.startOfWeek.iso   # => "2026-07-27"

endOfWeek

endOfWeek : Date

The Sunday of this date's week.

Returns the Sunday of this week

Examples
Date.of(2026, 7, 30).try.endOfWeek.iso   # => "2026-08-02"

daysUntil

daysUntil(other: Date) -> Integer

Whole days from this date to other, negative when other is earlier.

Parameters
other
the date to measure to

Returns the number of days

Examples
Date.of(2026, 7, 30).try.daysUntil(Date.of(2026, 8, 9).try)   # => 10
Date.of(2026, 8, 9).try.daysUntil(Date.of(2026, 7, 30).try)   # => -10

until

until(other: Date) -> Duration

The span from this date to other, as a Duration of whole days.

Parameters
other
the date to measure to

Returns the elapsed span

Examples
Date.of(2026, 7, 30).try.until(Date.of(2026, 8, 9).try).wholeDays   # => 10

monthsUntil

monthsUntil(other: Date) -> Integer

Whole calendar months from this date to other, negative when other is earlier.

Truncated, not rounded: a partial month does not count, so January 15th to February 14th is 0 months.

The count is the exact inverse of addMonths, which is why the correction below asks addMonths rather than comparing day-of-month fields: January 31st plus one month IS February 28th, so January 31st to February 28th is one month, even though 28 < 31. Comparing the day fields answers 0 there and contradicts the addition this same file performs.

Parameters
other
the date to measure to

Returns the number of whole months

Examples
Date.of(2026, 1, 15).try.monthsUntil(Date.of(2026, 2, 14).try)   # => 0
Date.of(2026, 1, 31).try.monthsUntil(Date.of(2026, 2, 28).try)   # => 1

yearsUntil

yearsUntil(other: Date) -> Integer

Whole calendar years from this date to other, negative when other is earlier. Truncated, like monthsUntil.

This is how to compute an age.

Parameters
other
the date to measure to

Returns the number of whole years

Examples
Date.of(2020, 1, 1).try.yearsUntil(Date.of(2026, 7, 30).try)   # => 6

Someone's age today

born.yearsUntil(Date.today())

before?

before?(other: Date) -> Bool

Returns true when this date is earlier than other.

Parameters
other
the date to compare against

Returns true when this date comes first

Examples
Date.of(2026, 7, 30).try.before?(Date.of(2026, 8, 1).try)   # => true

after?

after?(other: Date) -> Bool

Returns true when this date is later than other.

Parameters
other
the date to compare against

Returns true when this date comes second

Examples
Date.of(2026, 8, 1).try.after?(Date.of(2026, 7, 30).try)   # => true

compareTo

compareTo(other: Date) -> Ordering

Orders this date against another.

Parameters
other
the date to compare against

Returns Less, Equal or Greater

Examples
Date.of(2026, 7, 30).try.compareTo(Date.of(2026, 8, 1).try)   # => Less

Sorting dates

dates.sort { |a, b| a.compareTo(b) == Less }

iso

iso : String

The date as ISO 8601 text, 2026-07-30.

Returns the ISO 8601 date

Examples
Date.of(2026, 7, 30).try.iso   # => "2026-07-30"

at

at(time: Time, offset: Duration) -> DateTime

This date at a given time of day and offset, as a DateTime.

Parameters
time
the time of day
offset
the offset from UTC

Returns the instant

Examples
Date.of(2026, 7, 30).try.at(Time.of(9, 0, 0).try, Duration.zero()).iso
# => "2026-07-30T09:00:00Z"

From Showable

record Time

A time of day, with no date and no zone.

let t = Time.of(14, 3, 0).try
t.iso                  # => "14:03:00"
(t + 2.hours).iso      # => "16:03:00"

Arithmetic wraps within the day: there is no date to carry into. Reach for DateTime when the day rolling over matters.

Fields

minute
Integer
second
Integer
nanosecond
Integer

Implements Inspectable, Showable.

Functions

inspectValue Inspectable

inspectValue(colors: Bool) -> String

Renders the time structurally, for debugging output.

Parameters
colors
whether to include ANSI color escapes

Returns the rendered time

showValue Showable

showValue : String

Renders the time in ISO form, so interpolation and IO.printLine show 14:03:00.

Returns the ISO 8601 time

Examples
"starts at ${Time.of(14, 3, 0).try}"   # => "starts at 14:03:00"

secondsSinceMidnight

secondsSinceMidnight : Integer

Seconds from midnight to this time of day, ignoring the nanosecond field.

Returns seconds since midnight

Examples
Time.of(14, 3, 0).try.secondsSinceMidnight   # => 50580

before?

before?(other: Time) -> Bool

Returns true when this time of day is earlier than other.

Parameters
other
the time to compare against

Returns true when this time comes first

Examples
Time.of(14, 3, 0).try.before?(Time.of(15, 0, 0).try)   # => true

after?

after?(other: Time) -> Bool

Returns true when this time of day is later than other.

Parameters
other
the time to compare against

Returns true when this time comes second

Examples
Time.of(15, 0, 0).try.after?(Time.of(14, 3, 0).try)   # => true

+

+(span: Duration) -> Time

Advances the time of day by a span, wrapping within the day.

A Time has no date to carry into, so 23:00 + 2.hours is 01:00. Reach for DateTime when the day rolling over is something you need to see.

The nanosecond field rides along untouched: wholeSeconds truncates the span, so a sub-second Duration shifts nothing.

Parameters
span
the elapsed span to add

Returns the later time of day

Examples
(Time.of(14, 3, 0).try + 2.hours).iso    # => "16:03:00"
(Time.of(23, 0, 0).try + 2.hours).iso    # => "01:00:00"

-

-(span: Duration) -> Time

Moves the time of day back by a span, wrapping within the day.

Parameters
span
the elapsed span to subtract

Returns the earlier time of day

Examples
(Time.midnight() - 1.seconds).iso   # => "23:59:59"

addSeconds

addSeconds(count: Integer) -> Time

The time of day count seconds later, wrapping within the day.

Parameters
count
how many seconds to add

Returns the shifted time of day

Examples
Time.of(14, 3, 0).try.addSeconds(60).iso   # => "14:04:00"

addMinutes

addMinutes(count: Integer) -> Time

The time of day count minutes later, wrapping within the day.

Parameters
count
how many minutes to add

Returns the shifted time of day

Examples
Time.of(14, 3, 0).try.addMinutes(30).iso   # => "14:33:00"

addHours

addHours(count: Integer) -> Time

The time of day count hours later, wrapping within the day.

Parameters
count
how many hours to add

Returns the shifted time of day

Examples
Time.of(23, 0, 0).try.addHours(2).iso   # => "01:00:00"

until

until(other: Time) -> Duration

Elapsed time from this time of day to other, within the same day.

Negative when other is earlier. Sub-second precision is kept.

Parameters
other
the time to measure to

Returns the elapsed span

Examples
Time.of(14, 3, 0).try.until(Time.of(15, 3, 0).try).wholeMinutes   # => 60

iso

iso : String

The time of day as ISO 8601 text, with fractional seconds only when they are non-zero.

Returns the ISO 8601 time

Examples
Time.of(14, 3, 0).try.iso              # => "14:03:00"
Time.of(14, 3, 0, 123456789).try.iso   # => "14:03:00.123456789"

compareTo

compareTo(other: Time) -> Ordering

Orders this time of day against another, nanoseconds included.

Parameters
other
the time to compare against

Returns Less, Equal or Greater

Examples
Time.of(14, 3, 0).try.compareTo(Time.of(15, 0, 0).try)   # => Less

From Showable

record DateTime

An instant: a calendar date, a time of day, and a fixed offset from UTC.

let m = DateTime.parse("2026-07-30T14:03:00+02:00").try
m.iso        # => "2026-07-30T14:03:00+02:00"
m.utc.iso    # => "2026-07-30T12:03:00Z"

Two DateTime values that name the same instant compare equal whatever offsets they are written at: comparison goes through epochSeconds.

Fields

date
Date
time
Time
offset
Duration

Implements Inspectable, Showable.

Functions

inspectValue Inspectable

inspectValue(colors: Bool) -> String

Renders the instant structurally, for debugging output.

Parameters
colors
whether to include ANSI color escapes

Returns the rendered instant

showValue Showable

showValue : String

Renders the instant in ISO form, so interpolation and IO.printLine show +2026-07-30T14:03:00+02:00+.

Returns the ISO 8601 instant

Examples
"logged at ${DateTime.utcNow()}"   # => "logged at 2026-07-30T12:03:00Z"

epochSeconds

epochSeconds : Integer

Seconds since the Unix epoch.

This is the value to compare and subtract: it identifies the instant, independent of the offset rendering it, so 12:00Z and 14:00+02:00 have the same one.

Returns seconds since the Unix epoch

Examples
DateTime.parse("1970-01-01T00:00:00Z").try.epochSeconds   # => 0

Storing an instant as a number

FS.File.write("stamp", "${DateTime.utcNow().epochSeconds}")

weekday

weekday : Weekday

The day of the week this instant falls on, at its own offset.

No year/hour/... shorthands here: a function named after a record field makes value.year inside a module function dispatch to it on BEAM and fail with function_clause. Reach through .date and .time.

Returns the day of the week

Examples
DateTime.parse("2026-07-30T14:03:00Z").try.weekday.name   # => "Thursday"

at

at(offset: Duration) -> DateTime

The same instant, rendered at another offset.

Nothing moves: the wall clock changes because the offset does, and epochSeconds is unchanged.

Parameters
offset
the offset to render at

Returns the same instant, at that offset

Examples
DateTime.parse("2026-07-30T14:03:00+02:00").try
  .at(Duration.utcOffset(-5, 0)).iso
# => "2026-07-30T07:03:00-05:00"

utc

utc : DateTime

The same instant, rendered at UTC.

The form to store, compare and transmit: there is no offset to disagree about.

Returns the same instant, at UTC

Examples
DateTime.parse("2026-07-30T14:03:00+02:00").try.utc.iso
# => "2026-07-30T12:03:00Z"

+

+(span: Duration) -> DateTime

Advances the instant by a fixed span, keeping its offset.

Parameters
span
the elapsed span to add

Returns the later instant

Examples
(DateTime.parse("2026-07-30T14:03:00Z").try + 90.minutes).iso
# => "2026-07-30T15:33:00Z"

-

-(span: Duration) -> DateTime

Moves the instant back by a fixed span, keeping its offset.

Parameters
span
the elapsed span to subtract

Returns the earlier instant

Examples
(DateTime.utcNow() - 24.hours).iso   # yesterday, same wall clock

addDays

addDays(count: Integer) -> DateTime

The instant count days later, keeping the wall clock and the offset.

Parameters
count
how many days to add

Returns the shifted instant

Examples
DateTime.parse("2026-07-30T14:03:00Z").try.addDays(1).iso
# => "2026-07-31T14:03:00Z"

addWeeks

addWeeks(count: Integer) -> DateTime

The instant count weeks later, keeping the wall clock and the offset.

Parameters
count
how many weeks to add

Returns the shifted instant

Examples
DateTime.parse("2026-07-30T14:03:00Z").try.addWeeks(1).iso
# => "2026-08-06T14:03:00Z"

addMonths

addMonths(count: Integer) -> DateTime

The instant count calendar months later, with the day clamped into the target month.

Parameters
count
how many months to add

Returns the shifted instant

Examples
DateTime.parse("2026-01-31T09:00:00Z").try.addMonths(1).iso
# => "2026-02-28T09:00:00Z"

addYears

addYears(count: Integer) -> DateTime

The instant count calendar years later, with the day clamped.

Parameters
count
how many years to add

Returns the shifted instant

Examples
DateTime.parse("2024-02-29T09:00:00Z").try.addYears(1).iso
# => "2025-02-28T09:00:00Z"

until

until(other: DateTime) -> Duration

Elapsed time from this instant to other, negative when other is earlier. Sub-second precision is kept.

Parameters
other
the instant to measure to

Returns the elapsed span

Examples
started.until(DateTime.utcNow()).wholeSeconds

Enforcing a deadline

if started.until(DateTime.utcNow()).longerThan?(30.seconds)
  IO.printError("timed out")
end

before?

before?(other: DateTime) -> Bool

Returns true when this instant is earlier than other, whatever offsets they are written at.

Parameters
other
the instant to compare against

Returns true when this instant comes first

Examples
DateTime.parse("2026-07-30T14:03:00+02:00").try
  .before?(DateTime.parse("2026-07-31T00:00:00Z").try)
# => true

after?

after?(other: DateTime) -> Bool

Returns true when this instant is later than other.

Parameters
other
the instant to compare against

Returns true when this instant comes second

Examples
DateTime.utcNow().after?(started)   # => true

iso

iso : String

The instant as ISO 8601 text, +2026-07-30T14:03:00+02:00+.

Returns the ISO 8601 instant

Examples
DateTime.parse("2026-07-30T14:03:00+02:00").try.iso
# => "2026-07-30T14:03:00+02:00"

compareTo

compareTo(other: DateTime) -> Ordering

Orders this instant against another, by instant rather than by wall clock, so 12:00Z and 14:00+02:00 compare Equal.

Named compareTo rather than compare: a make-block compare is shadowed by the builtin comparison dispatch and fails at runtime on both backends.

Parameters
other
the instant to compare against

Returns Less, Equal or Greater

Examples
DateTime.parse("2026-07-30T12:00:00Z").try
  .compareTo(DateTime.parse("2026-07-30T14:00:00+02:00").try)
# => Equal

Sorting events by when they happened

events.sort { |a, b| a.at.compareTo(b.at) == Less }

From Showable

record Period

A calendar span. Months and years have no fixed length: February is 28 days or 29, a year 365 or 366, so they cannot live in a Duration, which is a count of seconds and nothing else. A Period carries the calendar fields themselves and lets the calendar resolve them:

Date.of(2026, 1, 31).try + 1.months        # 2026-02-28, not 2026-03-03
Date.of(2024, 2, 29).try + 1.years         # 2025-02-28

Use a Duration for elapsed time (36.hours is always 129600 seconds) and a Period for calendar steps (1.months is however long that month is).

Fields

years
Integer
months
Integer

Implements Inspectable, Showable.

Functions

inspectValue Inspectable

inspectValue(colors: Bool) -> String

Renders the period structurally, for debugging output.

Parameters
colors
whether to include ANSI color escapes

Returns the rendered period

showValue Showable

showValue : String

Renders the period as its ISO 8601 form, so interpolation and IO.printLine show P1Y2M3D.

Returns the ISO 8601 duration

Examples
"due in ${Period.of(0, 1, 0)}"   # => "due in P1M"

+

+(other: Period) -> Period

Adds two calendar spans, field by field.

Parameters
other
the span to add

Returns the total

Examples
(Period.of(1, 2, 3) + 1.years).iso   # => "P2Y2M3D"

-

-(other: Period) -> Period

Subtracts a calendar span, field by field. Fields may go negative.

Parameters
other
the span to subtract

Returns the difference

Examples
(Period.of(1, 2, 3) - 1.years).iso   # => "P2M3D"

negated

negated : Period

The same span with every field's sign flipped: a step backwards.

Returns the negated span

Examples
Period.of(1, 2, 3).negated.iso   # => "P-1Y-2M-3D"

*

*(factor: Integer) -> Period

Multiplies every field by factor.

Parameters
factor
the factor to multiply by

Returns the scaled span

Examples
(Period.of(1, 2, 3) * 2).iso   # => "P2Y4M6D"

zero?

zero? : Bool

Returns true when every field is zero.

Returns true for a zero span

Examples
Period.zero().zero?          # => true
Period.of(0, 1, 0).zero?     # => false

totalMonths

totalMonths : Integer

The years and months of the span, as one count of months.

Years and months are one quantity for arithmetic: a month is 1/12 of a year exactly, unlike days, which are not a fixed fraction of a month. This is why date + span applies them together rather than one after the other.

Returns the total months, ignoring days

Examples
Period.of(1, 2, 3).totalMonths   # => 14

normalized

normalized : Period

The same span with excess months carried into years.

14 months becomes 1 year 2 months. Days are left alone: there is no fixed number of them in a month to carry by.

Returns the normalised span

Examples
14.months.normalized.iso   # => "P1Y2M"

iso

iso : String

The span as an ISO 8601 duration, P1Y2M3D.

A zero period is P0D, the shortest spelling the grammar allows: P alone is not valid.

Returns the ISO 8601 duration

Examples
Period.of(1, 2, 3).iso   # => "P1Y2M3D"
Period.zero().iso        # => "P0D"
2.months.iso             # => "P2M"

From Showable

module Time

Building times of day, controlling the clock in tests, and the calendar arithmetic the rest of this file is written on.

of

of(hour: Integer, minute: Integer, second: Integer, nanosecond: Integer) -> Result<Time, TimeError>

Builds a validated time of day.

Every field is range-checked, so a Time you hold is always a real time. Leap seconds are not modeled, so a second of 60 is rejected.

Parameters
hour
the hour, 0 through 23
minute
the minute, 0 through 59
second
the second, 0 through 59
nanosecond
the sub-second part, 0 through 999999999; omitted for 0

Returns the time, or why it is not one

Examples
Time.of(14, 3, 0).map { |t| t.iso }   # => Ok("14:03:00")
Time.of(25, 0, 0)                     # => Error(InvalidTime(25, 0, 0))

With sub-second precision

Time.of(14, 3, 0, 123456789).map { |t| t.iso }
# => Ok("14:03:00.123456789")

midnight

midnight : Time

Midnight, 00:00:00. The start of a day.

Returns 00:00:00

Examples
Time.midnight().iso                   # => "00:00:00"
(Time.midnight() - 1.seconds).iso     # => "23:59:59"

fromSecondsSinceMidnight

fromSecondsSinceMidnight(count: Integer, nanosecond: Integer) -> Time

Builds a time of day from a count of seconds since midnight.

Wraps, so 86400 is midnight again and -1 is 23:59:59, which is what makes it total where Time.of is fallible.

Declared before the two-argument form: the interpreter resolves an overloaded module function to its LAST definition regardless of arity, so a delegating overload has to come first or it recurses into itself.

Parameters
count
seconds since midnight; wraps outside 0..86399
nanosecond
the sub-second part; omitted for 0

Returns the time of day

Examples
Time.fromSecondsSinceMidnight(50580).iso   # => "14:03:00"
Time.fromSecondsSinceMidnight(86400).iso   # => "00:00:00"
Time.fromSecondsSinceMidnight(-1).iso      # => "23:59:59"

parse

parse(text: String) -> Result<Time, TimeError>

Parses an ISO 8601 time of day.

Accepts 14:03, 14:03:00, or 14:03:00.123456789. Anything else is InvalidFormat.

Parameters
text
the text to parse

Returns the time, or why it could not be read

Examples
Time.parse("14:03").map { |t| t.iso }   # => Ok("14:03:00")
Time.parse("xx")                        # => Error(InvalidFormat("xx"))

now

now : Time

The current time of day, in this machine's zone.

Reads Time.Clock like everything else here does, so it is pinned by replacing that capability in a test.

Returns the current time of day

Examples
Time.now().iso   # => "14:03:00"

utcNow

utcNow : Time

The current time of day in UTC, whatever this machine's zone is.

Returns the current UTC time of day

Examples
Time.utcNow().iso   # => "12:03:00"

parseOffset

parseOffset(text: String) -> Result<Duration, TimeError>

Parses an ISO 8601 zone designator into an offset.

Accepts Z, +02:00, -05:30, or the empty string (all meaning UTC for the first and last).

Parameters
text
the zone designator

Returns the offset, or why it could not be read

Examples
Time.parseOffset("+02:00").map { |d| d.wholeSeconds }   # => Ok(7200)
Time.parseOffset("Z").map { |d| d.wholeSeconds }        # => Ok(0)

momentAt

momentAt(nanos: Integer) -> DateTime

The civil instant nanos after the Unix epoch, in this machine's zone as it stands at that instant. Foul because the zone is ambient.

Parameters
nanos
nanoseconds since the Unix epoch

Returns that instant, in the local zone

nanosOf

nanosOf(moment: DateTime) -> Integer

Nanoseconds since the Unix epoch for a civil datetime. A plain function rather than a DateTime function: on BEAM a function named epochNanos flattens onto the same name as the DateTime.epochNanos() module function, and the arity-0 one wins: silently, answering for the host clock instead of for moment.

leapYear?

leapYear?(year: Integer) -> Bool

Public because the make blocks below live at file level and reach them by qualification; they are equally useful on their own.

Returns true when year is a leap year in the proleptic Gregorian calendar.

Parameters
year
the calendar year

Returns true for a leap year

Examples
Time.leapYear?(2024)   # => true
Time.leapYear?(1900)   # => false
Time.leapYear?(2000)   # => true

daysInMonth

daysInMonth(year: Integer, month: Integer) -> Result<Integer, TimeError>

The number of days in a month.

Year first, matching Date.of(year, month, day) and every other date-shaped signature in this file.

Returns Error(InvalidDate(year, month, 1)) when the month is outside 1 through 12.

Parameters
year
the calendar year, which decides February's length
month
the month, 1 through 12

Returns the day count, or why the month is not one

Examples
Time.daysInMonth(2024, 2)   # => Ok(29)
Time.daysInMonth(2023, 2)   # => Ok(28)
Time.daysInMonth(2026, 13)  # => Error(InvalidDate(2026, 13, 1))

daysInValidMonth

daysInValidMonth(year: Integer, month: Integer) -> Integer

The number of days in a month, with the range check already done.

Every caller inside this file has a month it built or validated itself. Use daysInMonth for a month that came from outside.

Parameters
year
the calendar year
month
the month, assumed to be 1 through 12

Returns the day count

Examples
Time.daysInValidMonth(2024, 2)   # => 29

daysFromCivil

daysFromCivil(year: Integer, month: Integer, day: Integer) -> Integer

The number of days from 1970-01-01 to a calendar date, negative before it.

Howard Hinnant's civil-calendar algorithms: exact across the whole proleptic Gregorian range, and they need only truncating integer division: the semantics Kex's / already has.

Parameters
year
the calendar year
month
the month, 1 through 12
day
the day of the month

Returns days since the Unix epoch

Examples
Time.daysFromCivil(1970, 1, 1)   # => 0
Time.daysFromCivil(2026, 7, 30)  # => 20664

civilFromDays

civilFromDays(epochDay: Integer) -> Date

The calendar date a count of days since 1970-01-01 lands on. The inverse of daysFromCivil.

Parameters
epochDay
days since the Unix epoch

Returns the calendar date

Examples
Time.civilFromDays(0).iso       # => "1970-01-01"
Time.civilFromDays(20664).iso   # => "2026-07-30"

weekdayFromEpochDay

weekdayFromEpochDay(epochDay: Integer) -> Weekday

The weekday a count of days since 1970-01-01 falls on.

Parameters
epochDay
days since the Unix epoch

Returns the day of the week

Examples
Time.weekdayFromEpochDay(0)   # => Thursday   (1970-01-01 was a Thursday)

weekdayNumber

weekdayNumber(weekday: Weekday) -> Integer

The ISO number of a weekday: Monday is 1, Sunday is 7.

weekday.number is the readable way to ask.

Parameters
weekday
the day of the week

Returns its ISO number, 1 through 7

Examples
Time.weekdayNumber(Monday)   # => 1

weekdayName

weekdayName(weekday: Weekday) -> String

The English name of a weekday.

weekday.name is the readable way to ask.

Parameters
weekday
the day of the week

Returns its English name

Examples
Time.weekdayName(Sunday)   # => "Sunday"

errorMessage

errorMessage(error: TimeError) -> String

Renders a TimeError as a sentence for the user.

A plain function rather than an Errorable implementation: a message function here joins the same BEAM dispatcher as ParseError's message FIELD and breaks it (spec/record_field_method_collision.kex).

Parameters
error
the failure to describe

Returns the message to show

Examples
Time.errorMessage(InvalidDate(2026, 13, 1))
# => "not a calendar date: 2026-13-1"

Reporting a rejected date

match Date.parse(input) do
  Ok(date) => IO.printLine(date.iso)
  Error(e) => IO.printError(Time.errorMessage(e))
end

formatDate

formatDate(value: Date) -> String

formatDateTime

formatDateTime(value: DateTime) -> String

formatTime

formatTime(value: Time) -> String

formatFraction

formatFraction(nanosecond: Integer) -> String

Fractional seconds, in the 3/6/9-digit groupings ISO 8601 output conventionally uses, whichever is the shortest that loses nothing. A whole second renders no fraction at all, so 14:03:00 is unchanged.

Without this the nanosecond field was kept on the value and compared, but never rendered: Time.parse("14:03:00.5") and Time.parse("14:03:00") produced different values that printed identically, and every parse/format round-trip silently dropped sub-second precision.

formatOffset

formatOffset(offset: Duration) -> String

±HH:MM, the shape an ISO 8601 offset takes. UTC renders as "Z".

withNanosecond

withNanosecond(moment: DateTime, nanosecond: Integer) -> DateTime

floorDiv

floorDiv(value: Integer, divisor: Integer) -> Integer

Kex's / truncates toward zero; instants before the epoch need the floor.

truncatedBy

truncatedBy(seconds: Float, unit: Float) -> Integer

pad2

pad2(value: Integer) -> String

padTo

padTo(value: Integer, width: Integer) -> String

Left-pad with zeros to a fixed width. A value already that wide is left alone rather than truncated: losing digits would be worse than a field one character too long.

padYear

padYear(value: Integer) -> String

Years keep four digits where they fit; ISO 8601 has no fixed spelling beyond that, so wider years render as-is.

digitsIn

digitsIn(fragment: String, whole: String) -> Result<Integer, TimeError>

Digits, with the failure reported against the WHOLE input rather than the fragment that failed: Time.parse("2:03 pm") should complain about "2:03 pm", not about "03 pm".

digitsToInteger

digitsToInteger(text: String) -> Result<Integer, TimeError>

parseFraction

parseFraction(text: String, whole: String) -> Result<Integer, TimeError>

".5" is 500000000ns: the digits are padded out to nanosecond scale.

splitOffset

splitOffset(text: String) -> [String]

Splits "14:03:00+02:00" into its time and offset halves. A missing offset reads as UTC, matching what a zero offset formats back to.

module Time.Clock

The clock: the one place the current instant comes from.

Anything that asks what time it is (Time.now, Date.today, DateTime.utcNow) reads this, so replacing it replaces the whole calendar. It is a capability, so the replacement is for a lexical region: it reaches every call made from the region and every process spawned in it, it nests, and it cannot outlive its block. This is what makes code that calls Date.today() testable:

with Time.Clock = Mock.Clock { at: DateTime.parse("2026-07-30T14:03:00Z").try } do
  Date.today().iso   # "2026-07-30", always
end

now is the whole interface. Its answer carries the zone as its offset, so one member covers both ambient inputs: what time it is, and where. Mock.Clock is the ready-made stand-in; any record that implements now is one too.

now

now : DateTime

The current instant, in this machine's zone as it stands right now.

Returns the current instant

type Weekday

number

number : Integer

This weekday's ISO number: Monday is 1, Sunday is 7.

Returns the ISO number, 1 through 7

Examples
Monday.number   # => 1
Sunday.number   # => 7

name

name : String

This weekday's English name.

Returns the name

Examples
Date.of(2026, 7, 30).try.weekday.name   # => "Thursday"

weekend?

weekend? : Bool

Returns true for Saturday and Sunday.

Returns true on a weekend day

Examples
Saturday.weekend?   # => true
Monday.weekend?     # => false

Counting working days in a range

days.count { |d| !d.weekday.weekend? }

module Date

Building calendar dates, and asking what today is.

of

of(year: Integer, month: Integer, day: Integer) -> Result<Date, TimeError>

Builds a validated calendar date.

The month and the day are both range-checked, and the day is checked against that month's actual length, so February 30th is an Error, and a Date you hold is always a real day. The record literal Date { ... } bypasses this, so prefer it for anything derived from input.

Parameters
year
the calendar year
month
the month, 1 through 12
day
the day of the month

Returns the date, or why it is not one

Examples
Date.of(2026, 7, 30).map { |d| d.iso }   # => Ok("2026-07-30")
Date.of(2026, 13, 1)                     # => Error(InvalidDate(2026, 13, 1))
Date.of(2023, 2, 29)                     # => Error(InvalidDate(2023, 2, 29))

Taking the date or failing loudly

let due = Date.of(year, month, day).try

fromEpochDay

fromEpochDay(day: Integer) -> Date

The calendar date a count of days since 1970-01-01 lands on, negative before it.

Parameters
day
days since the Unix epoch

Returns the calendar date

Examples
Date.fromEpochDay(0).iso       # => "1970-01-01"
Date.fromEpochDay(20664).iso   # => "2026-07-30"

parse

parse(text: String) -> Result<Date, TimeError>

Parses an ISO 8601 calendar date, 2026-07-30.

The result is validated as well as parsed, so a well-formed but impossible date is InvalidDate rather than InvalidFormat.

Parameters
text
the text to parse

Returns the date, or why it could not be read

Examples
Date.parse("2026-07-30").map { |d| d.iso }   # => Ok("2026-07-30")
Date.parse("nope")                           # => Error(InvalidFormat("nope"))
Date.parse("2026-02-30")                     # => Error(InvalidDate(2026, 2, 30))

now

now : Date

Today's date, in this machine's zone.

Date.today() reads better in most code; now exists so every type in this file answers the same question the same way.

Returns today's date

Examples
Date.now().iso   # => "2026-07-30"

today

today : Date

Today's date, in this machine's zone.

Pinned by replacing Time.Clock in a test, like everything else that reads the clock.

Returns today's date

Examples
Date.today().iso   # => "2026-07-30"

Testing code that depends on today

with Time.Clock = Mock.Clock { at: DateTime.parse("2026-07-30T00:00:00Z").try } do
  Assert.equal(Date.today().iso, "2026-07-30")
end

tomorrow

tomorrow : Date

The day after today, in this machine's zone.

Returns tomorrow's date

Examples
Date.tomorrow().iso   # => "2026-07-31"

yesterday

yesterday : Date

The day before today, in this machine's zone.

Returns yesterday's date

Examples
Date.yesterday().iso   # => "2026-07-29"

utcNow

utcNow : Date

Today's date in UTC, whatever this machine's zone is.

Returns today's UTC date

Examples
Date.utcNow().iso   # => "2026-07-30"

utcToday

utcToday : Date

Today's date in UTC. The same as Date.utcNow(), under the name that reads better.

Returns today's UTC date

Examples
Date.utcToday().iso   # => "2026-07-30"

module DateTime

Building instants, and asking what time it is now.

of

of(date: Date, time: Time, offset: Duration) -> DateTime

Combines a date, a time of day and a UTC offset into an instant.

Parameters
date
the calendar date
time
the time of day
offset
the offset from UTC

Returns the instant

Examples
DateTime.of(Date.of(2026, 7, 30).try,
            Time.of(9, 0, 0).try,
            Duration.zero()).iso
# => "2026-07-30T09:00:00Z"

At an offset

DateTime.of(date, time, Duration.utcOffset(2, 0))

fromEpochSeconds

fromEpochSeconds(count: Integer, offset: Duration) -> DateTime

The instant a count of seconds since the Unix epoch names, rendered at UTC or at the offset you give.

Declared before the two-argument form: the interpreter resolves an overloaded module function to its LAST definition regardless of arity, so a delegating overload has to come first or it recurses into itself.

Parameters
count
seconds since the Unix epoch
offset
the offset to render at; omitted for UTC

Returns the instant

Examples
DateTime.fromEpochSeconds(0).iso   # => "1970-01-01T00:00:00Z"

Rendered at a local offset

DateTime.fromEpochSeconds(0, Duration.utcOffset(2, 0)).iso
# => "1970-01-01T02:00:00+02:00"

parse

parse(text: String) -> Result<DateTime, TimeError>

Parses an ISO 8601 instant.

Accepts +2026-07-30T14:03:00+02:00+, the same with Z, or a bare civil datetime with no zone at all, which is read as UTC.

Parameters
text
the text to parse

Returns the instant, or why it could not be read

Examples
DateTime.parse("2026-07-30T14:03:00+02:00").map { |m| m.utc.iso }
# => Ok("2026-07-30T12:03:00Z")
DateTime.parse("nope")   # => Error(InvalidFormat("nope"))

Reading a timestamp out of a log line

DateTime.parse(line.take(20)).map { |m| m.epochSeconds }

now

now : DateTime

The current instant, in this machine's zone as it stands right now.

The offset is the one in effect at this instant, so it is right today. Named IANA zones are not modeled, so it cannot say what the offset WILL be for some future local time.

Returns the current instant

Examples
DateTime.now().iso   # => "2026-07-30T14:03:00+02:00"

utcNow

utcNow : DateTime

The current instant, at UTC.

The form to prefer when the value is stored, compared or transmitted: there is no zone to disagree about.

Returns the current instant, at UTC

Examples
DateTime.utcNow().iso   # => "2026-07-30T12:03:00Z"

epochNanos

epochNanos : Integer

Nanoseconds since the Unix epoch, straight from the clock.

The right reading for measuring a short interval: a plain integer, with no zone to disagree about.

Returns nanoseconds since the Unix epoch

Examples

Timing a piece of work

let started = DateTime.epochNanos()
doTheThing()
let elapsedMs = (DateTime.epochNanos() - started) / 1000000

extends Integer

More functions for Integer, added by this module.

The plural spellings build a Duration; the singular ones from units.kex build a time Measure. 5.seconds is an elapsed span, 5.second a measurement.

nanoseconds

nanoseconds : Duration

microseconds

microseconds : Duration

milliseconds

milliseconds : Duration

This many milliseconds, as a Duration.

Returns the elapsed span

Examples
500.milliseconds.wholeMilliseconds   # => 500

seconds

seconds : Duration

This many seconds, as a Duration.

Note the plural: 5.seconds is an elapsed span, while 5.second from units.kex is a measurement.

Returns the elapsed span

Examples
30.seconds.wholeSeconds   # => 30

minutes

minutes : Duration

This many minutes, as a Duration.

Returns the elapsed span

Examples
90.minutes.wholeHours   # => 1

hours

hours : Duration

This many hours, as a Duration.

Returns the elapsed span

Examples
36.hours.wholeDays   # => 1

days

days : Duration

This many days, as a Duration: a fixed 86400 seconds each.

Use Period.days when the calendar should get a say.

Returns the elapsed span

Examples
(Date.of(2026, 7, 30).try + 10.days).iso   # => "2026-08-09"

weeks

weeks : Duration

This many weeks, as a Duration: a fixed 604800 seconds each.

Returns the elapsed span

Examples
2.weeks.wholeDays   # => 14

months

months : Period

This many calendar months, as a Period.

A calendar span, not a Duration: see the Period record above. 4.weeks and 1.months are deliberately different things: the first is exactly 28 days, the second is one calendar month, however long that turns out to be.

Returns the calendar span

Examples
(Date.of(2026, 7, 30).try + 1.months).iso   # => "2026-08-30"

The calendar clamps a day that does not exist

(Date.of(2026, 1, 31).try + 1.months).iso   # => "2026-02-28"

years

years : Period

This many calendar years, as a Period.

Returns the calendar span

Examples
(Date.of(2024, 2, 29).try + 1.years).iso   # => "2025-02-28"

extends Float

More functions for Float, added by this module.

The same Duration constructors on Float, for fractional spans: 1.5.hours, 0.25.seconds.

nanoseconds

nanoseconds : Duration

microseconds

microseconds : Duration

milliseconds

milliseconds : Duration

This many milliseconds, as a Duration.

Returns the elapsed span

seconds

seconds : Duration

This many seconds, as a Duration.

Returns the elapsed span

Examples
0.25.seconds.wholeMilliseconds   # => 250

minutes

minutes : Duration

This many minutes, as a Duration.

Returns the elapsed span

hours

hours : Duration

This many hours, as a Duration.

Returns the elapsed span

Examples
1.5.hours.wholeMinutes   # => 90

days

days : Duration

This many days, as a Duration: a fixed 86400 seconds each.

Returns the elapsed span

weeks

weeks : Duration

This many weeks, as a Duration: a fixed 604800 seconds each.

Returns the elapsed span

extends Duration

More functions for Duration, added by this module.

wholeNanoseconds

wholeNanoseconds : Integer

wholeMicroseconds

wholeMicroseconds : Integer

+

+(other: Duration) -> Duration

Adds two spans.

Parameters
other
the span to add

Returns the total

Examples
(90.minutes + 30.minutes).wholeHours   # => 2

-

-(other: Duration) -> Duration

Subtracts a span. The result may be negative.

Parameters
other
the span to subtract

Returns the difference

Examples
(1.hours - 90.minutes).negative?   # => true

negated

negated : Duration

The same span with its sign flipped.

Returns the negated span

Examples
90.minutes.negated.negative?   # => true

*

*(factor: Number) -> Duration

Multiplies the span by a plain number.

3 * 1.days is not the same call because the receiver has to be the Duration, so it is spelled 1.days * 3.

Parameters
factor
the factor to multiply by

Returns the scaled span

Examples
(90.minutes * 2).wholeHours   # => 3

/

/(divisor: Number) -> Duration

Divides the span by a plain number.

Parameters
divisor
the number to divide by

Returns the scaled span

Examples
(90.minutes / 2).wholeMinutes   # => 45

abs

abs : Duration

The span's magnitude, discarding its direction.

Returns the absolute span

Examples
(1.hours - 90.minutes).abs.wholeMinutes   # => 30

zero?

zero? : Bool

Returns true when the span is exactly zero.

Returns true for a zero span

Examples
Duration.zero().zero?   # => true

negative?

negative? : Bool

Returns true when the span points backwards.

A negative span is what until gives you when the other moment is earlier, so this is how to ask which came first.

Returns true for a negative span

Examples
date.until(other).negative?   # => true when `other` is earlier

positive?

positive? : Bool

Returns true when the span points forwards.

Returns true for a positive span

Examples
90.minutes.positive?   # => true

shorterThan?

shorterThan?(other: Duration) -> Bool

Returns true when this span is shorter than other.

Named for length rather than for order: before?/after? are about when something happened, and a Duration is not a point in time.

Parameters
other
the span to compare against

Returns true when this span is shorter

Examples
30.minutes.shorterThan?(1.hours)   # => true

Enforcing a timeout

if started.until(DateTime.utcNow()).longerThan?(30.seconds)
  IO.printError("timed out")
end

longerThan?

longerThan?(other: Duration) -> Bool

Returns true when this span is longer than other.

Parameters
other
the span to compare against

Returns true when this span is longer

Examples
90.minutes.longerThan?(1.hours)   # => true

wholeMilliseconds

wholeMilliseconds : Integer

The whole milliseconds in the span, truncated toward zero.

Returns the milliseconds

Examples
500.milliseconds.wholeMilliseconds   # => 500

wholeSeconds

wholeSeconds : Integer

The whole seconds in the span, truncated toward zero.

Returns the seconds

Examples
Duration.hours(2).wholeSeconds   # => 7200

wholeMinutes

wholeMinutes : Integer

The whole minutes in the span, truncated toward zero.

Returns the minutes

Examples
90.minutes.wholeMinutes   # => 90
1.5.hours.wholeMinutes    # => 90

wholeHours

wholeHours : Integer

The whole hours in the span, truncated toward zero. A partial hour does not count.

Returns the hours

Examples
90.minutes.wholeHours   # => 1

wholeDays

wholeDays : Integer

The whole days in the span, truncated toward zero.

This is what +date + span+ uses, which is why +date + 36.hours+ advances exactly one day.

Returns the days

Examples
36.hours.wholeDays   # => 1
2.weeks.wholeDays    # => 14

wholeWeeks

wholeWeeks : Integer

The whole weeks in the span, truncated toward zero.

Returns the weeks

Examples
14.days.wholeWeeks   # => 2

compareTo

compareTo(other: Duration) -> Ordering

Orders this span against another by length.

Delegates to Number.compare (algebra.kex), which orders the two Float second counts.

Parameters
other
the span to compare against

Returns Less, Equal or Greater

Examples
90.minutes.compareTo(60.minutes)   # => Greater

Sorting by length

spans.sort { |a, b| a.compareTo(b) == Less }

module Duration

Building elapsed spans, and UTC offsets.

zero

zero : Duration

A span of no time at all. Also the UTC offset.

Returns the zero span

Examples
Duration.zero().zero?   # => true
date.at(time, Duration.zero()).iso   # => "...T09:00:00Z"

nanoseconds

nanoseconds(count: Number) -> Duration

microseconds

microseconds(count: Number) -> Duration

milliseconds

milliseconds(count: Number) -> Duration

A span of count milliseconds.

Parameters
count
how many milliseconds

Returns the elapsed span

Examples
Duration.milliseconds(1500).wholeSeconds   # => 1

seconds

seconds(count: Number) -> Duration

A span of count seconds.

Parameters
count
how many seconds

Returns the elapsed span

Examples
Duration.seconds(90).wholeMinutes   # => 1

minutes

minutes(count: Number) -> Duration

A span of count minutes.

Parameters
count
how many minutes

Returns the elapsed span

Examples
Duration.minutes(90).wholeHours   # => 1

hours

hours(count: Number) -> Duration

A span of count hours.

Parameters
count
how many hours

Returns the elapsed span

Examples
Duration.hours(2).wholeSeconds   # => 7200

days

days(count: Number) -> Duration

A span of count days, each a fixed 86400 seconds.

Parameters
count
how many days

Returns the elapsed span

Examples
Duration.days(2).wholeHours   # => 48

weeks

weeks(count: Number) -> Duration

A span of count weeks, each a fixed 604800 seconds.

Parameters
count
how many weeks

Returns the elapsed span

Examples
Duration.weeks(2).wholeDays   # => 14

utcOffset

utcOffset(hours: Integer, minutes: Integer) -> Duration

A whole-minute UTC offset, the only kind ISO 8601 can spell.

A negative hour or minute puts the whole offset west of UTC, so utcOffset(-5, 30) is five and a half hours behind UTC, not four and a half.

Parameters
hours
the hour part of the offset
minutes
the minute part of the offset

Returns the offset

Examples
Duration.utcOffset(2, 0).wholeSeconds     # => 7200
Duration.utcOffset(-5, 0).wholeSeconds    # => -18000

Rendering an instant at another offset

moment.at(Duration.utcOffset(-5, 0)).iso

module Period

Building calendar spans.

zero

zero : Period

A span of nothing.

Returns the zero span

Examples
Period.zero().iso     # => "P0D"
Period.zero().zero?   # => true

of

of(years: Integer, months: Integer, days: Integer) -> Period

A span of the given years, months and days together.

Parameters
years
whole years
months
whole months
days
whole days

Returns the calendar span

Examples
Period.of(1, 2, 3).iso   # => "P1Y2M3D"

years

years(count: Integer) -> Period

A span of count calendar years.

Parameters
count
how many years

Returns the calendar span

Examples
(Date.of(2024, 2, 29).try + Period.years(1)).iso   # => "2025-02-28"

months

months(count: Integer) -> Period

A span of count calendar months.

Parameters
count
how many months

Returns the calendar span

Examples
(Date.of(2026, 1, 31).try + Period.months(1)).iso   # => "2026-02-28"

days

days(count: Integer) -> Period

A span of count days, as a calendar step.

Parameters
count
how many days

Returns the calendar span

Examples
Period.days(10).iso   # => "P10D"

weeks

weeks(count: Integer) -> Period

A span of count weeks, recorded as that many times seven days.

Parameters
count
how many weeks

Returns the calendar span

Examples
Period.weeks(2).iso   # => "P14D"