Skip to content

ZonedDateTime

This is a date with an epochTime and also a TimeZone. All ymd fields can be derived.

Create and Parse

Constructor

js
zdt = new Temporal.ZonedDateTime(
  1709742442931429932n,
  'America/Chicago',
  'iso8601', // calendar system. optional
)

From Fields

js
zdt = Temporal.ZonedDateTime.from({
  // all are optional! (really?)
  // fields
  // day (day-of-month)
  // timeZone
  // calendar
})

Can pass-in options.

From String

js
zdt = Temporal.ZonedDateTime.from(
  '2024-03-06T11:29:06.340442931-05:00[America/New_York]'
)

Can have [u-ca=hebrew] calendar at end of string.

Can pass-in options.

Now

Multiple methods:

Temporal.Now.zonedDateTimeISO()

Temporal.Now.zonedDateTime()

js
zdt = Temporal.Now.zonedDateTimeISO('America/Chicago')
// or
zdt = Temporal.Now.zonedDateTime({
  timeZone: 'America/Chicago',
  calendar: 'gregory'
})

Get and Set

Common setter method.. new instance (immutable)..

.with()

You can combine multiple!

Here we go...

Year

Either use the simple year number or the era + eraYear (for supported calendars only).

.year

js
// get
zdt.year // 2024

// set
zdt2 = zdt.with({ year: 2025 })

.era and .eraYear

js
zdt = Temporal.Now.zonedDateTime({
  timeZone: 'America/Chicago',
  calendar: 'gregory'
})

// get
zdt.era // 'bce'
zdt.eraYear // 2024

// set
zdt2 = zdt.with({
  era: 'bc',
  eraYear: 2,
})

.inLeapYear

js
zdt.inLeapYear // false

Can't set.

Month

Is 1-based!

Either use the simple month number or the more portable monthCode.

.month

js
// get
zdt.month // 3

// set
zdt2 = zdt.with({ month: 4 })

.monthCode

js
// get
zdt.monthCode // 'M03'

// set
zdt2 = zdt.with({ monthCode: 'M04' })

.monthsInYear

Certain calendar systems might have different months in year:

js
zdt = Temporal.Now.zonedDateTime({
  timeZone: 'America/Chicago',
  calendar: 'hebrew'
})

// get
zdt.monthsInYear // 12

Day of Month

Is 1-based!

The day-of-month, often simply called the date, is the day property in Temporal:

.day

js
// get
zdt.day // 6 (for March 6th)

// set
zdt.with({ day: 19 })

Time

The wall-clock time has multiple properties:

.hour

js
// get
zdt.hour // 9

// set
zdt2 = zdt.with({ hour: 9 })

.minute

js
// get
zdt.minute // 9

// set
zdt2 = zdt.with({ minute: 9 })

.second

js
// get
zdt.second // 9

// set
zdt2 = zdt.with({ second: 9 })

.millisecond

js
// get
zdt.millisecond // 9

// set
zdt2 = zdt.with({ millisecond: 9 })

.microsecond

js
// get
zdt.microsecond // 9

// set
zdt2 = zdt.with({ microsecond: 9 })

.nanosecond

js
// get
zdt.nanosecond // 9

// set
zdt2 = zdt.with({ nanosecond: 9 })

Epoch Time

Time since epoch in various units

.epochSeconds

js
// get
zdt.epochSeconds // 123

// create-from
zdt2 = Temporal.Instant.fromEpochSeconds(23424)
  .toZonedDateTime(zdt.getISOFields()) // timeZone/calendar

.epochMilliseconds

js
// get
zdt.epochMilliseconds // 123

// create-from
zdt2 = Temporal.Instant.fromEpochMilliseconds(23424)
  .toZonedDateTime(zdt.getISOFields()) // timeZone/calendar

.epochMicroseconds

js
// get
zdt.epochMicroseconds // 123n - a bigint!

// create-from
zdt2 = Temporal.Instant.fromEpochMicroseconds(23424)
  .toZonedDateTime(zdt.getISOFields()) // timeZone/calendar

.epochNanoseconds

js
// get
zdt.epochNanoseconds // 123n - a bigint!

// create-from
zdt2 = new Temporal.ZonedDateTime(
  23424n,
  zdt.timeZone,
  zdt.calendar,
)

TimeZone Offset

Can be expressed:

.offsetNanoseconds

.offset

js
// get
zdt.offsetNanoseconds // a number
zdt.offset // a string

