Package

dates

Calendar helpers on Time and Zone: boundaries, business days, ISO weeks, ranges, intervals, periods, RFC 2822, HTTP-date and ISO durations

0.1.0 MIT

Install

sspur add dates@^0.1.0

or in sspur.toml:

dates = { version = "^0.1.0" }

README

dates

Calendar helpers for SSPUR, built on the builtin Time, Duration and Zone: start and end of a day, week, month, quarter or year (in UTC or in a time zone, with DST handled), business days with holidays, the nth weekday of a month, Easter, ISO 8601 week and ordinal dates, date ranges, intervals, calendar differences and ages, ISO 8601 durations, strptime-style parsing, RFC 3339, RFC 2822 and HTTP dates, and relative descriptions such as "yesterday" or "last Tuesday".

Every function is pure. Functions that work in a zone take the Zone as an argument (from the builtin time_zone(name), fixed_zone(minutes) or local_zone()), so nothing reads the clock; pass now explicitly where a function needs it. Weekdays are numbered as Time.weekday numbers them: 1 is Monday, 7 is Sunday.

Example

use dates.{add_business_days, standard_calendar, nth_weekday, iso_week, format_iso_week, end_of_month_in, parse_http_date, parse_iso_duration, add_period, relative_date, date_range, Months}

fn main() -> Unit ! log, time
= do
  ny = time_zone("America/New_York").or(fixed_zone(-300))
  t = parse_time("2026-10-16T16:30:00Z").or(time_ms(0))
  cal = standard_calendar([date(2026, 10, 19).or(t)])
  log("due: {add_business_days(t, 3, cal)}")
  log("thanksgiving: {nth_weekday(2026, 11, 4, 4)}")
  log("week: {format_iso_week(t)} ({iso_week(t).week})")
  log("month ends in New York at {end_of_month_in(t, ny)}")
  log("http: {parse_http_date("Sun, 06 Nov 1994 08:49:37 GMT")}")
  log("plus P1M2D: {parse_iso_duration("P1M2D").map(p => add_period(t, p))}")
  log("{relative_date(parse_time("2026-10-13T09:00:00Z").or(t), t, ny)}")
  log("{date_range(date(2026, 1, 31).or(t), date(2026, 5, 1).or(t), 1, Months).map(_.day)}")
due: 2026-10-22T16:30:00Z
thanksgiving: some(2026-11-26T00:00:00Z)
week: 2026-W42-5 (42)
month ends in New York at 2026-11-01T03:59:59.999Z
http: some(1994-11-06T08:49:37Z)
plus P1M2D: some(2026-11-18T16:30:00Z)
last Tuesday
[31, 28, 31, 30]

