adjdate and ddays.
Every date argument below is an ordinary numeric value. The supported range runs from 1900-01-01 00:00:00 through 2200-12-31 23:59:59.999…: building a date outside it returns input.out_of_range, while reading a value that falls outside it — one that arithmetic carried past either end — returns math.out_of_range.
Building a date [date]
date(2026) is midnight on January 1 and date(2026; 7) is midnight on July 1.
date() has no string form — it takes numbers, not text like "7/21/2026". Write text like that as a value you supply: it becomes a number before the calculation runs, so an equation only ever operates on the number. See Dates.
input.out_of_range rather than rolling over into the neighboring month or year. Each of these is an error: date(2026; 2; 29) because 2026 is not a leap year, date(2026; 13; 1) because there is no thirteenth month, and date(1899; 12; 31) or date(2201; 1; 1) because the year is outside the supported range. To roll a value over on purpose, use adjdate.
Date components
date, as a bare scalar rather than a unit number — so a component can be used directly in arithmetic and in date(). Passing all six back to date() reconstructs the value it came from.
second is the finest accessor. Recover milliseconds from its fractional part with fpart — fpart(second(d)) is the sub-second remainder, in seconds.
The accessors read one component at a time. For the whole time of day as a single quantity, take the remainder of the date divided by one day with mod: mod(d; 1 d) is the time elapsed since midnight, and floor(date; "d") is the midnight it counts from. Cast the date to days first — mod(date d; 1 d) — and the same time of day comes back as a fraction of a day, 0.5 d at noon.
fpart produces that second figure without mod. Cast the date to the unit you want to split at and take the fractional part: fpart(date d) is the fraction of the day, fpart(date hr) the part-hour. ipart gives the whole part alongside it.
Day of week [weekday]
1 to 7, where 1 is Sunday and 7 is Saturday — the same numbering as the default WEEKDAY in Excel and Google Sheets.
1 and 7.
Leap year test [isleapyear]
true if date falls in a leap year, false otherwise, under the Gregorian rules: divisible by 4, except century years, except century years divisible by 400.
Calendar adjustment [adjdate]
date by whole days, months, and years. Each amount may be negative, and adjustments roll across month and year boundaries automatically. basis applies a day-count convention to the days amount and defaults to 0, actual days.
Amounts apply from the largest unit down: years, then months, then days.
Month ends
Adjusting by whole months or years keeps the same day number — the 15th stays the 15th. Two more rules apply at month ends, where the day number may not exist in the target month, or where the starting date is the last day of its own month. Both are read from the date, so nothing is carried between adjustments:- The last day of a month maps to the last day of the target month. A month-end date stays on month ends as you add months, even as the lengths of those months change.
- A day that does not exist in the target month clamps back to that month’s last day — the 31st of a 30-day month becomes the 30th, and the 30th of February becomes the 28th or 29th.
adjdate inside a loop and step the month count rather than the date, so every row is measured from the original date instead of from the previous row:
Day counts [ddays]
date1 to date2, as a bare scalar. The count is signed — negative when date2 is the earlier of the two.
basis sets the day-count convention, the same set the finance world uses for accruing interest over a partial period:
Only basis
1 changes the day count itself. Bases 0, 2, and 3 all count actual days and return the same number from ddays — they differ in the year length a later step uses when expressing the span as a fraction of a year.
ddays reads the calendar date of each argument and ignores the time of day, so any two instants on the same day give the same count.
In an activity, ddays also runs the other way: either date can be marked calculable and calculated from the day count and the other date. This works under every basis, in both the two- and three-argument forms.
Under basis 1, several dates can give the same count, because the 30/360 adjustments read the 31st as the 30th:
Snapping a date to a boundary
floor, ceil, and round take a time unit as a quoted string in place of a decimal place, which snaps a date to that boundary — the start of the day, the start of the month, the nearest hour.
Common patterns
The same operations with variable names in place of literal dates, as an activity would use them:d inside the third line. ddays returns a bare count, so it needs a unit attached before it can be converted: casting a plain number straight to wk relabels it rather than converting it, turning a 14-day span into 14 weeks. The second and third lines also answer slightly different questions. Subtracting is exact to the second, while ddays counts calendar days and ignores the time of day, so the two agree only when both dates sit at the same time of day.
Inside an activity equation, wrap the unit literals in backticks — closing_date + 30d“ — so they read as units rather than variable names. See Units in equations.