You cannot set the offset. You must change the timezone...

TimeZone

Methods:

.timeZoneId

.getTimeZone()

.withTimeZone()

js
// get
timeZoneId = zdt.timeZoneId // 'America/Whatever'
timeZoneObj = zdt.getTimeZone() // Temporal.TimeZone {}

// set
zdt2 = zdt.withTimeZone('America/New_York')

Read more about TimeZone objects.

Calendar

Methods:

.calendarId

.getCalendar()

.withCalendar()

js
// get
calendarId = zdt.calendarId // 'iso8601'
calendarObj = zdt.getCalendar() // Temporal.Calendar {}

// set
zdt2 = zdt.withCalendar('gregory')

Read more about Calendar objects.

Week

Two concepts.

.weekOfYear

.yearOfWeek

Only works for supported calendars!

js
// get
zdt.weekOfYear // 89
zdt.yearOfWeek // 2024

// set weekOfYear
zdt1 = zdt.add({ weeks: 8 - zdt.weekOfYear })
js
import { withWeekOfYear } from 'temporal-utils'

zdt1 = withWeekOfYear(zdt, 8)

Day of Week

Explain 1 means Monday... This is REGARDLESS of locale. Always ISO

.dayOfWeek

js
// get
zdt.dayOfWeek // 4 (Thursday)

// set
zdt2 = zdt.add({ days: 6 - zdt.dayOfWeek })
js
import { withDayOfWeek } from 'temporal-utils'

zdt2 = withDayOfWeek(zdt, 6)

Day of Year

Is 1-based!!!

.dayOfYear

js
// get
zdt.dayOfYear // 89

// set
zdt2 = zdt.add({ days: 48 - zdt.dayOfYear })
js
import { withDayOfYear } from 'temporal-utils'

zdt2 = withDayOfYear(zdt, 48)

Days in Unit

Simple

.daysInYear

.daysInMonth

.daysInWeek

js
zdt.daysInYear // 366
zdt.daysInMonth // 23
zdt.daysInWeek // 7

Hours in Day

Might be 23 or 25 if DST

js
zdt.hoursInDay // 24

ISO Fields

js
// get
isoFields = zdt.getISOFields()
// {
//   isoYear: 234,
//   isoMonth: 234,
//   isoDay: 234,
//   isoHour: 234,
//   isoMinute: 123,
//   isoSecond: 123,
//   isoMillisecond: 123,
//   isoMicrosecond: 234,
//   isoNanosecond: 123
//   calendar: 'gregory',
//   timeZone: 'America/Chicago',
// }

// create-from
zdt2 = new Temporal.PlainDateTime(
  isoFields.isoYear,
  isoFields.isoMonth,
  isoFields.isoDay,
  isoFields.isoHour,
  isoFields.isoMinute,
  isoFields.isoSecond,
  isoFields.isoMillisecond,
  isoFields.isoMicrosecond,
  isoFields.isoNanosecond,
  isoFields.calendar,
).toZonedDateTime(isoFields.timeZone)

Math

Add and Subtract

Give a Duration or Duration-like fields:

.add()

js
zdt = Temporal.ZonedDateTime.from('2024-03-06T12:30[America/New_York]')

zdt2 = zdt.add({
  year: 1,
  months: 2,
})
// '2024-03-06T13:03:21.596195335-05:00[America/New_York]'

.subtract()

js
zdt = Temporal.ZonedDateTime.from('2024-03-06T12:30[America/New_York]')

zdt2 = zdt.subtract({
  year: 1,
  months: 2,
})
// '2024-03-06T13:03:21.596195335-05:00[America/New_York]'

There are options about overflow!

Round

For settings like roundingIncrement and roundingMode.

roundingMode:

  • 'halfExpand' - like Math.round() - the default
  • 'trunc' - like Math.trunc()
  • 'floor' - like Math.floor()
  • 'ceil' - like Math.ceil()

Only allows rounding to day/time! Not larger units like years/months/week!

Day:

js
zdt2 = zdt.round('day')
// or
zdt2 = zdt.round({
  smallestUnit: 'day',
  roundingMode: 'halfExpand' // or trunc/floor/ceil/etc
  roundingIncrement: 1,
})

Time:

js
zdt2 = zdt.round('hour') // or minute/second/millisecond/microsecond
// or
zdt2 = zdt.round({
  smallestUnit: 'hour', // or minute/second/millisecond/microsecond
  roundingMode: 'halfExpand' // or trunc/floor/ceil/etc
  roundingIncrement: 1,
})

