Module
LinkDate validates calendar components, exchanges exact YYYY-MM-DD text, and adds signed whole days without time zones or a host clock.
Why
LinkA shared calendar and fixed text form give date construction, observation, and arithmetic the same meaning on every host.
Availability
LinkImport std.date in language mode 5.21. The current compiler selects std/date.tpz for this language mode. Availability in other released editions and inclusion in each installed distribution are Pending.
Exported types
Linkexport type Date = Date
Type members keep the compiler-owned type name shown in their declarations. Importing a module does not create a new type identity.
Public API
LinkPending contracts
LinkConstruct valid Gregorian civil dates
LinkConstruct valid Gregorian civil dates
Date is an immutable proleptic Gregorian calendar date from 0001-01-01 through 9999-12-31. It contains no time of day, time zone, or locale. The same calendar rules apply before and after 1582.
Date.fromYmd accepts years from 1 through 9999 and months from 1 through 12. The day must be a real day of the selected month and year. All range endpoints are included.
A year divisible by 4 is a leap year except a century year that is not divisible by 400. February has 29 days in a leap year and 28 otherwise. April, June, September, and November have 30 days. The other months have 31.
Valid components return Ok containing that date. Year zero, a year outside the supported range, an invalid month, and an invalid day return Err containing a descriptive nonempty string. Components are not normalized into another date.
The error string describes the failed construction and has no stable machine readable format. Callers branch on Ok or Err.
Date.year, Date.month, and Date.day are calls with no arguments. They return the validated components of the receiver. A valid Date makes these observations total and leaves the receiver unchanged.
Arguments are evaluated once in source order before validation. An earlier fault prevents later evaluation. Constructors and observations do not read a host clock or perform external effects.
Why
Validating civil dates at construction gives every date observation a definite value. A shared Gregorian calendar and year range make dates independent of locale, time zones, and historical calendar cutovers.
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 34 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 76 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 110 - compiler/crates/topaz_product_runtime/src/lib.rs at
912a5e86e1f9· 17 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 164 -
topaz.stdlib.contract.date-calendaratworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Read leap day and both supported endpoints
Linkimport std.date { Date }import std.testimport std.iolet leap = test.assertOk(Date.fromYmd(2000, 2, 29))io.writeLine("{leap.year()} {leap.month()} {leap.day()}")io.writeLine(leap.toIso())io.writeLine(test.assertOk(Date.fromYmd(1, 1, 1)).toIso())io.writeLine(test.assertOk(Date.fromYmd(9999, 12, 31)).toIso())
2000 2 29 2000-02-29 0001-01-01 9999-12-31
Expected result
Expected result
Execution completes. Exit code 0.
2000 2 292000-02-290001-01-019999-12-31
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 34 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 76 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 110 - compiler/crates/topaz_product_runtime/src/lib.rs at
912a5e86e1f9· 17 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 164 -
topaz.stdlib.contract.date-calendaratworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Reject invalid years, months, and days without normalization
Linkimport std.date { Date }import std.iofunction reject(year: int, month: int, day: int) -> () {match (Date.fromYmd(year, month, day)) {case Err(_) => io.writeLine("rejected")case Ok(_) => io.writeLine("accepted")}}reject(1900, 2, 29)reject(2023, 2, 29)reject(2024, 0, 1)reject(2024, 13, 1)reject(2024, 4, 31)reject(2024, 1, 0)reject(2024, 1, 32)reject(-1, 1, 1)reject(10000, 1, 1)reject(9223372036854775807, 1, 1)
rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected
Expected result
Expected result
Execution completes. Exit code 0.
rejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejected
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 34 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 76 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 110 - compiler/crates/topaz_product_runtime/src/lib.rs at
912a5e86e1f9· 17 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 164 -
topaz.stdlib.contract.date-calendaratworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Reject year zero during component construction
Linkimport std.date { Date }import std.iomatch (Date.fromYmd(0, 1, 1)) {case Err(_) => io.writeLine("year zero rejected")case Ok(_) => io.writeLine("accepted")}
year zero rejected
Expected result
Expected result
Execution completes. Exit code 0.
year zero rejected
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 34 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 76 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 110 - compiler/crates/topaz_product_runtime/src/lib.rs at
912a5e86e1f9· 17 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 164 -
topaz.stdlib.contract.date-calendaratworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Evaluate all components before date validation
Linkimport std.date { Date }import std.iofunction observed(label: string, value: int) -> int {io.writeLine(label)return value}match (Date.fromYmd(observed("year", 2024), observed("month", 13), observed("day", 1))) {case Err(_) => io.writeLine("rejected")case Ok(_) => io.writeLine("accepted")}
year month day rejected
Expected result
Expected result
Execution completes. Exit code 0.
yearmonthdayrejected
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 34 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 76 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 110 - compiler/crates/topaz_product_runtime/src/lib.rs at
912a5e86e1f9· 17 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 164 -
topaz.stdlib.contract.date-calendaratworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Exchange dates in one fixed ISO form
LinkExchange dates in one fixed ISO form
Date.parseIso accepts exactly ten ASCII characters in the form YYYY-MM-DD. The year has four digits and the month and day each have two digits. Hyphens occur after the year and month.
The components must form a valid Gregorian date in the same range as Date.fromYmd. Valid text returns Ok containing that date. Invalid grammar or an invalid calendar date returns Err containing a descriptive nonempty string.
Parsing does not trim whitespace or normalize another spelling. Signs, omitted zero padding, alternate separators, non ASCII digits, timestamps, time zones, and extra characters are rejected.
Date.toIso returns exactly the same ten character form using ASCII digits and zero padding. It has no locale or time zone parameter and leaves the receiver unchanged.
Parsing the formatted text recovers the original date. Formatting accepted input returns that same text. Date.fromYmd and Date.parseIso establish the same component and range invariants.
The receiver or argument is evaluated once before the operation. These operations do not change the input or perform external effects. Error strings have no stable machine readable format.
Why
One fixed calendar format makes date text predictable to store, compare lexically, and exchange. Sharing validation with component construction prevents two constructors from assigning different meanings to the same date.
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 34 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 76 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 110 - compiler/crates/topaz_product_runtime/src/lib.rs at
912a5e86e1f9· 17 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 130 -
topaz.stdlib.contract.date-isoatworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Parse canonical date text and preserve its components
Linkimport std.date { Date }import std.testimport std.iofor text in (["0001-01-01", "1582-10-10", "2000-02-29", "9999-12-31"]) {let date = test.assertOk(Date.parseIso(text))io.writeLine(date.toIso())}let leap = test.assertOk(Date.parseIso("2000-02-29"))io.writeLine("{leap.year()} {leap.month()} {leap.day()}")
0001-01-01 1582-10-10 2000-02-29 9999-12-31 2000 2 29
Expected result
Expected result
Execution completes. Exit code 0.
0001-01-011582-10-102000-02-299999-12-312000 2 29
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 34 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 76 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 110 - compiler/crates/topaz_product_runtime/src/lib.rs at
912a5e86e1f9· 17 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 130 -
topaz.stdlib.contract.date-isoatworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Reject alternate spellings and invalid calendar dates
Linkimport std.date { Date }import std.iofor text in (["", "2024-2-09", "2024-02-9", "2024/02/09", " 2024-02-09", "2024-02-09 ", "2024-02-09T00:00:00Z", "+2024-02-09", "2024-02-09", "2024-02-30", "1900-02-29", "10000-01-01"]) {match (Date.parseIso(text)) {case Err(_) => io.writeLine("rejected")case Ok(_) => io.writeLine("accepted")}}
rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected rejected
Expected result
Expected result
Execution completes. Exit code 0.
rejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejectedrejected
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 34 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 76 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 110 - compiler/crates/topaz_product_runtime/src/lib.rs at
912a5e86e1f9· 17 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 130 -
topaz.stdlib.contract.date-isoatworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Reject year zero in ISO text
Linkimport std.date { Date }import std.iomatch (Date.parseIso("0000-01-01")) {case Err(_) => io.writeLine("year zero rejected")case Ok(_) => io.writeLine("accepted")}
year zero rejected
Expected result
Expected result
Execution completes. Exit code 0.
year zero rejected
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 34 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 76 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 110 - compiler/crates/topaz_product_runtime/src/lib.rs at
912a5e86e1f9· 17 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 130 -
topaz.stdlib.contract.date-isoatworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Add whole calendar days within the civil date range
LinkAdd whole calendar days within the civil date range
Date.addDays accepts a signed int count of whole calendar days. Positive values move forward, negative values move backward, and zero returns the same date value.
The result follows the same proleptic Gregorian calendar as Date.fromYmd. Month boundaries, leap days, and year boundaries are crossed by counting calendar days. Historical calendar cutovers and daylight saving changes do not alter the result.
The result must remain from 0001-01-01 through 9999-12-31. A count whose mathematical result is outside that range raises TPZ4004 during execution. An intermediate integer overflow also raises TPZ4004.
An invalid result does not wrap, saturate, return a partial date, or change the receiver. Evaluation stops at the fault before later statements run.
The receiver is evaluated before the day count and each is evaluated once. An earlier fault prevents later evaluation. The operation has no external effects.
Adding a count and then its negative recovers the starting date when both results lie in range. Date.toIso and the component accessors observe the resulting date without changing it.
Why
Calendar arithmetic should depend on a date and a day count. A bounded result with an explicit overflow fault prevents wraparound from silently producing a different civil date.
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 3 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 144 - compiler/crates/topaz_value/src/lib.rs at
912a5e86e1f9· 164 -
topaz.stdlib.contract.date-arithmeticatworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Cross leap days, years, and the full supported interval
Linkimport std.date { Date }import std.testimport std.iolet leap = test.assertOk(Date.fromYmd(2000, 2, 28))io.writeLine(leap.addDays(1).toIso())io.writeLine(leap.addDays(2).toIso())io.writeLine(leap.toIso())io.writeLine(test.assertOk(Date.fromYmd(1900, 2, 28)).addDays(1).toIso())io.writeLine(test.assertOk(Date.fromYmd(1582, 10, 4)).addDays(1).toIso())io.writeLine(test.assertOk(Date.fromYmd(2000, 1, 1)).addDays(-1).toIso())io.writeLine(leap.addDays(0).toIso())io.writeLine(test.assertOk(Date.fromYmd(1, 1, 1)).addDays(3652058).toIso())io.writeLine(test.assertOk(Date.fromYmd(9999, 12, 31)).addDays(-3652058).toIso())
2000-02-29 2000-03-01 2000-02-28 1900-03-01 1582-10-05 1999-12-31 2000-02-28 9999-12-31 0001-01-01
Expected result
Expected result
Execution completes. Exit code 0.
2000-02-292000-03-012000-02-281900-03-011582-10-051999-12-312000-02-289999-12-310001-01-01
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 3 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 144 - compiler/crates/topaz_value/src/lib.rs at
912a5e86e1f9· 164 -
topaz.stdlib.contract.date-arithmeticatworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Reject the day after the final supported date
Linkimport std.date { Date }import std.testimport std.iolet date = test.assertOk(Date.fromYmd(9999, 12, 31))date.addDays(1)io.writeLine("after")
No stdout.
Expected result
Expected result
Rejection during run. Nonzero exit.
TPZ4004stdlib-date-range-overflow atmain.tpz:6:1
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 3 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 144 - compiler/crates/topaz_value/src/lib.rs at
912a5e86e1f9· 164 -
topaz.stdlib.contract.date-arithmeticatworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Reject the day before the first supported date
Linkimport std.date { Date }import std.testimport std.iolet date = test.assertOk(Date.fromYmd(1, 1, 1))date.addDays(-1)io.writeLine("after")
No stdout.
Expected result
Expected result
Rejection during run. Nonzero exit.
TPZ4004stdlib-date-range-overflow atmain.tpz:6:1
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 3 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 144 - compiler/crates/topaz_value/src/lib.rs at
912a5e86e1f9· 164 -
topaz.stdlib.contract.date-arithmeticatworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Reject a day count that overflows signed arithmetic
Linkimport std.date { Date }import std.testimport std.iolet date = test.assertOk(Date.fromYmd(1, 1, 1))date.addDays(-9223372036854775808)io.writeLine("after")
No stdout.
Expected result
Expected result
Rejection during run. Nonzero exit.
TPZ4004stdlib-date-range-overflow atmain.tpz:6:1
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 3 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 144 - compiler/crates/topaz_value/src/lib.rs at
912a5e86e1f9· 164 -
topaz.stdlib.contract.date-arithmeticatworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi
Evaluate a date receiver before its day count
Linkimport std.date { Date }import std.testimport std.iofunction receiver() -> Date {io.writeLine("date")return test.assertOk(Date.fromYmd(2024, 2, 28))}function days() -> int {io.writeLine("days")return 1}let next = receiver().addDays(days())io.writeLine(next.toIso())
date days 2024-02-29
Expected result
Expected result
Execution completes. Exit code 0.
datedays2024-02-29
Source evidence
- compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 3 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 13 - compiler/crates/topaz_value/src/value/builtins/date.rs at
912a5e86e1f9· 144 - compiler/crates/topaz_value/src/lib.rs at
912a5e86e1f9· 164 -
topaz.stdlib.contract.date-arithmeticatworking-tree - topaz.decision.date-civil-range.1 at
topaz.decisi