Temporal
Temporal types model JavaScript's Temporal API for date and time handling in Sessel. All types live under the Temporal.* namespace. They parse ISO 8601 strings, expose typed accessors, support arithmetic, convert between representations, and format output.
Type summary
| Type | Construction example | Description |
|---|---|---|
Temporal.Instant |
Temporal.Instant.from("2026-03-23T14:30:00Z") |
An exact moment in time (no timezone or calendar) |
Temporal.ZonedDateTime |
Temporal.ZonedDateTime.from("2026-03-23T14:30:00[Europe/London]") |
An exact moment in a named timezone |
Temporal.PlainDateTime |
Temporal.PlainDateTime.from("2026-03-23T14:30:00") |
A wall-clock date and time (no timezone) |
Temporal.PlainDate |
Temporal.PlainDate.from("2026-03-23") |
A calendar date (no time, no timezone) |
Temporal.PlainTime |
Temporal.PlainTime.from("14:30:00") |
A wall-clock time (no date, no timezone) |
Temporal.PlainYearMonth |
Temporal.PlainYearMonth.from("2026-03") |
A year-month pair |
Temporal.PlainMonthDay |
Temporal.PlainMonthDay.from("--03-23") |
A month-day pair (recurring date) |
Temporal.Duration |
Temporal.Duration.from("P1Y2M3DT4H5M6S") |
A span of time |
Temporal.PlainDate
A calendar date: year, month, and day, with no time component and no timezone.
Construction
Temporal.PlainDate.from("2026-03-23")
Temporal.PlainDate.from({ year: 2026, month: 3, day: 23 })
Accessors
let date = Temporal.PlainDate.from("2026-03-23");
date.year // 2026
date.month // 3
date.day // 23
date.dayOfWeek // 1 (Monday = 1, Sunday = 7, ISO 8601)
date.dayOfYear // 82
date.weekOfYear // 13
date.daysInMonth // 31
date.daysInYear // 365
date.inLeapYear // false
Arithmetic
let date = Temporal.PlainDate.from("2026-03-23");
date.add(Temporal.Duration.from("P1M")) // 2026-04-23
date.subtract(Temporal.Duration.from("P7D")) // 2026-03-16
date.until(Temporal.PlainDate.from("2026-12-31")) // Duration to end of year
date.since(Temporal.PlainDate.from("2026-01-01")) // Duration since start of year
Field replacement
Temporal.PlainDate.from("2026-03-23").with({ day: 1 }) // 2026-03-01
Temporal.PlainDate.from("2026-03-23").with({ month: 12 }) // 2026-12-23
Conversions
let date = Temporal.PlainDate.from("2026-03-23");
date.toPlainDateTime() // 2026-03-23T00:00:00
date.toPlainDateTime(Temporal.PlainTime.from("09:00")) // 2026-03-23T09:00:00
date.toZonedDateTime("Europe/London") // ZonedDateTime at midnight
Comparison
Temporal.PlainDate.compare(
Temporal.PlainDate.from("2026-01-01"),
Temporal.PlainDate.from("2026-12-31")
)
// -1 (first is earlier), 0 (equal), or 1 (first is later)
Equality
Temporal.PlainDate.from("2026-03-23").equals(Temporal.PlainDate.from("2026-03-23")) // true
Formatting
let date = Temporal.PlainDate.from("2026-03-23");
date.format("yyyy-MM-dd") // "2026-03-23"
date.format("dd/MM/yyyy") // "23/03/2026"
date.format("MMMM d, yyyy") // "March 23, 2026"
date.toLocaleString("en-US") // "3/23/2026"
date.toLocaleString("de-DE") // "23.3.2026"
String(date) // "2026-03-23" (ISO 8601)
Testable examples
PlainDate accessors evaluated through an expression binding:
GET /temporal-ref/date-accessors.html
HTTP/1.1 200
<!DOCTYPE html>
<html>
<body>
<div>
<p>Date: 2026-03-23</p>
<p>Year: 2026, Month: 3, Day: 23</p>
<p>Day of week: 1</p>
<p>Leap year: false</p>
</div>
</body>
</html>
Date arithmetic and field replacement in a rendered page:
GET /temporal-ref/date-arithmetic.html
HTTP/1.1 200
<!DOCTYPE html>
<html>
<body>
<div>
<p>Plus one month: 2026-04-23</p>
<p>Minus seven days: 2026-03-16</p>
<p>First of month: 2026-03-01</p>
</div>
</body>
</html>
Temporal.PlainTime
A wall-clock time: hour, minute, second, and sub-second precision. No date, no timezone.
Construction
Temporal.PlainTime.from("14:30:00")
Temporal.PlainTime.from("14:30:00.500")
Accessors
let t = Temporal.PlainTime.from("14:30:45.123456789");
t.hour // 14
t.minute // 30
t.second // 45
t.millisecond // 123
t.microsecond // 456
t.nanosecond // 789
Arithmetic
let t = Temporal.PlainTime.from("14:30:00");
t.add(Temporal.Duration.from("PT2H30M")) // 17:00:00
t.subtract(Temporal.Duration.from("PT1H")) // 13:30:00
t.until(Temporal.PlainTime.from("18:00:00")) // PT3H30M
t.since(Temporal.PlainTime.from("09:00:00")) // PT5H30M
Field replacement
Temporal.PlainTime.from("14:30:00").with({ hour: 9 }) // 09:30:00
Temporal.PlainTime.from("14:30:00").with({ minute: 0, second: 0 }) // 14:00:00
Conversions
let t = Temporal.PlainTime.from("14:30:00");
t.toPlainDateTime(Temporal.PlainDate.from("2026-03-23")) // 2026-03-23T14:30:00
Comparison
Temporal.PlainTime.compare(
Temporal.PlainTime.from("09:00:00"),
Temporal.PlainTime.from("17:00:00")
)
// -1
Equality
Temporal.PlainTime.from("14:30:00").equals(Temporal.PlainTime.from("14:30:00")) // true
Formatting
let t = Temporal.PlainTime.from("14:30:00");
t.format("HH:mm") // "14:30"
t.format("h:mm a") // "2:30 PM"
t.toLocaleString("en-US") // "2:30:00 PM"
String(t) // "14:30:00"
Temporal.PlainDateTime
A wall-clock date and time. No timezone.
Construction
Temporal.PlainDateTime.from("2026-03-23T14:30:00")
Temporal.PlainDateTime.from("2026-03-23T14:30:00.500")
Accessors
let dt = Temporal.PlainDateTime.from("2026-03-23T14:30:45");
dt.year // 2026
dt.month // 3
dt.day // 23
dt.hour // 14
dt.minute // 30
dt.second // 45
dt.millisecond // 0
dt.microsecond // 0
dt.nanosecond // 0
Arithmetic
let dt = Temporal.PlainDateTime.from("2026-03-23T14:30:00");
dt.add(Temporal.Duration.from("P1DT2H")) // 2026-03-24T16:30:00
dt.subtract(Temporal.Duration.from("PT30M")) // 2026-03-23T14:00:00
dt.until(Temporal.PlainDateTime.from("2026-03-30T09:00:00"))
dt.since(Temporal.PlainDateTime.from("2026-01-01T00:00:00"))
Field replacement
Temporal.PlainDateTime.from("2026-03-23T14:30:00").with({ hour: 9, minute: 0 })
// 2026-03-23T09:00:00
Conversions
let dt = Temporal.PlainDateTime.from("2026-03-23T14:30:00");
dt.toPlainDate() // 2026-03-23
dt.toPlainTime() // 14:30:00
dt.toZonedDateTime("America/New_York") // ZonedDateTime with timezone applied
Comparison
Temporal.PlainDateTime.compare(
Temporal.PlainDateTime.from("2026-03-23T09:00:00"),
Temporal.PlainDateTime.from("2026-03-23T17:00:00")
)
// -1
Equality
Temporal.PlainDateTime.from("2026-03-23T14:30:00")
.equals(Temporal.PlainDateTime.from("2026-03-23T14:30:00"))
// true
Formatting
let dt = Temporal.PlainDateTime.from("2026-03-23T14:30:00");
dt.format("yyyy-MM-dd HH:mm") // "2026-03-23 14:30"
dt.format("MMMM d, yyyy 'at' h:mm a") // "March 23, 2026 at 2:30 PM"
dt.toLocaleString("en-GB") // "23/03/2026, 14:30:00"
String(dt) // "2026-03-23T14:30:00"
Temporal.Instant
An exact moment in time — a point on the UTC timeline with no calendar or timezone.
Construction
Temporal.Instant.from("2026-03-23T14:30:00Z")
Temporal.Instant.from("2026-03-23T14:30:00.500Z")
Temporal.Instant.fromEpochSeconds(1) // 1970-01-01T00:00:01Z
Accessors
let instant = Temporal.Instant.from("1970-01-01T00:00:01Z");
instant.epochSeconds // 1
instant.epochMilliseconds // 1000
instant.epochMicroseconds // 1000000
instant.epochNanoseconds // 1000000000
Arithmetic
let instant = Temporal.Instant.from("2026-03-23T14:30:00Z");
instant.add(Temporal.Duration.from("PT1H"))
instant.subtract(Temporal.Duration.from("PT30M"))
instant.until(Temporal.Instant.from("2026-03-23T18:00:00Z")) // PT3H30M
instant.since(Temporal.Instant.from("2026-03-23T12:00:00Z")) // PT2H30M
Conversions
let instant = Temporal.Instant.from("2026-03-23T14:30:00Z");
instant.toZonedDateTimeISO("America/New_York") // ZonedDateTime in Eastern time
Comparison
Temporal.Instant.compare(
Temporal.Instant.from("2026-03-23T14:00:00Z"),
Temporal.Instant.from("2026-03-23T15:00:00Z")
)
// -1
Equality
Temporal.Instant.from("2026-03-23T14:30:00Z")
.equals(Temporal.Instant.from("2026-03-23T14:30:00Z"))
// true
Formatting
let instant = Temporal.Instant.from("2026-03-23T14:30:00Z");
instant.format("yyyy-MM-dd HH:mm 'UTC'") // formats in UTC
instant.toLocaleString("en-US")
String(instant) // "2026-03-23T14:30:00Z"
Testable examples
Instant epoch accessors and arithmetic in a rendered page:
GET /temporal-ref/instant.html
HTTP/1.1 200
<!DOCTYPE html>
<html>
<body>
<div>
<p>Epoch seconds: 1</p>
<p>Plus one hour: 2026-03-23T15:30:00Z</p>
<p>Compare: -1</p>
</div>
</body>
</html>
Temporal.ZonedDateTime
A moment in time in a named IANA timezone — combines an exact instant with a calendar representation.
Construction
Temporal.ZonedDateTime.from("2026-03-23T14:30:00[Europe/London]")
Temporal.ZonedDateTime.from("2026-03-23T14:30:00+05:30[Asia/Kolkata]")
Accessors
let zdt = Temporal.ZonedDateTime.from("2026-03-23T14:30:00[America/New_York]");
zdt.year // 2026
zdt.month // 3
zdt.day // 23
zdt.hour // 14
zdt.minute // 30
zdt.second // 0
zdt.millisecond // 0
zdt.microsecond // 0
zdt.nanosecond // 0
zdt.timeZoneId // "America/New_York"
zdt.offset // "-04:00" (or "-05:00" depending on DST)
Arithmetic
let zdt = Temporal.ZonedDateTime.from("2026-03-23T14:30:00[America/New_York]");
zdt.add(Temporal.Duration.from("P1DT2H"))
zdt.subtract(Temporal.Duration.from("PT1H"))
zdt.until(Temporal.ZonedDateTime.from("2026-04-01T00:00:00[America/New_York]"))
zdt.since(Temporal.ZonedDateTime.from("2026-01-01T00:00:00[America/New_York]"))
Field replacement
Temporal.ZonedDateTime.from("2026-03-23T14:30:00[America/New_York]")
.with({ hour: 9, minute: 0 })
// 2026-03-23T09:00:00[America/New_York]
Temporal.ZonedDateTime.from("2026-03-23T14:30:00[America/New_York]")
.with({ timeZone: "Europe/London" })
// re-interprets the wall clock time in the new timezone
Conversions
let zdt = Temporal.ZonedDateTime.from("2026-03-23T14:30:00[America/New_York]");
zdt.toInstant() // the underlying Instant
zdt.toPlainDate() // 2026-03-23
zdt.toPlainTime() // 14:30:00
zdt.toPlainDateTime() // 2026-03-23T14:30:00
zdt.withTimeZone("Europe/London") // same instant, different timezone
Comparison
Temporal.ZonedDateTime.compare(
Temporal.ZonedDateTime.from("2026-03-23T09:00:00[America/New_York]"),
Temporal.ZonedDateTime.from("2026-03-23T14:00:00[Europe/London]")
)
// compares the underlying instants
Equality
ZonedDateTime.equals() requires both the same instant AND the same timezone:
let a = Temporal.ZonedDateTime.from("2026-03-23T14:30:00[America/New_York]");
let b = Temporal.ZonedDateTime.from("2026-03-23T19:30:00[Europe/London]");
a.equals(b) // false — different timezones, even though same instant
Formatting
let zdt = Temporal.ZonedDateTime.from("2026-03-23T14:30:00[America/New_York]");
zdt.format("yyyy-MM-dd HH:mm z") // "2026-03-23 14:30 EDT"
zdt.toLocaleString("en-US")
String(zdt) // "2026-03-23T14:30:00-04:00[America/New_York]"
Testable examples
ZonedDateTime timezone conversion in a rendered page:
GET /temporal-ref/zoned.html
HTTP/1.1 200
<!DOCTYPE html>
<html>
<body>
<div>
<p>Timezone: UTC</p>
<p>New York hour: 6</p>
<p>Date: 2026-03-23</p>
</div>
</body>
</html>
Temporal.Duration
A span of time with both calendar components (years, months, weeks, days) and time components (hours, minutes, seconds, sub-seconds).
Construction
ISO 8601 duration format: P<date>T<time>. The P prefix is required. The T separator is required if any time component is present.
Temporal.Duration.from("P1Y2M3DT4H5M6S") // 1 year, 2 months, 3 days, 4 hours, 5 minutes, 6 seconds
Temporal.Duration.from("P1M") // 1 month
Temporal.Duration.from("PT30M") // 30 minutes
Temporal.Duration.from("P7D") // 7 days
Temporal.Duration.from("-PT1H") // negative 1 hour
Map construction:
Temporal.Duration.from({ years: 1, months: 6 })
Temporal.Duration.from({ hours: 2, minutes: 30 })
Temporal.Duration.from({ days: 7 })
Accessors
let dur = Temporal.Duration.from("P1Y2M3DT4H5M6S");
dur.years // 1
dur.months // 2
dur.weeks // 0
dur.days // 3
dur.hours // 4
dur.minutes // 5
dur.seconds // 6
dur.milliseconds // 0
dur.microseconds // 0
dur.nanoseconds // 0
dur.sign // 1 (positive), -1 (negative), or 0 (zero)
dur.blank // false (true only when all components are zero)
Field replacement
Temporal.Duration.from("P1Y2M3DT4H5M6S").with({ years: 2, hours: 0 })
// P2Y2M3DT5M6S
.with() takes a map of the same keys as map construction above (years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds) and replaces only the given components, leaving the rest unchanged.
Calendar arithmetic and relativeTo
Operations involving years, months, or weeks are calendar-relative — the number of days in a month varies. These operations require a relativeTo argument providing a reference date:
let oneMonth = Temporal.Duration.from("P1M");
// Add two durations with calendar components — need relativeTo
oneMonth.add(
Temporal.Duration.from("P1M"),
Temporal.PlainDate.from("2026-01-31") // relativeTo
)
// P2M (relative to Jan 31, gives Mar 31)
// total() converts to a specific unit — always needs relativeTo for calendar units
Temporal.Duration.from("P1Y").total("days", Temporal.PlainDate.from("2026-01-01"))
// 365
Pure time durations (hours, minutes, seconds) do not need relativeTo:
Temporal.Duration.from("PT2H").add(Temporal.Duration.from("PT30M"))
// PT2H30M
Negation and absolute value
Temporal.Duration.from("P1M").negated() // -P1M
Temporal.Duration.from("-PT2H").abs() // PT2H
Temporal.Duration.from("-P1M").sign // -1
Equality
Temporal.Duration.from("P1M").equals(Temporal.Duration.from("P1M")) // true
Temporal.Duration.from("P30D").equals(Temporal.Duration.from("P1M")) // false (different components)
Testable examples
Duration accessors and negation in a rendered page:
GET /temporal-ref/duration.html
HTTP/1.1 200
<!DOCTYPE html>
<html>
<body>
<div>
<p>Duration: P1Y2M3DT4H5M6S</p>
<p>Years: 1</p>
<p>Sign: 1</p>
<p>Negated: -PT1H</p>
<p>Blank: true</p>
</div>
</body>
</html>
Temporal.PlainYearMonth
A year and month without a day. Useful for billing periods, monthly reports, or month-level comparisons.
Construction
Temporal.PlainYearMonth.from("2026-03")
Temporal.PlainYearMonth.from({ year: 2026, month: 3 })
Accessors
let ym = Temporal.PlainYearMonth.from("2026-03");
ym.year // 2026
ym.month // 3
ym.daysInMonth // 31
ym.daysInYear // 365
ym.inLeapYear // false
Arithmetic
let ym = Temporal.PlainYearMonth.from("2026-03");
ym.add(Temporal.Duration.from("P3M")) // 2026-06
ym.subtract(Temporal.Duration.from("P1Y")) // 2025-03
ym.until(Temporal.PlainYearMonth.from("2027-01")) // P10M
ym.since(Temporal.PlainYearMonth.from("2026-01")) // P2M
Field replacement
Temporal.PlainYearMonth.from("2026-03").with({ month: 12 }) // 2026-12
Conversions
Temporal.PlainYearMonth.from("2026-03").toPlainDate(15) // 2026-03-15
Comparison
Temporal.PlainYearMonth.compare(
Temporal.PlainYearMonth.from("2026-01"),
Temporal.PlainYearMonth.from("2026-06")
)
// -1
Equality
Temporal.PlainYearMonth.from("2026-03").equals(Temporal.PlainYearMonth.from("2026-03")) // true
Formatting
Temporal.PlainYearMonth.from("2026-03").format("MMMM yyyy") // "March 2026"
Temporal.PlainYearMonth.from("2026-03").toLocaleString("en-US")
String(Temporal.PlainYearMonth.from("2026-03")) // "2026-03"
Temporal.PlainMonthDay
A month and day without a year. Useful for recurring dates such as birthdays or anniversaries.
Construction
ISO 8601 month-day format: --MM-DD.
Temporal.PlainMonthDay.from("--03-23")
Temporal.PlainMonthDay.from({ month: 3, day: 23 })
Accessors
let md = Temporal.PlainMonthDay.from("--03-23");
md.month // 3
md.day // 23
Field replacement
Temporal.PlainMonthDay.from("--03-23").with({ day: 1 }) // --03-01
Conversions
Temporal.PlainMonthDay.from("--03-23").toPlainDate(2026) // 2026-03-23
Equality
Temporal.PlainMonthDay.from("--03-23").equals(Temporal.PlainMonthDay.from("--03-23")) // true
Formatting
Temporal.PlainMonthDay.from("--03-23").format("MMMM d") // "March 23"
Temporal.PlainMonthDay.from("--03-23").toLocaleString("en-US")
String(Temporal.PlainMonthDay.from("--03-23")) // "--03-23"
Temporal.Now
Temporal.Now provides current date/time values. These are non-deterministic — calls return different values on each evaluation.
Temporal.Now.instant() // current Instant (UTC timestamp)
Temporal.Now.zonedDateTimeISO() // ZonedDateTime in the system timezone
Temporal.Now.zonedDateTimeISO("America/New_York") // ZonedDateTime in a specific timezone
Temporal.Now.plainDateISO() // PlainDate in the system timezone
Temporal.Now.plainDateISO("Europe/London")
Temporal.Now.plainTimeISO() // PlainTime in the system timezone
Temporal.Now.plainTimeISO("Asia/Tokyo")
Temporal.Now.plainDateTimeISO() // PlainDateTime in the system timezone
Temporal.Now.plainDateTimeISO("Australia/Sydney")
Because Temporal.Now is non-deterministic, avoid it in constraint expressions where repeatable evaluation is required. Use it in mutation handlers and response expressions where the current time at execution is meaningful.
String coercion
String(value) on any Temporal value returns its ISO 8601 canonical form:
String(Temporal.PlainDate.from("2026-03-23")) // "2026-03-23"
String(Temporal.PlainTime.from("14:30:00")) // "14:30:00"
String(Temporal.PlainDateTime.from("2026-03-23T14:30:00")) // "2026-03-23T14:30:00"
String(Temporal.Instant.from("2026-03-23T14:30:00Z")) // "2026-03-23T14:30:00Z"
String(Temporal.Duration.from("P1Y2M3D")) // "P1Y2M3D"
String(Temporal.PlainYearMonth.from("2026-03")) // "2026-03"
String(Temporal.PlainMonthDay.from("--03-23")) // "--03-23"
.toString() is equivalent to String(value).
Type checking
The isa operator checks whether a value belongs to a Temporal type:
let date = Temporal.PlainDate.from("2026-03-23");
date isa Temporal.PlainDate // true
date isa Temporal.PlainDateTime // false
date isa Temporal // true (any Temporal type)
Formatting patterns
.format(pattern) accepts CLDR-style patterns:
| Token | Meaning | Example |
|---|---|---|
yyyy |
4-digit year | 2026 |
yy |
2-digit year | 26 |
MMMM |
Full month name | March |
MMM |
Short month name | Mar |
MM |
2-digit month | 03 |
M |
Month number | 3 |
dd |
2-digit day | 23 |
d |
Day number | 23 |
HH |
24-hour hour (0-padded) | 14 |
H |
24-hour hour | 14 |
hh |
12-hour hour (0-padded) | 02 |
h |
12-hour hour | 2 |
mm |
Minutes (0-padded) | 30 |
ss |
Seconds (0-padded) | 00 |
a |
AM/PM | PM |
'...' |
Literal text | 'at' → at |
Testable examples
Pattern-based formatting with .format() in a rendered page:
GET /temporal-ref/formatting.html
HTTP/1.1 200
<!DOCTYPE html>
<html>
<body>
<div>
<p>ISO: 2026-03-23</p>
<p>European: 23/03/2026</p>
<p>Long: March 23, 2026</p>
<p>12-hour time: 2:30 PM</p>
</div>
</body>
</html>
.toLocaleString(locale) uses ICU4X locale-aware formatting:
Temporal.PlainDate.from("2026-03-23").toLocaleString("en-US") // "3/23/2026"
Temporal.PlainDate.from("2026-03-23").toLocaleString("de-DE") // "23.3.2026"
Temporal.PlainDate.from("2026-03-23").toLocaleString("ja-JP") // "2026/3/23"