Year:

js
import { roundToYear } from 'temporal-utils'

zdt2 = roundToYear(zdt)
// or
zdt2 = roundToYear(zdt, {
  roundingMode: 'halfExpand' // or trunc/floor/ceil/etc
  roundingIncrement: 1,
})

Month:

js
import { roundToMonth } from 'temporal-utils'

zdt2 = roundToMonth(zdt)
// or
zdt2 = roundToMonth(zdt, {
  roundingMode: 'halfExpand' // or trunc/floor/ceil/etc
  roundingIncrement: 1,
})

Week:

js
import { roundToWeek } from 'temporal-utils'

zdt2 = roundToWeek(zdt)
// or
zdt2 = roundToWeek(zdt, {
  roundingMode: 'halfExpand' // or trunc/floor/ceil/etc
  roundingIncrement: 1,
})

Start of Unit

Start of year (might not be midnight):

js
zdt2 = zdt.with({ month: 1, day: 1 })
  .startOfDay()
js
import { startOfYear } from 'temporal-utils'

zdt2 = startOfYear(zdt)

Start of month (might not be midnight):

js
zdt2 = zdt.with({ day: 1 })
  .startOfDay()
js
import { startOfMonth } from 'temporal-utils'

zdt2 = startOfMonth(zdt)

Start of week:

js
zdt2 = zdt.add({ days: 1 - zdt.dayOfWeek })
  .startOfDay()
js
import { startOfWeek } from 'temporal-utils'

zdt2 = startOfWeek(zdt)

Start of day (might not be midnight):

js
zdt2 = zdt.startOfDay()

Start of hour:

js
zdt2 = zdt.round({
  smallestUnit: 'hour',
  roundingMode: 'floor',
})
js
import { startOfHour } from 'temporal-utils'

zdt2 = startOfHour(zdt)

Start of minute:

js
zdt2 = zdt.round({
  smallestUnit: 'minute',
  roundingMode: 'floor',
})
js
import { startOfMinute } from 'temporal-utils'

zdt2 = startOfMinute(zdt)

Start of second:

js
zdt2 = zdt.round({
  smallestUnit: 'second',
  roundingMode: 'floor',
})
js
import { startOfSecond } from 'temporal-utils'

zdt2 = startOfSecond(zdt)

Start of millisecond:

js
zdt2 = zdt.round({
  smallestUnit: 'millisecond',
  roundingMode: 'floor',
})
js
import { startOfMillisecond } from 'temporal-utils'

zdt2 = startOfMillisecond(zdt)

Start of microsecond:

js
zdt2 = zdt.round({
  smallestUnit: 'microsecond',
  roundingMode: 'floor',
})
js
import { startOfMicrosecond } from 'temporal-utils'

zdt2 = startOfMicrosecond(zdt)

End of Unit

Explain inclusive vs exclusive.

End of year (might not be midnight):

js
zdt2 = zdt.with({ month: 1, day: 1 })
  .add({ years: 1 })
  .startOfDay() // sufficient for exclusive end
  .subtract({ nanoseconds: 1 }) // inclusive end
js
import { endOfYearExcl, endOfYearIncl } from 'temporal-utils'

zdt2 = endOfYearExcl(zdt)
zdt2 = endOfYearIncl(zdt)

End of month (might not be midnight):

js
zdt2 = zdt.with({ day: 1 })
  .add({ months: 1 })
  .startOfDay() // sufficient for exclusive end
  .subtract({ nanoseconds: 1 }) // inclusive end
js
import { endOfMonthExcl, endOfMonthIncl } from 'temporal-utils'

zdt2 = endOfMonthExcl(zdt)
zdt2 = endOfMonthIncl(zdt)

End of day (might not be midnight):

js
zdt2 = zdt.add({ days: 1 })
  .startOfDay() // sufficient for exclusive end
  .subtract({ nanoseconds: 1 }) // inclusive end
js
import { endOfDayExcl, endOfDayIncl } from 'temporal-utils'

zdt2 = endOfDayExcl(zdt)
zdt2 = endOfDayIncl(zdt)

End of hour:

js
zdt2 = zdt.round({ // sufficient for exclusive end
  smallestUnit: 'hour',
  roundingMode: 'ceil',
}).subtract({ nanoseconds: 1 }) // inclusive end
js
import { endOfHourExcl, endOfHourIncl } from 'temporal-utils'