(main performs time only because time_zone reads the system's zone database.)

API

Types

  • Period: years, months, weeks, days, hours, minutes, seconds and millis, the parts of an ISO 8601 duration; parts may be negative.
  • Interval: a half-open span {start, end}, start included, end excluded.
  • IsoWeek: an ISO week date {year, week, weekday}.
  • CalUnit: Days | Weeks | Months | Quarters | Years, the unit of add_units and ranges.
  • BusinessCalendar: {weekend, holidays}, the weekend weekdays and the holiday dates.

Calendar facts

  • is_leap_year(y), days_in_month(y, m), days_in_year(y): proleptic Gregorian.
  • quarter(t): 1 to 4.
  • weekday_name(d), weekday_abbr(d), month_name(m), month_abbr(m): English names.
  • epoch_day(t), from_epoch_day(n): days since 1970-01-01, rounded down.
  • same_day(a, b), same_day_in(a, b, z): same UTC or local date.
  • is_weekend(t): Saturday or Sunday.

Boundaries

  • UTC: start_of_day, end_of_day, start_of_week(t, first), end_of_week(t, first), start_of_month, end_of_month, start_of_quarter, end_of_quarter, start_of_year, end_of_year. Ends are the last millisecond (23:59:59.999). first is the weekday weeks start on (1 for ISO, 7 for the US).
  • In a zone: the same names with _in and a Zone (start_of_day_in(t, z), end_of_week_in(t, z, first), ...). A local day can be 23 or 25 hours long; where local midnight falls in a DST gap the day starts at the first instant after the gap.
  • resolve_local(z, wall): the instant a wall-clock time shows in zone z: the earlier one in an overlap, the moment the clocks jump in a gap.
  • local_date(t, z): the local date of t, as midnight UTC.

Business days

  • standard_calendar(holidays): Saturday and Sunday off, plus the holidays.
  • is_business_day(t, cal).
  • add_business_days(t, n, cal): forward or backward, keeping the time of day; n == 0 returns t.
  • business_days_between(a, b, cal): from a's date (included) to b's (excluded), negative when reversed; O(holidays), not O(days).
  • next_business_day, previous_business_day, roll_forward, roll_backward.

Weekdays and holidays

  • next_weekday(t, d), previous_weekday(t, d): strictly after or before, 1 to 7 days away.
  • next_or_same_weekday(t, d), previous_or_same_weekday(t, d).
  • nth_weekday(y, m, d, n): the 3rd Monday (n = 3) or the last Friday (n = -1); none for a missing fifth one.
  • last_weekday(y, m, d).
  • easter(y): Western Easter Sunday (anonymous Gregorian algorithm).

ISO week and ordinal dates

  • iso_week(t), iso_weeks_in_year(y) (52 or 53), from_iso_week(y, w, d).
  • format_iso_week(t) ("2026-W42-5"), parse_iso_week(s) (extended or basic, weekday optional).
  • format_ordinal_date(t) ("2026-289"), from_ordinal(y, n), parse_ordinal_date(s).

Ranges and differences

  • add_units(t, n, unit): months, quarters and years clamp the day to the month's end.
  • date_range(start, end, every, unit): end excluded; date_range_inclusive includes it. Each step is computed from start, so monthly ranges from the 31st don't drift.
  • days_between(a, b), months_between(a, b), years_between(a, b): whole calendar units.
  • age(birth, on): whole years; a February 29 birthday counts on February 28 in common years.
  • diff(a, b): the calendar difference as a Period, so add_period(a, diff(a, b)) == b.

Intervals

  • interval(a, b): none when b < a.
  • interval_length, interval_is_empty, covers(iv, t), encloses(outer, inner), overlaps, abuts, intersection, gap, hull, merge_intervals(xs).

Periods and ISO 8601 durations

  • period_zero(), negate_period(p), period_is_zero(p).
  • add_period(t, p): years and months first (clamping), then the exact parts.
  • period_duration(p): none when it has years or months; duration_period(d).
  • format_iso_duration(p) ("P1Y2M3DT4H5M6.5S", "-P1D", "PT0S"), parse_iso_duration(s).

Parsing and formatting

  • unix_seconds(t), from_unix_seconds(s).
  • format_rfc3339(t, z), parse_rfc3339(s): strict, requires seconds and an offset.
  • format_rfc2822(t, z), parse_rfc2822(s): e-mail dates, including the obsolete zone names and two-digit years of RFC 5322.
  • format_http_date(t), parse_http_date(s): IMF-fixdate, and the RFC 850 and asctime forms recipients must accept (RFC 9110).
  • parse_format(s, pat): strptime with the directives Time.format writes (%Y %m %d %H %M %S %L %j %u %a %A %b %B %F %T %%) plus %z. Fixed widths, so it reads back what Time.format writes.

Relative descriptions

  • relative_date(t, now, z): "today", "yesterday", "tomorrow", "last Tuesday", "next Friday", "last week", "in 2 weeks", "last month", "in 3 years".
  • relative_days(t, now, z): the same with numbers: "3 days ago", "in 5 days", "1 week ago".

Out of scope

  • Reading the clock: pass now() yourself, so the functions stay pure and testable.
  • Locale-aware names and formats: the builtin Locale (format_date, format_date_long) covers six locales; names here are English.
  • Non-Gregorian calendars (Julian, Hebrew, Islamic, Chinese), Orthodox Easter, and holiday tables for particular countries (pass the holidays in a BusinessCalendar).
  • Leap seconds, which Time can't represent (parse_rfc3339 rejects ":60").
  • Duration-based phrases such as "3 minutes ago" or "about 2 hours": see the humanize package. This package describes calendar days.
  • Cron expressions and recurrence rules (RFC 5545 RRULE), as in python-dateutil.rrule; date_range covers fixed steps.
  • Time zone abbreviations in parsing (%Z): they are ambiguous (CST is three different zones).

Compared with Python's datetime and dateutil, Java's java.time and npm's date-fns, the boundaries, business days, ISO weeks, ranges, intervals, periods and RFC formats are covered; formatting itself is the builtin Time.format and Time.format_in.

Install

sspur add dates

MIT licensed.

API

Period type

pub type Period = {years: Int, months: Int, weeks: Int, days: Int, hours: Int, minutes: Int, seconds: Int, millis: Int} derive Eq, Show

A calendar period: the parts of an ISO 8601 duration. Years and months have no fixed length, so they are applied with Time.add_months (clamping the day to the month's end); weeks and days are 7 and 1 days of 24 hours; the rest are exact. Parts may be negative.

Interval type

pub type Interval = {start: Time, end: Time} derive Eq, Show

A half-open span of time, from start (included) to end (excluded), with start <= end.

IsoWeek type

pub type IsoWeek = {year: Int, week: Int, weekday: Int} derive Eq, Show

An ISO 8601 week date: the ISO week-numbering year, the week (1 to 53) and the weekday (1 is Monday).

CalUnit type

pub type CalUnit = Days | Weeks | Months | Quarters | Years derive Eq, Show

The unit of a step in a date range, or of add_units.

BusinessCalendar type

pub type BusinessCalendar = {weekend: List[Int], holidays: List[Time]} derive Eq, Show

Which days are business days: the weekdays of the weekend (1 is Monday, 7 Sunday) and a list of holidays (only their UTC dates count).

is_leap_year fn

fn is_leap_year(y: Int) -> Bool

Whether y is a leap year in the proleptic Gregorian calendar (year 0 is a leap year).

days_in_month fn

fn days_in_month(y: Int, m: Int where _ >= 1 and _ <= 12) -> Int

The number of days in month m (1 to 12) of year y.

days_in_year fn

fn days_in_year(y: Int) -> Int

The number of days in year y: 365 or 366.

quarter fn

fn quarter(t: Time) -> Int

The quarter of the year t is in (UTC): 1 for January to March, up to 4.

weekday_name fn

fn weekday_name(d: Int where _ >= 1 and _ <= 7) -> Str

The English name of weekday d (1 is Monday): "Monday" to "Sunday".

weekday_abbr fn

fn weekday_abbr(d: Int where _ >= 1 and _ <= 7) -> Str

The three-letter English abbreviation of weekday d: "Mon" to "Sun".

month_name fn

fn month_name(m: Int where _ >= 1 and _ <= 12) -> Str

The English name of month m: "January" to "December".

month_abbr fn

fn month_abbr(m: Int where _ >= 1 and _ <= 12) -> Str

The three-letter English abbreviation of month m: "Jan" to "Dec".

epoch_day fn

fn epoch_day(t: Time) -> Int

The number of days from 1970-01-01 to the UTC date of t, rounded down (negative before 1970).

from_epoch_day fn

fn from_epoch_day(n: Int) -> Time

Midnight UTC of the date n days after 1970-01-01.

same_day fn

fn same_day(a: Time, b: Time) -> Bool

Whether a and b fall on the same UTC date.

same_day_in fn

fn same_day_in(a: Time, b: Time, z: Zone) -> Bool

Whether a and b fall on the same local date in zone z.

is_weekend fn

fn is_weekend(t: Time) -> Bool

Whether t is on a Saturday or a Sunday (UTC).

start_of_day fn

fn start_of_day(t: Time) -> Time

Midnight UTC at the start of t's date.

end_of_day fn

fn end_of_day(t: Time) -> Time

The last millisecond of t's UTC date (23:59:59.999).

start_of_week fn

fn start_of_week(t: Time, first: Int where _ >= 1 and _ <= 7) -> Time

Midnight at the start of the week containing t, where weeks start on weekday first (1 is Monday, as in ISO 8601; 7 is Sunday, as in the US).

end_of_week fn

fn end_of_week(t: Time, first: Int where _ >= 1 and _ <= 7) -> Time

The last millisecond of the week containing t, where weeks start on weekday first.

start_of_month fn

fn start_of_month(t: Time) -> Time

Midnight UTC on the first day of t's month.

end_of_month fn

fn end_of_month(t: Time) -> Time

The last millisecond of t's month (UTC).

start_of_quarter fn

fn start_of_quarter(t: Time) -> Time

Midnight UTC on the first day of t's quarter (January, April, July or October 1).

end_of_quarter fn

fn end_of_quarter(t: Time) -> Time

The last millisecond of t's quarter (UTC).

start_of_year fn

fn start_of_year(t: Time) -> Time

Midnight UTC on January 1 of t's year.

end_of_year fn

fn end_of_year(t: Time) -> Time

The last millisecond of t's year (UTC): December 31, 23:59:59.999.

resolve_local fn

fn resolve_local(z: Zone, wall: Time) -> Time

The instant at which the wall clock of zone z shows wall (a Time whose UTC fields are read as local fields, as Zone.local returns). In an overlap (clocks turned back) it is the earlier instant; in a gap (clocks turned forward, so wall never shows) it is the first instant after the gap, the moment the clocks jump.

start_of_day_in fn

fn start_of_day_in(t: Time, z: Zone) -> Time

The first instant of t's local date in zone z (usually local midnight; where midnight falls in a DST gap, the first instant after the gap).

end_of_day_in fn

fn end_of_day_in(t: Time, z: Zone) -> Time

The last millisecond of t's local date in zone z: one millisecond before the start of the next local date, so a day can be 23 or 25 hours long.

start_of_week_in fn

fn start_of_week_in(t: Time, z: Zone, first: Int where _ >= 1 and _ <= 7) -> Time

The first instant of the local week containing t in zone z, weeks starting on first.

end_of_week_in fn

fn end_of_week_in(t: Time, z: Zone, first: Int where _ >= 1 and _ <= 7) -> Time

The last millisecond of the local week containing t in zone z, weeks starting on first.

start_of_month_in fn

fn start_of_month_in(t: Time, z: Zone) -> Time

The first instant of t's local month in zone z.

end_of_month_in fn

fn end_of_month_in(t: Time, z: Zone) -> Time

The last millisecond of t's local month in zone z.

start_of_quarter_in fn

fn start_of_quarter_in(t: Time, z: Zone) -> Time

The first instant of t's local quarter in zone z.

end_of_quarter_in fn

fn end_of_quarter_in(t: Time, z: Zone) -> Time

The last millisecond of t's local quarter in zone z.

start_of_year_in fn

fn start_of_year_in(t: Time, z: Zone) -> Time

The first instant of t's local year in zone z.

end_of_year_in fn

fn end_of_year_in(t: Time, z: Zone) -> Time

The last millisecond of t's local year in zone z.

local_date fn

fn local_date(t: Time, z: Zone) -> Time

The local date of t in zone z, as midnight UTC of that date (for date arithmetic and comparisons that should ignore the zone afterwards).

standard_calendar fn

fn standard_calendar(holidays: List[Time]) -> BusinessCalendar

A calendar with Saturday and Sunday as the weekend and the given holidays.

is_business_day fn

fn is_business_day(t: Time, cal: BusinessCalendar) -> Bool

Whether the UTC date of t is a business day of cal: not a weekend day and not a holiday.

add_business_days fn

fn add_business_days(t: Time, n: Int, cal: BusinessCalendar) -> Time

t moved by n business days of cal (backwards when n is negative), keeping the time of day. Counting starts from the day after (or before) t, so t itself need not be a business day; n == 0 returns t unchanged. The weekend must leave at least one business day a week.

business_days_between fn

fn business_days_between(a: Time, b: Time, cal: BusinessCalendar) -> Int

The number of business days of cal from the date of a (included) to the date of b (excluded); negative when b is before a. Times of day are ignored.

next_business_day fn

fn next_business_day(t: Time, cal: BusinessCalendar) -> Time

The first business day of cal strictly after t's date, keeping the time of day.

previous_business_day fn

fn previous_business_day(t: Time, cal: BusinessCalendar) -> Time

The last business day of cal strictly before t's date, keeping the time of day.

roll_forward fn

fn roll_forward(t: Time, cal: BusinessCalendar) -> Time

t if it is a business day of cal, else the next business day (the "following" convention).

roll_backward fn

fn roll_backward(t: Time, cal: BusinessCalendar) -> Time

t if it is a business day of cal, else the previous business day ("preceding").

next_weekday fn

fn next_weekday(t: Time, d: Int where _ >= 1 and _ <= 7) -> Time

The first instant after t that is on weekday d, at the same time of day: 1 to 7 days later.

previous_weekday fn

fn previous_weekday(t: Time, d: Int where _ >= 1 and _ <= 7) -> Time

The last instant before t that is on weekday d, at the same time of day: 1 to 7 days earlier.

next_or_same_weekday fn

fn next_or_same_weekday(t: Time, d: Int where _ >= 1 and _ <= 7) -> Time

t if it is on weekday d, else next_weekday(t, d).

previous_or_same_weekday fn

fn previous_or_same_weekday(t: Time, d: Int where _ >= 1 and _ <= 7) -> Time

t if it is on weekday d, else previous_weekday(t, d).

nth_weekday fn

fn nth_weekday(y: Int, m: Int where _ >= 1 and _ <= 12, d: Int where _ >= 1 and _ <= 7, n: Int where _ != 0 and _ >= -5 and _ <= 5) -> Opt[Time]

The nth weekday d of month m of year y, at midnight UTC: n is 1 to 5 counting from the start (3 and Monday is the third Monday), or -1 to -5 counting from the end (-1 and Friday is the last Friday). none when the month has no such day (a fifth Monday).

last_weekday fn

fn last_weekday(y: Int, m: Int where _ >= 1 and _ <= 12, d: Int where _ >= 1 and _ <= 7) -> Time

The last weekday d of month m of year y, at midnight UTC (the last Friday).

easter fn

fn easter(y: Int where _ >= 1) -> Time

Western (Gregorian) Easter Sunday of year y, at midnight UTC, by the anonymous Gregorian algorithm (Meeus, Jones, Butcher). It is computed for any year from 1, proleptically before 1583.

iso_week fn

fn iso_week(t: Time) -> IsoWeek

The ISO 8601 week date of t (UTC): weeks start on Monday and week 1 is the week with the year's first Thursday, so early January can be in the previous year's week 52 or 53 and late December in the next year's week 1.

iso_weeks_in_year fn

fn iso_weeks_in_year(y: Int) -> Int

The number of ISO weeks in ISO year y: 53 when January 1 is a Thursday, or a Wednesday in a leap year, else 52.

from_iso_week fn

fn from_iso_week(y: Int, w: Int, d: Int) -> Opt[Time]

Midnight UTC of ISO week date (y, w, d); none when the week or weekday is out of range.

format_iso_week fn

fn format_iso_week(t: Time) -> Str

The ISO week date of t as text: "2026-W42-5".

parse_iso_week fn

fn parse_iso_week(s: Str) -> Opt[Time]

Parses an ISO week date, extended ("2026-W42-5", "2026-W42") or basic ("2026W425", "2026W42"); a missing weekday is Monday. none for anything else or a week the year doesn't have.

format_ordinal_date fn

fn format_ordinal_date(t: Time) -> Str

The ordinal date of t (UTC) as text: year and day of the year, "2026-289".

from_ordinal fn

fn from_ordinal(y: Int, n: Int) -> Opt[Time]

Midnight UTC of day n (1 to 365 or 366) of year y; none when n is out of range.

parse_ordinal_date fn

fn parse_ordinal_date(s: Str) -> Opt[Time]

Parses an ordinal date, "2026-289" or "2026289".

add_units fn

fn add_units(t: Time, n: Int, unit: CalUnit) -> Time

t moved by n units (backwards when negative). Months, quarters and years use Time.add_months, which clamps the day to the end of the month (January 31 plus one month is February 28 or 29); days and weeks are exact multiples of 24 hours.

date_range fn

fn date_range(start: Time, end: Time, every: Int where _ > 0, unit: CalUnit) -> List[Time]

The times start, start + every units, start + 2 * every units, ... that are before end (end excluded); empty when end <= start. Each element is computed from start (add_units(start, k * every, unit)), so a monthly range from January 31 gives February 28, March 31, April 30 rather than drifting to the 28th.

date_range_inclusive fn

fn date_range_inclusive(start: Time, end: Time, every: Int where _ > 0, unit: CalUnit) -> List[Time]

Like date_range but end is included when the steps land on it exactly.

days_between fn

fn days_between(a: Time, b: Time) -> Int

The number of calendar days from the UTC date of a to the UTC date of b (negative when b is earlier); times of day are ignored.

months_between fn

fn months_between(a: Time, b: Time) -> Int

The number of whole months from a to b: the largest m with a.add_months(m) <= b, or the negation of months_between(b, a) when b is before a.

years_between fn

fn years_between(a: Time, b: Time) -> Int

The number of whole years from a to b, by the rule of months_between.

age fn

fn age(birth: Time, on: Time) -> Int

The age in whole years on date on of someone born on birth, from their dates (UTC; times of day are ignored). A February 29 birthday is reached on February 28 in common years. Negative when on is before birth.

diff fn

fn diff(a: Time, b: Time) -> Period

The calendar difference from a to b as years, months, days, hours, minutes, seconds and milliseconds (weeks 0), such that add_period(a, diff(a, b)) == b when a <= b (except when a month end clamps). When b is before a every part is negated from diff(b, a).

interval fn

fn interval(a: Time, b: Time) -> Opt[Interval]

The interval from a to b; none when b is before a.

interval_length fn

fn interval_length(iv: Interval) -> Duration

The length of interval iv.

interval_is_empty fn

fn interval_is_empty(iv: Interval) -> Bool

Whether iv is empty (its start equals its end).

covers fn

fn covers(iv: Interval, t: Time) -> Bool

Whether instant t is in iv: start <= t < end.

encloses fn

fn encloses(outer: Interval, inner: Interval) -> Bool

Whether outer contains all of inner.

overlaps fn

fn overlaps(a: Interval, b: Interval) -> Bool

Whether a and b share some instant (empty intervals overlap nothing).

abuts fn

fn abuts(a: Interval, b: Interval) -> Bool

Whether one interval ends exactly where the other starts.

intersection fn

fn intersection(a: Interval, b: Interval) -> Opt[Interval]

The part a and b share; none when they don't overlap.

gap fn

fn gap(a: Interval, b: Interval) -> Opt[Interval]

The interval between a and b when they are apart; none when they overlap or abut.

hull fn

fn hull(a: Interval, b: Interval) -> Interval

The smallest interval that contains both a and b.

merge_intervals fn

fn merge_intervals(xs: List[Interval]) -> List[Interval]

The intervals sorted by start, with overlapping and abutting ones merged; empty ones dropped.

period_zero fn

fn period_zero() -> Period

The period with every part 0.

negate_period fn

fn negate_period(p: Period) -> Period

The period with every part of p negated.

period_is_zero fn

fn period_is_zero(p: Period) -> Bool

Whether every part of p is 0.

add_period fn

fn add_period(t: Time, p: Period) -> Time

t moved by period p: first the years and months together with Time.add_months (clamping the day to the month's end), then the weeks, days, hours, minutes, seconds and milliseconds as exact durations.

period_duration fn

fn period_duration(p: Period) -> Opt[Duration]

The exact length of p when it has no years or months (days count 24 hours); none otherwise.

duration_period fn

fn duration_period(d: Duration) -> Period

Duration d split into days, hours, minutes, seconds and milliseconds (no weeks, months or years); every part has the sign of d.

format_iso_duration fn

fn format_iso_duration(p: Period) -> Str

p as an ISO 8601 duration: "P1Y2M3DT4H5M6.5S", "P2W", "PT0S" for zero. When every non-zero part is negative the text starts with "-" ("-P1D"); mixed signs are written per part ("P1M-2D"), as java.time does. Milliseconds are written as a fraction of the seconds.

parse_iso_duration fn

fn parse_iso_duration(s: Str) -> Opt[Period]

Parses an ISO 8601 duration: "P1Y2M10DT2H30M", "PT1.5S", "P3W", "-P1D", "P1M-2D". Units must come in the order Y M W D, then "T" and H M S; at least one part is required, and "T" must be followed by one. Only seconds may have a fraction ("." or ","), kept to milliseconds and truncated beyond. A leading "-" negates every part. none for anything else, or for a value that does not fit an Int.

unix_seconds fn

fn unix_seconds(t: Time) -> Int

The Unix time of t in whole seconds, rounded down.

from_unix_seconds fn

fn from_unix_seconds(s: Int) -> Opt[Time]

The time s seconds after 1970-01-01T00:00:00Z; none when s * 1000 overflows an Int.

format_rfc3339 fn

fn format_rfc3339(t: Time, z: Zone) -> Str

t as RFC 3339 text in zone z, with milliseconds only when they are not 0: "2026-10-16T21:43:27+05:30", "2026-10-16T16:13:27.123Z" in UTC.

parse_rfc3339 fn

fn parse_rfc3339(s: Str) -> Opt[Time]

Parses an RFC 3339 timestamp: date, "T" (or "t" or a space), hours, minutes and seconds, an optional fraction (kept to milliseconds, truncated beyond), and "Z" or an offset "+05:30". A leap second (":60") is rejected, as Time can't hold one. Stricter than the builtin parse_time, which also takes dates alone and offsets without minutes.

format_rfc2822 fn

fn format_rfc2822(t: Time, z: Zone) -> Str

t as an RFC 5322 (RFC 2822) date in zone z: "Fri, 16 Oct 2026 21:43:27 +0530".

parse_rfc2822 fn

fn parse_rfc2822(s: Str) -> Opt[Time]

Parses an RFC 5322 (RFC 2822) date: an optional weekday and comma, a day of one or two digits, an English month abbreviation, a year (four digits; the obsolete two-digit years are 2000 to 2049 for 00 to 49 and 1950 to 1999 for 50 to 99, three-digit years add 1900), hours and minutes with optional seconds, and a zone: "+0530", "-0000", or the obsolete "UT", "GMT", "Z", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT" (military letters count as +0000, as RFC 5322 says). Names are case-insensitive, comments in parentheses and extra whitespace are ignored. A weekday that doesn't match the date gives none.

format_http_date fn

fn format_http_date(t: Time) -> Str

t as an HTTP date (IMF-fixdate, RFC 9110 section 5.6.7), always in GMT: "Sun, 06 Nov 1994 08:49:37 GMT".

parse_http_date fn

fn parse_http_date(s: Str) -> Opt[Time]

Parses an HTTP date in the three forms RFC 9110 requires recipients to accept: IMF-fixdate ("Sun, 06 Nov 1994 08:49:37 GMT"), the obsolete RFC 850 form ("Sunday, 06-Nov-94 08:49:37 GMT", two-digit years 00 to 69 are 2000 to 2069 and 70 to 99 are 1970 to 1999) and asctime ("Sun Nov 6 08:49:37 1994"). Names are case-sensitive, as the RFC specifies. A weekday that doesn't match the date gives none.

parse_format fn

fn parse_format(s: Str, pat: Str) -> Opt[Time]

Parses s by strftime pattern pat, the directives Time.format writes plus %z: %Y (four digits), %m %d %H %M %S (two digits), %L (three-digit milliseconds), %j (three-digit day of the year), %u (weekday digit, 1 is Monday), %a %A %b %B (English names, case-insensitive), %F (%Y-%m-%d), %T (%H:%M:%S), %z ("Z", "+0530" or "+05:30") and %%; every other character must match itself. Missing fields default to 1970-01-01 00:00:00.000 UTC; %j sets the month and day unless %m or %d is given, and then they must agree. A weekday that disagrees with the date, an unknown directive, an invalid date or leftover text gives none. Zone names (%Z) can't be parsed, since abbreviations are ambiguous.

relative_date fn

fn relative_date(t: Time, now: Time, z: Zone) -> Str

How the local date of t relates to the local date of now in zone z, in words: "today", "yesterday", "tomorrow", a weekday within six days ("last Tuesday", "next Friday"), "last week" or "next week" (7 to 13 days), "3 weeks ago" or "in 2 weeks" (under a month), "last month", "in 5 months", "last year", "in 3 years". Times of day are ignored; whole months and years are counted by months_between of the local dates.

relative_days fn

fn relative_days(t: Time, now: Time, z: Zone) -> Str

Like relative_date but with numbers instead of weekday names and "last"/"next": "today", "yesterday", "tomorrow", "3 days ago", "in 5 days", "1 week ago", "in 2 months", "4 years ago".

Versions

VersionPublishedCommitSourceEffects
0.1.0 2026-10-168f109a382dsnapshot