date
ISO-8601 string parsing and duration-branded numbers.
import type {
Year, Month, Day, TimeOf, IsLeapYear, DaysInMonth, DayOfWeek,
Duration, Milliseconds, Seconds, Minutes, Hours, Days, Weeks, AnyDuration,
} from '@gentleduck/ttest/date'
ISO-8601 string types ('2026-04-23', '2026-04-23T12:34:56Z') plus branded duration scalars.
Year
type Year<S extends string>
Extract year from a date string.
type Y = Year<'2026-04-23'> // 2026
Month
type Month<S extends string>
Extract month (1–12, leading zero stripped).
type M = Month<'2026-04-23'> // 4
Day
type Day<S extends string>
Extract day of month.
type D = Day<'2026-04-23T12:00:00Z'> // 23
TimeOf
type TimeOf<S extends string>
Extract HH:MM:SS from an ISO datetime; never if absent.
type T = TimeOf<'2026-04-23T12:34:56Z'> // '12:34:56'
IsLeapYear
type IsLeapYear<Y extends number>
Gregorian leap year: divisible by 4, except centuries not divisible by 400. Implemented by string-suffix matching to avoid tuple arithmetic.
type a = IsLeapYear<2024> // true
type b = IsLeapYear<2100> // false
type c = IsLeapYear<2000> // true
DaysInMonth
type DaysInMonth<Y extends number, M extends number>
Days in Month of Year. February returns 29 in leap years.
type a = DaysInMonth<2024, 2> // 29
type b = DaysInMonth<2026, 2> // 28
type c = DaysInMonth<2026, 4> // 30
DayOfWeek
type DayOfWeek = 1 | 2 | 3 | 4 | 5 | 6 | 7
ISO-8601 day of week: Mon = 1 ... Sun = 7.
Duration brands
type Duration<U extends string> = number & { readonly [_UNIT]: U }
type Milliseconds = Duration<'ms'>
type Seconds = Duration<'s'>
type Minutes = Duration<'min'>
type Hours = Duration<'h'>
type Days = Duration<'d'>
type Weeks = Duration<'w'>
type AnyDuration = Milliseconds | Seconds | Minutes | Hours | Days | Weeks
Branded numbers — runtime is still a number, but Milliseconds and Seconds are not assignable to each other.
function sleep(ms: Milliseconds) { /* ... */ }
sleep(500 as Milliseconds)
sleep(2 as Seconds) // Error — Seconds is not Milliseconds.