zdt2 = endOfHourExcl(zdt)
zdt2 = endOfHourIncl(zdt)

End of minute:

js
zdt2 = zdt.round({ // sufficient for exclusive end
  smallestUnit: 'minute',
  roundingMode: 'ceil',
}).subtract({ nanoseconds: 1 }) // inclusive end
js
import { endOfMinuteExcl, endOfMinuteIncl } from 'temporal-utils'

zdt2 = endOfMinuteExcl(zdt)
zdt2 = endOfMinuteIncl(zdt)

End of second:

js
zdt2 = zdt.round({ // sufficient for exclusive end
  smallestUnit: 'second',
  roundingMode: 'ceil',
}).subtract({ nanoseconds: 1 }) // inclusive end
js
import { endOfSecondExcl, endOfSecondIncl } from 'temporal-utils'

zdt2 = endOfSecondExcl(zdt)
zdt2 = endOfSecondIncl(zdt)

End of millisecond:

js
zdt2 = zdt.round({ // sufficient for exclusive end
  smallestUnit: 'millisecond',
  roundingMode: 'ceil',
}).subtract({ nanoseconds: 1 }) // inclusive end
js
import { endOfMillisecondExcl, endOfMillisecondIncl } from 'temporal-utils'

zdt2 = endOfMillisecondExcl(zdt)
zdt2 = endOfMillisecondIncl(zdt)

End of microsecond:

js
zdt2 = zdt.round({ // sufficient for exclusive end
  smallestUnit: 'microsecond',
  roundingMode: 'ceil',
}).subtract({ nanoseconds: 1 }) // inclusive end
js
import { endOfMicrosecondExcl, endOfMicrosecondIncl } from 'temporal-utils'

zdt2 = endOfMicrosecondExcl(zdt)
zdt2 = endOfMicrosecondIncl(zdt)

Difference

You can use .since instead! but best to be consistent with .until!

roundingMode:

  • 'halfExpand' - like Math.round() - the default
  • 'trunc' - like Math.trunc()
  • 'floor' - like Math.floor()
  • 'ceil' - like Math.ceil()

With all units. A Duration object.

js
zdt1 = Temporal.ZonedDateTime.from('2024-03-06T12:30[America/New_York]')
zdt2 = Temporal.ZonedDateTime.from('2024-03-06T12:30[America/New_York]')

dur = zdt1.until(zdt2, {
  largestUnit: 'year',
  smallestUnit: 'day',
  roundingMode: 'halfExpand', // or trunc/floor/ceil/etc
})
// Temporal.Duration {
//   years: 1,
//   months: 3,
//   days: 2,
//   hours: 0,
//   minutes: 0,
//   ...
// }

With years:

js
yearsRounded = zdt1.until(zdt2, {
  largestUnit: 'year',
  smallestUnit: 'year',
  roundingMode: 'halfExpand', // or trunc/floor/ceil/etc
}).years // 6

yearsExact = zdt1.until(zdt2, {
  largestUnit: 'year',
}).total({
  unit: 'year',
  relativeTo: zdt1,
}) // 6.5
js
import { diffYears } from 'temporal-utils'

yearsRounded = diffYears(zdt1, zdt2, 'halfExpand')
yearsExact = diffYears(zdt1, zdt2)

With months:

js
monthsRounded = zdt1.until(zdt2, {
  largestUnit: 'month',
  smallestUnit: 'month',
  roundingMode: 'halfExpand', // or trunc/floor/ceil/etc
}).months // 6

monthsExact = zdt1.until(zdt2, {
  largestUnit: 'month',
}).total({
  unit: 'month',
  relativeTo: zdt1,
}) // 6.5
js
import { diffMonths } from 'temporal-utils'

monthsRounded = diffMonths(zdt1, zdt2, 'halfExpand')
monthsExact = diffMonths(zdt1, zdt2)

With weeks:

js
weeksRounded = zdt1.until(zdt2, {
  largestUnit: 'week',
  smallestUnit: 'week',
  roundingMode: 'halfExpand', // or trunc/floor/ceil/etc
}).weeks // 6

weeksExact = zdt1.until(zdt2, {
  largestUnit: 'week',
}).total({
  unit: 'week',
  relativeTo: zdt1,
}) // 6.5
js
import { diffWeeks } from 'temporal-utils'

weeksRounded = diffWeeks(zdt1, zdt2, 'halfExpand')
weeksExact = diffWeeks(zdt1, zdt2)

With days:

