How does DateTime work with timezones in EK9?
← Getting Started · Ref: Q92
DateTime combines date, time, and timezone into a single built-in type. It is the preferred type for timestamps, events, logging, and any data that needs timezone awareness.
LITERAL SYNTAX
UTC: 2024-06-15T10:30:00Z (the Z means UTC)
With offset: 2020-10-04T12:15:00-05:00 (five hours behind UTC)
CONSTRUCTOR FORMS
DateTime(2024, 06, 15, 10, 30) creates a DateTime with year, month, day, hour, minute. Additional constructors accept 3 args (date only), 4 args (with hour), or 6 args (with seconds).
DateTime() creates an unset DateTime (not now). DateTime().today() returns the current date and time. DateTime().now() also returns the current date and time.
ACCESSORS
All Date accessors: year(), month(), day(), dayOfMonth(), dayOfWeek(), dayOfYear()
Time accessors: hour(), minute(), second()
Timezone: zone() returns the timezone string (e.g. 'Z' or '-05:00'). offSetFromUTC() returns the offset as a Duration (e.g. PT-5H for the -05:00 timezone).
Extract parts: date() returns the Date portion, time() returns the Time portion.
ARITHMETIC
DateTime + Duration gives DateTime. DateTime - Duration gives DateTime. DateTime - DateTime gives Duration. Compound assignment with += and -= also works.
DATE PROMOTES TO DATETIME
The #^ promote operator converts Date to DateTime automatically. You can assign a Date to a DateTime variable directly.
Stream collection: cat durations | collect as DateTime (adds durations from epoch 1970-01-01T00:00:00Z).
See Q31 for Date and Time basics. See Q32 for Duration details. See Q44 for locale formatting with shortFormat(), longFormat(), and other locale-aware display methods.
Use 'ek9 -h DateTime' to see the full API.
See Q31 for date and time. See Q32 for duration. See Q541-Q553 for deep-dive timezone coverage including withSameInstant vs withZone, UTC storage, and cross-timezone comparison.
Example
defines module qa.datetime defines program DateTimeDemo() stdout <- Stdout() // DateTime literals with timezone meetingUTC <- 2024-06-15T10:30:00Z meetingEST <- 2020-10-04T12:15:00-05:00 stdout.println(`UTC meeting: ${meetingUTC}`) stdout.println(`EST meeting: ${meetingEST}`) // Constructor form constructed <- DateTime(year: 2024, month: 06, dayOfMonth: 15, hour: 10, minute: 30) stdout.println(`Constructed matches UTC: ${constructed == meetingUTC}`) // DateTime() is unset, DateTime().today() gets current unsetDT <- DateTime() stdout.println(`Unset isSet: ${unsetDT?}`) currentDT <- DateTime().today() stdout.println(`Current: ${currentDT}`) // All accessors stdout.println(`Year: ${meetingUTC.year()}, Month: ${meetingUTC.month()}, Day: ${meetingUTC.day()}`) stdout.println(`Hour: ${meetingUTC.hour()}, Minute: ${meetingUTC.minute()}, Second: ${meetingUTC.second()}`) stdout.println(`Day of week: ${meetingUTC.dayOfWeek()}, Day of year: ${meetingUTC.dayOfYear()}`) // Timezone accessors tz <- meetingEST.zone() offset <- meetingEST.offSetFromUTC() stdout.println(`Timezone: ${tz}, Offset: ${offset}`) // Extract date and time parts datePart <- meetingUTC.date() timePart <- meetingUTC.time() stdout.println(`Date part: ${datePart}, Time part: ${timePart}`) // DateTime + Duration arithmetic meetingEnd <- meetingUTC + PT1H30M prepTime <- meetingUTC - PT30M stdout.println(`Meeting ends: ${meetingEnd}`) stdout.println(`Prep starts: ${prepTime}`) // DateTime - DateTime gives Duration laterMeeting <- 2024-06-15T14:00:00Z meetingGap <- laterMeeting - meetingUTC stdout.println(`Gap between meetings: ${meetingGap}`) // Compound assignment scheduled <- 2024-06-15T09:00:00Z scheduled += P1D stdout.println(`Rescheduled: ${scheduled}`) // Date promotes to DateTime birthday <- 1971-02-01 birthdayDT as DateTime: birthday stdout.println(`Promoted: ${birthdayDT}`) // Stream collection from durations durations as List of Duration := [P1Y1M4D, PT2H30M] collectedDT <- cat durations | collect as DateTime stdout.println(`Collected: ${collectedDT}`) // Locale formatting enGB <- Locale("en_GB") stdout.println(`Short: ${enGB.shortFormat(meetingUTC)}`) stdout.println(`Long: ${enGB.longFormat(meetingUTC)}`)
Common mistakes
E50060 — EK9 DateTime uses short method names: zone(), date(), time() not getZone(), getDate(), getTime(). Triggers E50060 — method not resolved. See ek9 -h DateTime for the full API.
Incorrect:
meetingEST.getZone()
Correct:
meetingEST.zone()
E50060 — EK9 DateTime uses short method names: zone(), date(), time() — not Java-style getZone(), getDate(), getTime(). Using a Java-style name triggers E50060 — method not resolved. Use 'ek9 -h DateTime' to see the full API.
Incorrect:
tz <- meetingEST.getZone()
Correct:
tz <- meetingEST.zone()
Other ways to ask this
- How do I use DateTime literals with UTC and offsets?
- How does EK9 handle timezone conversion?
- What is the difference between Date and DateTime in EK9?
Coming from another language?
Java: java.time.ZonedDateTime/OffsetDateTime (Java 8), verbose factory methods, no literals. Python: datetime.datetime with pytz/zoneinfo for timezones, no literals. Rust: no built-in, chrono crate with DateTime<Tz>. Go: time.Time with time.Location, no literals, bizarre reference format. JavaScript: Date has no timezone support beyond local/UTC, Temporal.ZonedDateTime still not finalised. C#: DateTimeOffset reasonable but no literals. EK9: DateTime built-in with literal syntax including timezone offset, zone()/offSetFromUTC() accessors, Duration arithmetic, Date promotion, no imports needed.
Keywords: first, beginner, intro, timestamp, start, combined, time, promote, zone, datetime, date, timezone, utc, offset