{"addressing":"content-addressed","addressedBy":"94c5acc700678b60dd2565e8c68bec4172dfbd482371f489b3c89ac279795e47","canonicalText":"{\"formatVersion\":1,\"frozen\":{\"address\":{\"language\":\"typescript\",\"major\":1,\"name\":\"date/add\"},\"benchmarks\":{\"profiles\":[{\"class\":\"accepted\",\"data\":{\"fields\":[],\"kind\":\"record\"},\"description\":\"Durations with no calendar unit, which skip the month arithmetic entirely. The dominant shape in expiry and timeout code.\",\"name\":\"elapsed-time-only\",\"samples\":{\"kind\":\"carried\",\"values\":[{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"hours\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"minutes\",\"value\":{\"kind\":\"primitive\",\"value\":30}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":7}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"weeks\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":3}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"milliseconds\",\"value\":{\"kind\":\"primitive\",\"value\":250}}],\"kind\":\"record\"}}],\"kind\":\"record\"}]}},{\"class\":\"accepted\",\"data\":{\"fields\":[],\"kind\":\"record\"},\"description\":\"Durations that touch only the month arithmetic, on days that never clamp.\",\"name\":\"calendar-only\",\"samples\":{\"kind\":\"carried\",\"values\":[{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":1}},{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":6}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":-18}}],\"kind\":\"record\"}}],\"kind\":\"record\"}]}},{\"class\":\"accepted\",\"data\":{\"fields\":[],\"kind\":\"record\"},\"description\":\"Calendar arithmetic that lands on a day the target month does not have, isolating the cost of finding the length of a month. Measured separately because the clamp is the one branch an implementation can get expensive.\",\"name\":\"clamping\",\"samples\":{\"kind\":\"carried\",\"values\":[{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2023-01-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-05-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-02-29T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2096-02-29T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":4}}],\"kind\":\"record\"}}],\"kind\":\"record\"}]}},{\"class\":\"accepted\",\"data\":{\"fields\":[],\"kind\":\"record\"},\"description\":\"A duration carrying all eight fields, the worst case for validation and summation.\",\"name\":\"every-field\",\"samples\":{\"kind\":\"carried\",\"values\":[{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":1}},{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":2}},{\"name\":\"weeks\",\"value\":{\"kind\":\"primitive\",\"value\":3}},{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":4}},{\"name\":\"hours\",\"value\":{\"kind\":\"primitive\",\"value\":5}},{\"name\":\"minutes\",\"value\":{\"kind\":\"primitive\",\"value\":6}},{\"name\":\"seconds\",\"value\":{\"kind\":\"primitive\",\"value\":7}},{\"name\":\"milliseconds\",\"value\":{\"kind\":\"primitive\",\"value\":8}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T23:59:59.999Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":-1}},{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":-2}},{\"name\":\"weeks\",\"value\":{\"kind\":\"primitive\",\"value\":-3}},{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":-4}},{\"name\":\"hours\",\"value\":{\"kind\":\"primitive\",\"value\":-5}},{\"name\":\"minutes\",\"value\":{\"kind\":\"primitive\",\"value\":-6}},{\"name\":\"seconds\",\"value\":{\"kind\":\"primitive\",\"value\":-7}},{\"name\":\"milliseconds\",\"value\":{\"kind\":\"primitive\",\"value\":-8}}],\"kind\":\"record\"}}],\"kind\":\"record\"}]}},{\"class\":\"rejected\",\"data\":{\"fields\":[],\"kind\":\"record\"},\"description\":\"Calls that must return null. Measured separately because an implementation may take a very different path when it refuses, and callers validating untrusted input hit that path most.\",\"name\":\"rejected-inputs\",\"samples\":{\"kind\":\"carried\",\"values\":[{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"not a date\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":0.5}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"number\",\"literal\":\"nan\"}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-15T12:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":1e+21}}],\"kind\":\"record\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"+275760-09-13T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"milliseconds\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}}],\"kind\":\"record\"}]}}],\"vocabulary\":[{\"meaning\":\"every sample of the profile is answered rather than refused\",\"name\":\"accepted\"},{\"meaning\":\"every sample of the profile is refused, which is a different path\",\"name\":\"rejected\"}]},\"caseTables\":[{\"cases\":[{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T10:30:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-16T10:30:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"baseline\",\"id\":\"an-ordinary-day\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"An ordinary day is added, and the time of day is untouched.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T10:30:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"minutes\",\"value\":{\"kind\":\"primitive\",\"value\":90}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T12:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"baseline\",\"id\":\"minutes-carry-into-hours\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Elapsed units carry into the next unit; ninety minutes is an hour and a half.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"1969-12-31T23:59:59.999Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"milliseconds\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"1970-01-01T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"baseline\",\"id\":\"the-epoch-is-not-a-boundary\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The epoch is not a boundary. It is listed because implementations that branch on the sign of the timestamp get this one wrong.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-02-29T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"end-of-month-clamping\",\"id\":\"clamp-to-the-end-of-february\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A day that the target month does not have is clamped down to the last day it does. There is no 31 February, and the two defensible answers are the end of February or the overflow into March. Measured, date-fns, luxon, dayjs, moment, js-joda and Temporal under its default overflow all clamp; JavaScript's own setUTCMonth overflows, to 2 March. The contract follows the six libraries against the language, because \\\"a month later\\\" naming a date in March is a surprise no caller asked for.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2023-01-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2023-02-28T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"end-of-month-clamping\",\"id\":\"clamp-in-a-common-year\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The same clamp in a common year lands on the 28th, since that is the last day.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-05-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-06-30T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"end-of-month-clamping\",\"id\":\"clamp-into-a-thirty-day-month\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Clamping is not about February; any 31-day month followed by a 30-day one clamps.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-03-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":-1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-02-29T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"end-of-month-clamping\",\"id\":\"clamp-going-backwards\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Clamping applies in both directions; going backwards is not a special case.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-02-29T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":-1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-29T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"end-of-month-clamping\",\"id\":\"the-clamp-does-not-round-trip\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Adding a month and removing it again does not return where it started: 31 January plus one month is 29 February, and 29 February minus one month is 29 January. The clamp discards the day of the month, and nothing remembers it. This is a consequence of clamping rather than a defect, it is what every measured library does, and it is why the round-trip property in block 4.3 excludes calendar units instead of pretending they qualify.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T23:59:59.999Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-02-29T23:59:59.999Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"end-of-month-clamping\",\"id\":\"the-clamp-keeps-the-time-of-day\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The clamp moves the date and leaves the UTC time of day exactly as it was, down to the millisecond, including at the last instant of a day.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-02-29T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2025-02-28T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"leap-years\",\"id\":\"a-leap-day-plus-one-year\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A leap day plus one year clamps, because the target year has no 29 February.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-02-29T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":4}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2028-02-29T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"leap-years\",\"id\":\"a-leap-day-plus-four-years\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Four years later the day exists again and nothing is clamped.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2096-02-29T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":4}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2100-02-28T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"leap-years\",\"id\":\"the-century-rule\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The century rule bites: 2100 is divisible by 4 but not by 400, so it is not a leap year and the day is clamped. An implementation testing only `year % 4` answers 2100-02-29.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"0050-01-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"0050-02-28T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"leap-years\",\"id\":\"a-two-digit-year\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A two-digit year stays a two-digit year rather than being read as a nineteen-hundreds one. It is listed because `Date.UTC` maps years 0 to 99 onto 1900 to 1999 - measured, `Date.UTC(50, 0, 1)` is 1950-01-01 where `setUTCFullYear(50, 0, 1)` is year 50 - so an implementation that builds its result through `Date.UTC` answers in the wrong century. The measured limit of this case is published rather than hidden: an implementation using `Date.UTC` only to look up the length of a month passes it, because Y and 1900 + Y are congruent modulo four and so agree on February everywhere except the century rule. The case below is the one input where they part.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"0000-01-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"0000-02-29T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"leap-years\",\"id\":\"year-zero\",\"provenance\":{\"kind\":\"found-by-mutation\",\"mutant\":{\"battery\":\"date-add\",\"contract\":{\"language\":\"typescript\",\"major\":1,\"name\":\"date/add\"},\"mutant\":\"D-07\"}},\"rationale\":\"Year 0 is the single two-digit year where reading a month length through `Date.UTC` and reading it through `setUTCFullYear` disagree: 0 is a leap year and 1900, which `Date.UTC` maps it to, is not. Measured, February of year 0 has 29 days by `setUTCFullYear` and 28 by `Date.UTC`. Every other case in this table is congruent modulo four with the year it would be mistaken for, so all of them pass under that implementation and this one does not.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2023-01-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2023-03-31T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"aggregation-within-a-step\",\"id\":\"two-months-aggregated\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Two months are added as two months, not as one month twice. Measured, adding one month twice gives 2023-03-28, because the clamp to 28 February is never undone. The contract aggregates, as every measured library does.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2023-01-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":1}},{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-02-29T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"aggregation-within-a-step\",\"id\":\"years-and-months-are-one-total\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Years and months are one total of thirteen months, not a year then a month. Applying months first would give 2024-02-28, one day earlier, because it clamps twice.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-02-25T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"weeks\",\"value\":{\"kind\":\"primitive\",\"value\":1}},{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-03-04T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"aggregation-within-a-step\",\"id\":\"weeks-and-days-are-one-total\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Weeks and days are one total of eight days; a week is seven days and nothing else.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"weeks\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-02-07T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"aggregation-within-a-step\",\"id\":\"a-week-never-clamps\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A week never clamps, because it is a count of days rather than a calendar unit.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-30T23:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}},{\"name\":\"hours\",\"value\":{\"kind\":\"primitive\",\"value\":2}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-03-01T01:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"order-between-the-steps\",\"id\":\"calendar-before-elapsed-hours\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Calendar units are applied before elapsed time, and the order is observable: the calendar step clamps 30 January to 29 February, and the two hours then cross midnight into 1 March. Adding the hours first would reach 31 January, which clamps to 29 February - a different answer, a day earlier.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-30T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}},{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-03-01T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"order-between-the-steps\",\"id\":\"calendar-before-elapsed-days\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The same order between months and days: 30 January clamps to 29 February, then one day is added. Days first would give 31 January, then 29 February.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":-3}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-12T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"negative-and-mixed-signs\",\"id\":\"a-negative-field-subtracts\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A negative field subtracts; there is no separate subtraction function.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}},{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":-1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-02-28T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"negative-and-mixed-signs\",\"id\":\"fields-of-opposite-sign\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Fields may disagree in sign. Temporal rejects a mixed-sign duration outright; this contract accepts it, because `{ months: 1, days: -1 }` is how a caller writes \\\"the day before the same date next month\\\" and refusing it makes a common intention inexpressible. luxon and date-fns accept it and agree on this answer.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T12:34:56.789Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T12:34:56.789Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-neutral-duration\",\"id\":\"the-empty-duration\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The empty duration is the neutral element: it returns the same instant, in a new object. Temporal rejects it - measured, `TypeError: No valid fields` - on the grounds that it declares no operation; this contract accepts it, as date-fns and luxon do, because `addToDate(d, buildDuration(form))` must not fail when the form is empty.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T12:34:56.789Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"number\",\"literal\":\"negative-zero\"}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T12:34:56.789Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-neutral-duration\",\"id\":\"a-negative-zero-field\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Negative zero is a whole number and adds nothing. Unlike `number/parse@1`, where the sign of zero is preserved in the result, it cannot survive here: measured, `new Date(-0).getTime()` is `0`, so `Date` erases the distinction before any implementation could keep it.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T12:34:56.789Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"undefined\"}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-31T12:34:56.789Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-neutral-duration\",\"id\":\"a-field-set-to-undefined\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A declared field set to `undefined` means zero, not an error. `{ days: form.days }` where the form has no value is ordinary TypeScript, and rejecting it would make the type lie.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1.5}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"field-not-whole\"}}],\"kind\":\"record\"},\"group\":\"durations-that-are-not-whole-units\",\"id\":\"a-fractional-month\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A fractional month has no meaning the contract can honour, and the established libraries disagree about it - measured, date-fns and dayjs silently truncate to one month, luxon converts the half into about fifteen days, Temporal throws. Silently truncating is the same failure as `Number(\\\"0x1F\\\")` returning 31: an answer that looks right and is not what was asked. This is the first place in the catalogue where a contract takes a side instead of following the ecosystem.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":0.5}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"field-not-whole\"}}],\"kind\":\"record\"},\"group\":\"durations-that-are-not-whole-units\",\"id\":\"half-a-day\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Half a day is rejected even though it has an exact meaning in UTC, because the rule is one rule for every field. `{ hours: 12 }` says the same thing without ambiguity.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"number\",\"literal\":\"nan\"}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"field-not-whole\"}}],\"kind\":\"record\"},\"group\":\"durations-that-are-not-whole-units\",\"id\":\"a-field-that-is-not-a-number\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"NaN is not a whole number. Adding it would produce an Invalid Date.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"number\",\"literal\":\"infinity\"}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"field-not-whole\"}}],\"kind\":\"record\"},\"group\":\"durations-that-are-not-whole-units\",\"id\":\"an-infinite-field\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"An infinite duration is not a whole number and has no representable result.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":1e+21}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"field-not-whole\"}}],\"kind\":\"record\"},\"group\":\"durations-that-are-not-whole-units\",\"id\":\"a-field-past-the-safe-range\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Beyond 2^53 an integer is no longer exactly representable as a double, so the arithmetic stops being the arithmetic the caller wrote. `Number.isInteger(1e21)` is true and every measured library accepts it; this contract requires `Number.isSafeInteger` instead, for the same reason it rejects a fractional month - a wrong answer delivered silently is worse than no answer.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":9007199254740992}},{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":-108086391056891900}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"field-not-whole\"}}],\"kind\":\"record\"},\"group\":\"durations-that-are-not-whole-units\",\"id\":\"two-fields-whose-total-cancels\",\"provenance\":{\"kind\":\"found-by-mutation\",\"mutant\":{\"battery\":\"date-add\",\"contract\":{\"language\":\"typescript\",\"major\":1,\"name\":\"date/add\"},\"mutant\":\"D-08\"}},\"rationale\":\"Two fields past the safe range whose month total is exactly zero. Measured, `2**53` is an integer and not a safe integer, and both products are exactly representable as doubles, so their sum really is 0. An implementation guarding with `Number.isInteger` instead of `Number.isSafeInteger` therefore accepts both fields, computes a total it can represent, and returns the date unchanged - a plausible answer rather than an obviously wrong one. Every other case here is caught by the total guard even when the field guard is weakened; this is the one that requires the rule to be about the fields as written.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"years\",\"value\":{\"kind\":\"primitive\",\"value\":9007199254740991}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"total-not-exact\"}}],\"kind\":\"record\"},\"group\":\"durations-that-are-not-whole-units\",\"id\":\"a-month-total-that-is-not-exact\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"Each field is a safe integer here, but the total is not: measured, `MAX_SAFE_INTEGER * 12` is not a safe integer, so the month total could not be computed exactly. The rule applies to the totals the contract actually adds, not only to the fields as written.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"milliseconds\",\"value\":{\"kind\":\"primitive\",\"value\":9007199254740991}},{\"name\":\"seconds\",\"value\":{\"kind\":\"primitive\",\"value\":9007199254740991}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"total-not-exact\"}}],\"kind\":\"record\"},\"group\":\"durations-that-are-not-whole-units\",\"id\":\"an-elapsed-total-that-is-not-exact\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The same rule for elapsed time: two safe integers whose sum in milliseconds is not one. Past 2^53 milliseconds - about 285 000 years, far outside the range a Date can hold anyway - the sum would round, and the contract does not return values it cannot compute exactly. It carries the same reason as the month total above, because a caller repairs both by making the duration it wrote smaller.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"not a date\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"invalid-date\"}}],\"kind\":\"record\"},\"group\":\"inputs-that-are-not-dates\",\"id\":\"an-input-that-is-not-a-date\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"An Invalid Date in gives null out, never an Invalid Date out. `new Date(\\\"nonsense\\\")` is a Date whose timestamp is NaN; measured, date-fns propagates it and returns another Invalid Date, which then poisons every later computation exactly as NaN does in `number/parse@1`.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"not a date\"}},{\"name\":\"duration\",\"value\":{\"fields\":[],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"invalid-date\"}}],\"kind\":\"record\"},\"group\":\"inputs-that-are-not-dates\",\"id\":\"an-input-that-is-not-a-date-with-the-empty-duration\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The neutral duration does not rescue an invalid input. The date is checked before the duration is looked at, so no duration can make an unanswerable call answerable.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"+275760-09-13T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":\"+275760-09-13T00:00:00.000Z\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":null}}],\"kind\":\"record\"},\"group\":\"the-edges-of-the-representable-range\",\"id\":\"the-last-representable-instant\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The last representable instant is a valid input and is returned unchanged.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"+275760-09-13T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"milliseconds\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"out-of-range\"}}],\"kind\":\"record\"},\"group\":\"the-edges-of-the-representable-range\",\"id\":\"one-millisecond-past-the-end-of-the-range\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"One millisecond past the end of the range has no Date. `new Date(8.64e15 + 1)` is an Invalid Date; the contract returns null rather than hand one back.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"-271821-04-20T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"milliseconds\",\"value\":{\"kind\":\"primitive\",\"value\":-1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"out-of-range\"}}],\"kind\":\"record\"},\"group\":\"the-edges-of-the-representable-range\",\"id\":\"one-millisecond-before-the-start-of-the-range\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The range is symmetric, and so is the rejection.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"+275760-09-13T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"months\",\"value\":{\"kind\":\"primitive\",\"value\":1}},{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":-40}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"out-of-range\"}}],\"kind\":\"record\"},\"group\":\"the-edges-of-the-representable-range\",\"id\":\"an-intermediate-step-outside-the-range\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The steps are applied in the declared order and each one must land inside the range. Adding a month leaves it, and removing forty days would come back; the contract does not allow the detour. Declaring this follows from declaring the order at all - a contract that fixed the order but let intermediate results roam would have declared half a rule.\"}],\"groups\":[{\"id\":\"baseline\",\"note\":null,\"title\":\"Baseline\"},{\"id\":\"end-of-month-clamping\",\"note\":null,\"title\":\"End-of-month clamping\"},{\"id\":\"leap-years\",\"note\":null,\"title\":\"Leap years\"},{\"id\":\"aggregation-within-a-step\",\"note\":null,\"title\":\"Aggregation within a step\"},{\"id\":\"order-between-the-steps\",\"note\":null,\"title\":\"Order between the steps\"},{\"id\":\"negative-and-mixed-signs\",\"note\":null,\"title\":\"Negative and mixed signs\"},{\"id\":\"the-neutral-duration\",\"note\":null,\"title\":\"The neutral duration\"},{\"id\":\"durations-that-are-not-whole-units\",\"note\":null,\"title\":\"Durations that are not exact whole units\"},{\"id\":\"inputs-that-are-not-dates\",\"note\":null,\"title\":\"Inputs that are not dates\"},{\"id\":\"the-edges-of-the-representable-range\",\"note\":null,\"title\":\"The edges of the representable range\"}],\"name\":\"edge-cases\",\"purpose\":\"the calls this contract settles, every answer computed by two oracles beforehand\"},{\"cases\":[{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"day\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"unknown-field\"}}],\"kind\":\"record\"},\"group\":\"fields-the-contract-does-not-define\",\"id\":\"a-singular-day-field\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A field the contract does not define is a rejection, not a no-op. `{ day: 1 }` for `{ days: 1 }` is the singular-for-plural slip everyone makes, and returning the date unchanged would be `Number(\\\"\\\")` returning 0 in another costume: a plausible value that silently drops what the caller asked for.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"month\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"unknown-field\"}}],\"kind\":\"record\"},\"group\":\"fields-the-contract-does-not-define\",\"id\":\"a-singular-month-field\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"The same slip on the calendar side, rejected for the same reason.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":1}},{\"name\":\"day\",\"value\":{\"kind\":\"primitive\",\"value\":1}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"unknown-field\"}}],\"kind\":\"record\"},\"group\":\"fields-the-contract-does-not-define\",\"id\":\"an-unknown-field-beside-a-declared-one\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"One unknown field is enough to reject the whole call, even alongside a valid one. Applying the part that was understood would be the most dangerous answer available: it looks like it worked.\"},{\"data\":{\"fields\":[{\"name\":\"date\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T00:00:00.000Z\"}},{\"name\":\"duration\",\"value\":{\"fields\":[{\"name\":\"days\",\"value\":{\"kind\":\"primitive\",\"value\":\"1\"}}],\"kind\":\"record\"}},{\"name\":\"expected\",\"value\":{\"kind\":\"primitive\",\"value\":null}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"field-not-whole\"}}],\"kind\":\"record\"},\"group\":\"fields-carrying-the-wrong-type\",\"id\":\"a-declared-field-carrying-a-string\",\"provenance\":{\"kind\":\"specified\"},\"rationale\":\"A declared field carrying the wrong type is rejected too. A string reaching this function has come from JSON or a form and has not been through the parser it needed. It shares the reason of a fractional field rather than earning one, because the same sentence is true of both: a string is not a whole number either.\"}],\"groups\":[{\"id\":\"fields-the-contract-does-not-define\",\"note\":null,\"title\":\"A field the contract does not define\"},{\"id\":\"fields-carrying-the-wrong-type\",\"note\":null,\"title\":\"A declared field carrying the wrong type\"}],\"name\":\"untyped-callers\",\"purpose\":\"durations no TypeScript caller can write and every JavaScript caller can\"}],\"environments\":[\"node\",\"browser\",\"bun\"],\"harness\":[{\"bytes\":25661,\"path\":\"contract.ts\",\"sha256\":\"20008f62fa10a8f19844e47106fe6ea43c5b52fe2cf2b53a8f92440d3e78ca14\"},{\"bytes\":6489,\"path\":\"edge-cases.test.ts\",\"sha256\":\"917eb540b4b442f6597c046f4144cc25dcca4983ed4f72b8091f2bdabef7648e\"},{\"bytes\":26184,\"path\":\"edge-cases.ts\",\"sha256\":\"a13e2f1738f5856669f2a99c94af088bbd43fbdcbf2e609e920268c3fe7cf718\"},{\"bytes\":929,\"path\":\"profiles.test.ts\",\"sha256\":\"8578064e049dadbed1111b3d0dadd6bae82cc04b447990cf370624466594dbfd\"},{\"bytes\":19906,\"path\":\"properties.test.ts\",\"sha256\":\"17d57482bac7750ef5de6aec41c9e144150d14969b572ea60057681e1db0e5b2\"},{\"bytes\":7075,\"path\":\"reference.ts\",\"sha256\":\"9a3c09dfb4932fb2a3fc566ffbc421dd160b76f1e81df80bdca710e209c9a757\"},{\"bytes\":1668,\"path\":\"signature.test-d.ts\",\"sha256\":\"621325ac2fdc1fc7c58021d4cf56b05e01768d677d0eff5a0ea01c3932895087\"}],\"identity\":{\"description\":\"Adds a duration to a Date in JavaScript and TypeScript and returns a new Date, without mutating the one it was given and without reading the machine time zone. The built-in way to do this mutates: `d.setDate(d.getDate() + 7)` changes the caller's object in place, and it reads the calendar of whichever time zone the process happens to run in, so the same code answers differently on a laptop in Paris and a server in UTC. It also has no notion of a month that is too short - setUTCMonth on 31 January lands on 2 or 3 March rather than the end of February. This contract computes in absolute UTC time, clamps a day that does not exist in the target month down to the last day that does, applies calendar units before elapsed time, and returns null rather than an Invalid Date when the input is not a date, when the duration carries a field this contract does not declare, when the duration is not made of exact whole units, or when the result falls outside the range a Date can hold. It never returns an Invalid Date, the value that propagates as NaN through every later computation and surfaces far away from the call that produced it. Which of those refusals happened is published by a second export rather than folded into the return value, so a caller who only needs a date is not made to unwrap one.\",\"exportName\":\"addToDate\",\"inputDomain\":\"Absolute instants, shifted by durations written in whole units: an expiry, a reminder, a retention window, a report period, a timestamp read from a database or an API. The arithmetic is UTC throughout, so no daylight-saving transition is ever crossed, skipped or repeated, and one day is always exactly 24 hours. That exclusion is deliberate rather than an oversight: reading the wall clock of a particular place, where a civil day can last 23 or 25 hours, requires knowing which place, and a function that silently borrows the process time zone is impure and answers differently on two machines running the same code. Calendar arithmetic in a named zone is a separate, later contract that will take the zone as a parameter. This one is not a parser, not a formatter, and not a difference-between-two-dates function.\",\"searchAliases\":[\"add days to date\",\"addDays\",\"add months to date\",\"addMonths\",\"addYears\",\"add hours\",\"subtract days from date\",\"subDays\",\"date arithmetic\",\"date plus duration\",\"shift date\",\"date add duration\",\"increment date\"],\"summary\":\"Add a duration to a Date and get a new Date back, in UTC, without mutating the input, or null when the call cannot be answered exactly - with a named reason available beside it for a caller who needs to know which refusal it was.\"},\"ownDeclarations\":[{\"name\":\"durationFields\",\"value\":{\"entries\":[{\"kind\":\"primitive\",\"value\":\"years\"},{\"kind\":\"primitive\",\"value\":\"months\"},{\"kind\":\"primitive\",\"value\":\"weeks\"},{\"kind\":\"primitive\",\"value\":\"days\"},{\"kind\":\"primitive\",\"value\":\"hours\"},{\"kind\":\"primitive\",\"value\":\"minutes\"},{\"kind\":\"primitive\",\"value\":\"seconds\"},{\"kind\":\"primitive\",\"value\":\"milliseconds\"}],\"kind\":\"list\"},\"verification\":\"structural\"},{\"name\":\"applicationOrder\",\"value\":{\"entries\":[{\"fields\":[{\"name\":\"step\",\"value\":{\"kind\":\"primitive\",\"value\":\"calendar\"}},{\"name\":\"fields\",\"value\":{\"entries\":[{\"kind\":\"primitive\",\"value\":\"years\"},{\"kind\":\"primitive\",\"value\":\"months\"}],\"kind\":\"list\"}},{\"name\":\"rule\",\"value\":{\"kind\":\"primitive\",\"value\":\"Aggregated into a single month total, `years * 12 + months`, applied once to the UTC year and month. The day of the month is then clamped down to the last day the target month has, and the UTC time of day is carried across untouched.\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"step\",\"value\":{\"kind\":\"primitive\",\"value\":\"elapsed\"}},{\"name\":\"fields\",\"value\":{\"entries\":[{\"kind\":\"primitive\",\"value\":\"weeks\"},{\"kind\":\"primitive\",\"value\":\"days\"},{\"kind\":\"primitive\",\"value\":\"hours\"},{\"kind\":\"primitive\",\"value\":\"minutes\"},{\"kind\":\"primitive\",\"value\":\"seconds\"},{\"kind\":\"primitive\",\"value\":\"milliseconds\"}],\"kind\":\"list\"}},{\"name\":\"rule\",\"value\":{\"kind\":\"primitive\",\"value\":\"Summed into a single count of milliseconds and added to the instant, one day being exactly 24 hours. There are two steps rather than three because the six fields here commute with one another - measured, a day then two hours and two hours then a day reach the same instant, since UTC has no transitions to make a day any other length. Declaring an order among them would fix something no caller can observe.\"}}],\"kind\":\"record\"}],\"kind\":\"list\"},\"verification\":\"structural\"},{\"executableBy\":{\"battery\":\"date-add\",\"guard\":\"no-ambient-input-from-the-time-zone\"},\"name\":\"ambientProbeInstants\",\"value\":{\"fields\":[{\"name\":\"january\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-01-15T12:00:00.000Z\"}},{\"name\":\"july\",\"value\":{\"kind\":\"primitive\",\"value\":\"2024-07-15T12:00:00.000Z\"}}],\"kind\":\"record\"},\"verification\":\"executable\"},{\"executableBy\":{\"battery\":\"date-add\",\"guard\":\"no-ambient-input-from-the-time-zone\"},\"name\":\"ambientTimeZoneProbes\",\"value\":{\"entries\":[{\"fields\":[{\"name\":\"timeZone\",\"value\":{\"kind\":\"primitive\",\"value\":\"UTC\"}},{\"name\":\"offsetMinutes\",\"value\":{\"fields\":[{\"name\":\"january\",\"value\":{\"kind\":\"primitive\",\"value\":0}},{\"name\":\"july\",\"value\":{\"kind\":\"primitive\",\"value\":0}}],\"kind\":\"record\"}},{\"name\":\"role\",\"value\":{\"kind\":\"primitive\",\"value\":\"control - agrees with every other zone, and alone cannot see the defect\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"timeZone\",\"value\":{\"kind\":\"primitive\",\"value\":\"Europe/Paris\"}},{\"name\":\"offsetMinutes\",\"value\":{\"fields\":[{\"name\":\"january\",\"value\":{\"kind\":\"primitive\",\"value\":-60}},{\"name\":\"july\",\"value\":{\"kind\":\"primitive\",\"value\":-120}}],\"kind\":\"record\"}},{\"name\":\"role\",\"value\":{\"kind\":\"primitive\",\"value\":\"changes offset twice a year, so a local civil day is sometimes 23 or 25 hours\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"timeZone\",\"value\":{\"kind\":\"primitive\",\"value\":\"Asia/Kathmandu\"}},{\"name\":\"offsetMinutes\",\"value\":{\"fields\":[{\"name\":\"january\",\"value\":{\"kind\":\"primitive\",\"value\":-345}},{\"name\":\"july\",\"value\":{\"kind\":\"primitive\",\"value\":-345}}],\"kind\":\"record\"}},{\"name\":\"role\",\"value\":{\"kind\":\"primitive\",\"value\":\"offset that is not a whole number of hours, and never changes\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"timeZone\",\"value\":{\"kind\":\"primitive\",\"value\":\"Pacific/Chatham\"}},{\"name\":\"offsetMinutes\",\"value\":{\"fields\":[{\"name\":\"january\",\"value\":{\"kind\":\"primitive\",\"value\":-825}},{\"name\":\"july\",\"value\":{\"kind\":\"primitive\",\"value\":-765}}],\"kind\":\"record\"}},{\"name\":\"role\",\"value\":{\"kind\":\"primitive\",\"value\":\"both at once - a quarter-hour offset that also shifts twice a year\"}}],\"kind\":\"record\"}],\"kind\":\"list\"},\"verification\":\"executable\"},{\"name\":\"LATEST_REPRESENTABLE\",\"value\":{\"kind\":\"primitive\",\"value\":\"+275760-09-13T00:00:00.000Z\"},\"verification\":\"structural\"},{\"name\":\"EARLIEST_REPRESENTABLE\",\"value\":{\"kind\":\"primitive\",\"value\":\"-271821-04-20T00:00:00.000Z\"},\"verification\":\"structural\"},{\"executableBy\":{\"battery\":\"validation-stage-1\",\"guard\":\"an-implementation-that-calls-a-forbidden-method-is-refused\"},\"name\":\"staticAnalysisRequirements\",\"value\":{\"entries\":[{\"fields\":[{\"name\":\"name\",\"value\":{\"kind\":\"primitive\",\"value\":\"no local time methods\"}},{\"name\":\"forbiddenMethods\",\"value\":{\"entries\":[{\"kind\":\"primitive\",\"value\":\"getFullYear\"},{\"kind\":\"primitive\",\"value\":\"getMonth\"},{\"kind\":\"primitive\",\"value\":\"getDate\"},{\"kind\":\"primitive\",\"value\":\"getDay\"},{\"kind\":\"primitive\",\"value\":\"getHours\"},{\"kind\":\"primitive\",\"value\":\"getMinutes\"},{\"kind\":\"primitive\",\"value\":\"getSeconds\"},{\"kind\":\"primitive\",\"value\":\"getMilliseconds\"},{\"kind\":\"primitive\",\"value\":\"setFullYear\"},{\"kind\":\"primitive\",\"value\":\"setMonth\"},{\"kind\":\"primitive\",\"value\":\"setDate\"},{\"kind\":\"primitive\",\"value\":\"setHours\"},{\"kind\":\"primitive\",\"value\":\"setMinutes\"},{\"kind\":\"primitive\",\"value\":\"setSeconds\"},{\"kind\":\"primitive\",\"value\":\"setMilliseconds\"},{\"kind\":\"primitive\",\"value\":\"getTimezoneOffset\"},{\"kind\":\"primitive\",\"value\":\"toLocaleDateString\"},{\"kind\":\"primitive\",\"value\":\"toLocaleTimeString\"},{\"kind\":\"primitive\",\"value\":\"toLocaleString\"}],\"kind\":\"list\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"Every one of these reads or writes the calendar of the process time zone, which is ambient input the signature does not declare. An implementation using them answers differently on two machines running the same code.\"}}],\"kind\":\"record\"},{\"fields\":[{\"name\":\"name\",\"value\":{\"kind\":\"primitive\",\"value\":\"no global state\"}},{\"name\":\"forbiddenMethods\",\"value\":{\"entries\":[],\"kind\":\"list\"}},{\"name\":\"reason\",\"value\":{\"kind\":\"primitive\",\"value\":\"A feature may not read or write anything outside its arguments and its own module scope. Unreachable by property: a test cannot observe a write that happened before it ran, and a correct cache is indistinguishable from a defect by behaviour alone.\"}}],\"kind\":\"record\"}],\"kind\":\"list\"},\"verification\":\"executable\"}],\"properties\":{\"runs\":1000,\"universal\":[{\"applicable\":true,\"name\":\"never mutates its arguments\",\"reason\":\"Applicable here, where it was not on `number/parse@1`: `Date` is a mutable object and the implementation a developer writes first - `date.setUTCDate(date.getUTCDate() + n)` - mutates the caller's object and returns it. The property asserts both halves, that the input holds the same instant afterwards and that the result is a different object, because an implementation that mutates and returns its argument satisfies output equality.\"},{\"applicable\":true,\"name\":\"deterministic\",\"reason\":\"Violable in practice, and witnessed by D-18 of the battery: an implementation that shifts the caller's own Date and returns a copy answers from a moved instant on the second call. Two candidate witnesses were measured and rejected, because a property pinned to a flaky or silent mutant is worse than an open gap. A clock read is not observable here - Date.now has millisecond resolution and the two calls this property makes are microseconds apart, so the mutant would be green almost always and red by accident. A cache is not observable either: consulted first, it is primed by the property's own first call and returns that same answer to the second. D-02, which mutates the input and hands the object back rather than a copy, leaves this property green for a third reason - measured - because both calls then return the one object and it is compared against itself. This property is ordered under `no ambient input` rather than independent of it: every mutant measured to redden it reddens that one too, and the memoise-last mutant reddens that one and not this. D-22 is that mutant here.\"},{\"applicable\":true,\"name\":\"no ambient input\",\"reason\":\"The substantive property of this contract. The ambient input at stake is the process time zone: `getMonth` and `setDate` read and write the local calendar, so an implementation built on them is a different function on a machine in Paris and a machine in UTC. Measured, such an implementation diverges from the UTC answer on 411 of 1000 pseudo-random draws once the zone is varied - and on 0 of 1000 if the zone is never varied. The zone instance is witnessed by D-05, which reads and writes the local calendar. The call history is tested as a second instance of the same property, and witnessed by D-19: the Date used to look up the length of a month hoisted to module scope, so the year the previous call left in it decides the next one. State that advances with every call is the only thing this instance can see - a cache consulted first is primed by the probe and stays invisible, measured on `number/parse@1` where two such caches survive the whole battery. D-22 is the witness that belongs to this instance alone: measured, it reddens here and leaves determinism green.\"},{\"applicable\":false,\"name\":\"no ambient output\",\"reason\":\"Not reachable by a property - a test cannot observe a write that happened before it ran, and a correct memoising cache is indistinguishable from a defect by behaviour alone. Here it is a static analysis requirement instead, declared in `staticAnalysisRequirements` above.\"}]},\"sharedHarness\":[{\"bytes\":16621,\"path\":\"packages/catalogue/every-contract.ts\",\"sha256\":\"651fcc16fdc900bcded8e572bc5bb044c52322bb9c04bc2c05630e2761d5cc16\"},{\"bytes\":9777,\"path\":\"packages/catalogue/identifier.ts\",\"sha256\":\"8ab32ffcd699e39aa4fcb7604722f28dc12b723fca3a126a413c3e0a26ff3ff6\"}],\"surface\":{\"couplingRule\":\"addToDate(d, u) === null if and only if describeAddFailure(d, u) !== null, for every date d and duration u\",\"exports\":[{\"name\":\"addToDate\",\"parameters\":[{\"name\":\"date\",\"type\":\"Date\"},{\"name\":\"duration\",\"type\":\"Duration\"}],\"role\":\"the-answer\",\"text\":\"(date: Date, duration: Duration) => Date | null\",\"typeName\":\"AddToDate\"},{\"name\":\"describeAddFailure\",\"parameters\":[{\"name\":\"date\",\"type\":\"Date\"},{\"name\":\"duration\",\"type\":\"Duration\"}],\"role\":\"the-diagnostic\",\"text\":\"(date: Date, duration: Duration) => AddFailureReason | null\",\"typeName\":\"DescribeAddFailure\"}],\"failureReasons\":[\"invalid-date\",\"unknown-field\",\"field-not-whole\",\"total-not-exact\",\"out-of-range\"],\"supportingTypes\":[{\"name\":\"Duration\",\"text\":\"{\\n  readonly years?: number | undefined\\n  readonly months?: number | undefined\\n  readonly weeks?: number | undefined\\n  readonly days?: number | undefined\\n  readonly hours?: number | undefined\\n  readonly minutes?: number | undefined\\n  readonly seconds?: number | undefined\\n  readonly milliseconds?: number | undefined\\n}\"}]}},\"unit\":\"contract\"}","formatVersion":1}