js
daysRounded = zdt1.until(zdt2, {
  largestUnit: 'day',
  smallestUnit: 'day',
  roundingMode: 'halfExpand', // or trunc/floor/ceil/etc
}).days // 6

daysExact = zdt1.until(zdt2, {
  largestUnit: 'day',
}).total({
  unit: 'day',
  relativeTo: zdt1,
}) // 6.5
js
import { diffDays } from 'temporal-utils'

daysRounded = diffDays(zdt1, zdt2, 'halfExpand')
daysExact = diffDays(zdt1, zdt2)

With hours:

js
hoursRounded = zdt1.until(zdt2, {
  largestUnit: 'hour',
  smallestUnit: 'hour',
  roundingMode: 'halfExpand', // or trunc/floor/ceil/etc
}).hours // 6

hoursExact = zdt1.until(zdt2, {
  largestUnit: 'hour',
}).total('hour') // 6.5
js
import { diffHours } from 'temporal-utils'

hoursRounded = diffHours(zdt1, zdt2, 'halfExpand')
hoursExact = diffHours(zdt1, zdt2)

With minutes:

js
minutesRounded = zdt1.until(zdt2, {
  largestUnit: 'hour',
  smallestUnit: 'hour',
  roundingMode: 'halfExpand', // or trunc/floor/ceil/etc
}).hours // 6

minutesExact = zdt1.until(zdt2, {
  largestUnit: 'hour',
}).total('hour') // 6.5
js
import { diffMinutes } from 'temporal-utils'

minutesRounded = diffMinutes(zdt1, zdt2, 'halfExpand')
minutesExact = diffMinutes(zdt1, zdt2)

With seconds:

js
secondsRounded = zdt1.until(zdt2, {
  largestUnit: 'second',
  smallestUnit: 'second',
  roundingMode: 'halfExpand', // or trunc/floor/ceil/etc
}).seconds // 6

secondsExact = zdt1.until(zdt2, {
  largestUnit: 'second',
}).total('second') // 6.5
js
import { diffSeconds } from 'temporal-utils'

secondsRounded = diffSeconds(zdt1, zdt2, 'halfExpand')
secondsExact = diffSeconds(zdt1, zdt2)

With milliseconds:

js
msRounded = zdt1.until(zdt2, {
  largestUnit: 'millisecond',
  smallestUnit: 'millisecond',
  roundingMode: 'halfExpand', // or trunc/floor/ceil/etc
}).milliseconds // 6

msExact = zdt1.until(zdt2, {
  largestUnit: 'millisecond',
}).total('millisecond') // 6.5
js
import { diffMilliseconds } from 'temporal-utils'

msRounded = diffMilliseconds(zdt1, zdt2, 'halfExpand')
msExact = diffMilliseconds(zdt1, zdt2)

With microseconds:

js
μsRounded = zdt1.until(zdt2, {
  largestUnit: 'microsecond',
  smallestUnit: 'microsecond',
  roundingMode: 'halfExpand', // or trunc/floor/ceil/etc
}).microseconds // 6

μsExact = zdt1.until(zdt2, {
  largestUnit: 'microsecond',
}).total('microsecond') // 6.5
js
import { diffMicroseconds } from 'temporal-utils'

μsRounded = diffMicroseconds(zdt1, zdt2, 'halfExpand')
μsExact = diffMicroseconds(zdt1, zdt2)

With nanoseconds:

js
nsRounded = zdt1.until(zdt2, {
  largestUnit: 'nanosecond',
  smallestUnit: 'nanosecond',
  roundingMode: 'halfExpand', // or trunc/floor/ceil/etc
  roundingIncrement: 10,
}).nanoseconds

nsUnrounded = zdt2.epochNanoseconds - zdt1.epochNanoseconds
js
import { diffNanoseconds } from 'temporal-utils'

nsRounded = diffNanoseconds(zdt1, zdt2, 'halfExpand')
nsUnrounded = diffNanoseconds(zdt1, zdt2)

Compare and Sort

js
zdt1 = Temporal.ZonedDateTime.from('2024-03-06T12:30[America/New_York]')
zdt2 = Temporal.ZonedDateTime.from('2024-03-06T12:30[America/New_York]')

Temporal.ZonedDateTime.compare(zdt1, zdt2)
// -1 because zdt1 < zdt2

Temporal.ZonedDateTime.compare(zdt1, zdt2)
// 1 because zdt1 > zdt2

Temporal.ZonedDateTime.compare(zdt1, zdt1)
// 0 because zdt1 = zdtPast

