8.9 KiB
Lyng time functions
Lyng date and time support requires importing lyng.time. The module provides four related types:
Instantfor absolute timestamps.Datefor calendar dates without time-of-day or timezone.DateTimefor calendar-aware points in time in a specific timezone.Durationfor absolute elapsed time.
Time instant: Instant
Instant represents some moment of time independently of the calendar. It is similar to SQL TIMESTAMP
or Kotlin Instant.
Constructing and converting
import lyng.time
val t1 = Instant()
val t2 = Instant(1704110400)
val t3 = Instant("2024-01-01T12:00:00.123456Z")
val t4 = t3.truncateToMinute()
assertEquals("2024-01-01T12:00:00Z", t4.toRFC3339())
val dt = t3.toDateTime("+02:00")
assertEquals(14, dt.hour)
val d = t3.toDate("Z")
assertEquals(Date(2024, 1, 1), d)
Instant members
| member | description |
|---|---|
| epochSeconds: Real | offset in seconds since Unix epoch |
| epochWholeSeconds: Int | whole seconds since Unix epoch |
| nanosecondsOfSecond: Int | nanoseconds within the current second |
| isDistantFuture: Bool | true if it is Instant.distantFuture |
| isDistantPast: Bool | true if it is Instant.distantPast |
| truncateToMinute(): Instant | truncate to minute precision |
| truncateToSecond(): Instant | truncate to second precision |
| truncateToMillisecond(): Instant | truncate to millisecond precision |
| truncateToMicrosecond(): Instant | truncate to microsecond precision |
| toRFC3339(): String | format as RFC3339 string in UTC |
| toDateTime(tz?): DateTime | localize to a timezone |
| toDate(tz?): Date | convert to a calendar date in a timezone |
Calendar date: Date
Date represents a pure calendar date. It has no time-of-day and no attached timezone. Use it for values
like birthdays, due dates, invoice dates, and SQL DATE columns.
Constructing
import lyng.time
val today = Date()
val d1 = Date(2026, 4, 15)
val d2 = Date("2024-02-29")
val d3 = Date.parseIso("2024-02-29")
val d4 = Date(DateTime(2024, 5, 20, 15, 30, 45, "+02:00"))
val d5 = Date(Instant("2024-01-01T23:30:00Z"), "+02:00")
Date members
| member | description |
|---|---|
| year: Int | year component |
| month: Int | month component (1..12) |
| day: Int | day of month (alias dayOfMonth) |
| dayOfWeek: Int | day of week (1=Monday, 7=Sunday) |
| dayOfYear: Int | day of year (1..365/366) |
| isLeapYear: Bool | whether this date is in a leap year |
| lengthOfMonth: Int | number of days in this month |
| lengthOfYear: Int | 365 or 366 |
| toIsoString(): String | ISO YYYY-MM-DD string |
| toSortableString(): String | alias to toIsoString() |
| toDateTime(tz="Z"): DateTime | start-of-day DateTime in the specified timezone |
| atStartOfDay(tz="Z"): DateTime | alias to toDateTime() |
| addDays(n): Date | add or subtract calendar days |
| addMonths(n): Date | add or subtract months, normalizing end-of-month |
| addYears(n): Date | add or subtract years |
| daysUntil(other): Int | calendar days until other |
| daysSince(other): Int | calendar days since other |
| static today(tz?): Date | today in the specified timezone |
| static parseIso(s): Date | parse ISO YYYY-MM-DD |
Date arithmetic
Date supports only whole-day arithmetic. This is deliberate: calendar dates should not silently accept
sub-day durations.
import lyng.time
val d1 = Date(2026, 4, 15)
val d2 = d1.addDays(10)
assertEquals(Date(2026, 4, 25), d2)
assertEquals(Date(2026, 4, 18), d1 + 3.days)
assertEquals(Date(2026, 4, 12), d1 - 3.days)
assertEquals(10, d1.daysUntil(d2))
assertEquals(10, d2.daysSince(d1))
assertEquals(10, d2 - d1)
Date conversions
import lyng.time
val i = Instant("2024-01-01T23:30:00Z")
assertEquals(Date(2024, 1, 1), i.toDate("Z"))
assertEquals(Date(2024, 1, 2), i.toDate("+02:00"))
val dt = DateTime(2024, 5, 20, 15, 30, 45, "+02:00")
assertEquals(Date(2024, 5, 20), dt.date)
assertEquals(Date(2024, 5, 20), dt.toDate())
assertEquals(DateTime(2024, 5, 20, 0, 0, 0, "Z"), Date(2024, 5, 20).toDateTime("Z"))
assertEquals(DateTime(2024, 5, 20, 0, 0, 0, "+02:00"), Date(2024, 5, 20).atStartOfDay("+02:00"))
Calendar time: DateTime
DateTime represents a point in time in a specific timezone. It provides access to calendar components
such as year, month, day, and hour.
Constructing
import lyng.time
val now = DateTime.now()
val offsetTime = DateTime.now("+02:00")
val dt = Instant().toDateTime("Z")
val dt2 = DateTime(2024, 1, 1, 12, 0, 0, "Z")
val dt3 = DateTime.parseRFC3339("2024-01-01T12:00:00+02:00")
DateTime members
| member | description |
|---|---|
| year: Int | year component |
| month: Int | month component (1..12) |
| day: Int | day of month (alias dayOfMonth) |
| hour: Int | hour component (0..23) |
| minute: Int | minute component (0..59) |
| second: Int | second component (0..59) |
| dayOfWeek: Int | day of week (1=Monday, 7=Sunday) |
| timeZone: String | timezone ID string |
| date: Date | calendar date component |
| toInstant(): Instant | convert back to absolute Instant |
| toDate(): Date | extract the calendar date in this timezone |
| toUTC(): DateTime | shortcut to convert to UTC |
| toTimeZone(tz): DateTime | convert to another timezone |
| addMonths(n): DateTime | add/subtract months (normalizes end of month) |
| addYears(n): DateTime | add/subtract years |
| toRFC3339(): String | format with timezone offset |
| static now(tz?): DateTime | create DateTime with current time |
| static parseRFC3339(s): DateTime | parse RFC3339 string |
Arithmetic and normalization
DateTime handles calendar arithmetic correctly:
import lyng.time
val leapDay = Instant("2024-02-29T12:00:00Z").toDateTime("Z")
val nextYear = leapDay.addYears(1)
assertEquals(28, nextYear.day)
Duration class
Duration represents absolute elapsed time between two instants.
import lyng.time
val t1 = Instant()
delay(1.millisecond)
val t2 = Instant()
assert(t2 - t1 >= 1.millisecond)
assert(t2 - t1 < 100.millisecond)
>>> void
Duration values can be created from numbers using extensions on Int and Real:
n.millisecond,n.millisecondsn.second,n.secondsn.minute,n.minutesn.hour,n.hoursn.day,n.days
Larger units like months or years are calendar-dependent and are intentionally not part of Duration.
Each duration instance can be converted to numbers in these units:
d.microsecondsd.millisecondsd.secondsd.minutesd.hoursd.days
Example:
import lyng.time
assertEquals(60, 1.minute.seconds)
assertEquals(10.milliseconds, 0.01.seconds)
>>> void
Utility functions
delay(duration: Duration)
Suspends the current coroutine for at least the specified duration.