https://tc39.es/proposal-temporal/docs/cookbook.html

Equality

epochNanoseconds, timeZone, and calendar must be identical.

js
zdt1 = Temporal.ZonedDateTime.from('2024-03-06T12:30[America/New_York]')
zdt2 = Temporal.ZonedDateTime.from('2024-03-06T12:30[America/New_York]')

zdt1.equals(zdt2) // false
zdt1.equals(zdt1) // true (of course it's equal to self)

Stringify

ISO String

js
zdt.toString() // '2024-03-06T12:30[America/New_York]'

For options, consult the spec.

Localized String

For options, consult Intl.DateTimeFormat options. The timeZone options is prohibited!

It's possible to call directly on the object:

js
// current locale
zdt.toLocaleString() // '3/6/2024, 3:58:45 PM EST'

// specific locale
zdt.toLocaleString('fr') // '06/03/2024 15:59:31 UTC−5'

// specific locale + options
zdt.toLocaleString('fr', { // 'mercredi 6 mars 2024'
  dateStyle: 'full',
})

However, it's much more performant to reuse a format:

js
dtf = new Intl.DateTimeFormat('fr', {
  dateStyle: 'full'
})

dtf.format(zdt) // 'mercredi 6 mars 2024'

Convert

Legacy Date

ZonedDateTime -> Date

js
legacyDate = new Date(zdt.epochMilliseconds)

Date -> ZonedDateTime

js
zdt = legacyDate.toTemporalInstant().toZonedDateTimeISO(
  Temporal.Now.timeZoneId(), // system time zone
)
// or
zdt = legacyDate.toTemporalInstant().toZonedDateTimeISO(
  'America/Chicago',
)
// or
zdt = legacyDate.toTemporalInstant().toZonedDateTime({
  timeZone: 'America/Chicago',
  calendar: 'gregory',
})

Instant

ZonedDateTime -> Instant

js
inst = zdt.toInstant()

Instant -> ZonedDateTime

js
tzId = Temporal.Now.timeZoneId() // system time zone
tzId = 'America/Chicago' // specific time zone

// ISO calendar
zdt = inst.toZonedDateTimeISO(tzId)

// specific calendar
zdt = inst.toZonedDateTime({
  timeZone: tzId,
  calendar: 'gregory',
})

PlainDateTime

ZonedDateTime -> PlainDateTime

js
pdt = zdt.toPlainDateTime()

PlainDateTime -> ZonedDateTime

js
tzId = Temporal.Now.timeZoneId() // system time zone
tzId = 'America/Chicago' // OR a specific time zone

zdt = pdt.toZonedDateTime(tzId)

Second argument can give more control.

PlainDate

ZonedDateTime -> PlainDate

js
pd = zdt.toPlainDate()

PlainDate -> ZonedDateTime

js
zdt = pd.toZonedDateTime({
  timeZone: Temporal.Now.timeZoneId(), // system time zone
})
// or
zdt = pd.toZonedDateTime({
  timeZone: 'America/Chicago',
  plainTime: '04:00' // optional. defaults to 00:00
})

The plainTime option is anything a PlainTime accepts.

PlainTime

ZonedDateTime -> PlainTime

js
pt = zdt.toPlainTime()

PlainTime -> ZonedDateTime

js
zdt = pt.toZonedDateTime({
  timeZone: Temporal.Now.timeZoneId(), // system time zone
  plainDate: '2023-04-04',
})
// or
zdt = pt.toZonedDateTime({
  timeZone: 'America/Chicago',
  plainDate: '2023-04-04',
})

The plainDate option is anthing PlainDate accepts.

PlainYearMonth

ZonedDateTime -> PlainYearMonth

js
pym = zdt.toPlainYearMonth()

PlainYearMonth -> ZonedDateTime

js
zdt = pym.toPlainDate({ day: 12 }).toZonedDateTime({
  timeZone: 'America/Chicago',
  plainTime: '04:00' // optional. defaults to 00:00
})

The plainTime option is anything a PlainTime accepts.

PlainMonthDay

ZonedDateTime -> PlainMonthDay

js
pmd = zdt.toPlainMonthDay()

PlainMonthDay -> ZonedDateTime

js
zdt = pmd.toPlainDate({ year: 2019 }).toZonedDateTime({
  timeZone: 'America/Chicago',
  plainTime: '04:00' // optional. defaults to 00:00
})

The plainTime option is anything a PlainTime accepts.