> ## Documentation Index
> Fetch the complete documentation index at: https://docs.truemath.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Dates

> How TrueMath represents a date — a time value counted in seconds from the Unix epoch — and the arithmetic, comparison, and calendar rules that follow from it.

A **date** is not a separate value type in TrueMath. It is a [unit number](/math/types/unit-numbers) carrying a time unit: the count of seconds from `1970-01-01 00:00:00` — the Unix epoch — to that instant. The [date functions](/math/functions/dates) read that number as a point on the calendar.

This is the spreadsheet model, where a date is a serial number and any number can be read as a date. What TrueMath adds is that the serial is a *time value* rather than a bare count, so a date combines with durations under the ordinary [unit rules](/math/units): `closing_date + 30 d` is a date because `30 d` is a quantity of time.

```
date(1970)                       => 0 s
date(1970; 1; 1; 0; 0; 1)        => 1 s
date(1969; 12; 31; 23; 59; 59)   => -1 s
```

## What follows from the model

* **Midnight on the epoch is `0`,** one day later is `86400`, and every instant before the epoch is negative. Dates before 1970 need no special handling.
* **No hidden state.** A date carries no flags — nothing marks it as "has a time" or "is a month end." Two dates are equal exactly when their values are equal, and a function such as [`adjdate`](/math/functions/dates#calendar-adjustment-adjdate) works only from the value it is given.
* **Wall clock, no zones.** A date is a plain wall-clock reading — the components *are* the value. Nothing in a calculation shifts a date between time zones or applies daylight-saving rules.
* **Nothing in the math reads a clock.** Date functions are pure: the same arguments always give the same result, which is part of what makes a result [reproducible](/introduction/guarantees). A clock enters through the *values* a calculation is given — a value or default written as `today` or `now` becomes a number before the calculation runs, and two of them in the same calculation mean the same moment. `today` is midnight of the current day and `now` carries the time of day as well, read in the time zone the request supplies — the browser's in the [Playground](/playground/tour), the `time_zone` parameter over the API — and otherwise in the account's time-zone setting. See [Time zones](/api/calculate#time-zones). Only that number is used in the calculation.
* **Sub-second precision** is held in the fractional part: `0.5` is half a second. The smallest named time unit is the millisecond (`ms`), and there is no millisecond accessor — read it from the fraction of `second(d)` with [`fpart`](/math/functions/rounding-and-numeric#fractional-part-fpart).
* **Text is read on the way in, not in the math.** A date you *supply* can be written the way people write dates — `7/21/2026`, `today` — and it becomes a number before the calculation runs. Inside an *equation* there is no parsing: [`date()`](/math/functions/dates#building-a-date-date) builds a date from numeric components, so an equation never reads `"7/21/2026"`.

## Supported range and calendar rules

* **Supported range: `1900-01-01 00:00:00` through `2200-12-31 23:59:59.999…`** — every instant of 1900 through every instant of 2200, since the limit is on the year. [`date()`](/math/functions/dates#building-a-date-date) rejects a year outside it with `input.out_of_range`. A value that arithmetic carries past either end is not itself an error — it is still just a number — but reading it with a date function returns `math.out_of_range`.
* **Gregorian calendar throughout.** A leap year is divisible by 4, except century years, except century years divisible by 400 — `2000` is a leap year and `1900` is not.
* **A day that does not exist is rejected,** not rolled over: there is no `2026-02-29`.
* **Every day is exactly 86,400 seconds.** Leap seconds are not modeled, matching Unix and spreadsheets, which keeps the conversion between a serial and its calendar components pure arithmetic.

## Arithmetic

Because a date is a number carrying a time unit, date arithmetic is ordinary unit arithmetic.

| Expression        | Result                  |
| ----------------- | ----------------------- |
| date `+` duration | a date, shifted later   |
| date `-` duration | a date, shifted earlier |
| date `-` date     | a duration              |

A **duration** is any value carrying a time unit: `ms`, `s`, `min`, `hr`, `d`, `wk`, `mo`, or `yr`. The fixed-length units — `ms` through `wk` — are exact, so `date + 1 wk` always lands on the same wall-clock time seven days later. A `date - date` difference comes back in seconds, the base unit of time; [cast](/math/units#casting-to-a-specific-unit) it to report the span in another unit.

```
date(2026; 7; 21) + 10 d                        => date(2026; 7; 31)
date(2026; 7; 21) - 3 d                         => date(2026; 7; 18)
date(2026; 8; 4) - date(2026; 7; 21)            => 1209600 s
(date(2026; 8; 4) - date(2026; 7; 21)) d        => 14 d
(date(2026; 8; 4) - date(2026; 7; 21)) wk       => 2 wk
(date(2026; 8; 4) - date(2026; 7; 21)) hr       => 336 hr
```

The third line is the raw difference — seconds, because that is what both dates are counted in. The three after it are the same span [cast](/math/units#casting-to-a-specific-unit) to the unit you want to report in, which is how an elapsed span is reported in whichever unit the result should use.

### Durations

The difference between two dates is a duration, and a duration is an ordinary [unit number](/math/types/unit-numbers). Durations add and subtract, scale by a plain number, and convert on cast:

```
2hr + 30min          => 2.5 hr
(2hr + 30min) min    => 150 min
3d + 12hr            => 3.5 d
(1wk - 2d) d         => 5 d
8hr * 5              => 40 hr
```

Multiplied by a rate, the time cancels and leaves what you were pricing — the arithmetic behind a timesheet, a carrying cost, or a rental:

```
40hr * 85USD/hr      => 3400 USD
2500USD / (40hr)     => 62.5 USD/hr
```

To split a duration into whole units and a remainder, cast it and take the [integer part](/math/functions/rounding-and-numeric#integer-part-ipart), then read the leftover with [`mod`](/math/functions/arithmetic-and-powers#modulo-mod):

```
ipart((9000s) hr)      => 2 hr
mod(9000s; 1 hr) min   # => 30 min
```

### `mo` and `yr` shift by an average, not a calendar step

<Warning>
  Adding `1 mo` or `1 yr` to a date does **not** move it to the same day of the next month or year. `mo` and `yr` are fixed average durations — 30.4375 days and 365.25 days — so the result lands wherever that many seconds falls. Use [`adjdate`](/math/functions/dates#calendar-adjustment-adjdate) to step a date by calendar months or years.
</Warning>

The averages are deliberate, and they are what makes duration math exact: `12 mo` is precisely `1 yr`, `18 mo` precisely `1.5 yr`, and a rate of `1200 USD/yr` precisely `100 USD/mo`. A conversion between time units always round-trips. But that consistency is the opposite of what a calendar does, where a month is 28 to 31 days and a year is 365 or 366.

Used to step a date, an average misses in one of three ways:

```
date(2026; 1; 15) + 1 mo    => date(2026; 2; 14; 10; 30)
date(2026; 1; 31) + 1 mo    => date(2026; 3; 2; 10; 30)
date(2026; 7; 21) + 1 yr    => date(2027; 7; 21; 6)
```

* **The day slips.** Thirty and a bit days from the 15th is the 14th of the next month, at 10:30 in the morning.
* **A month can be skipped entirely.** January 31 plus `1 mo` lands in March, stepping straight over February.
* **And the miss can be invisible.** A year later *is* the right calendar day — and six hours past midnight, because a quarter of a day is left over. Shown as a date alone it reads `2027-07-21` and looks correct, while the value is not equal to `date(2027; 7; 21)`. Comparisons against it fail, day boundaries fall in the wrong place, and nothing raises an error.

The third case is the one to watch. A wrong value that looks right in a result stays wrong in every calculation that consumes it, and no error marks the spot. This is why [`adjdate`](/math/functions/dates#calendar-adjustment-adjdate) exists: it is the calendar-correct form of all three, and it preserves the time of day it started with.

```
adjdate(date(2026; 1; 15); 0; 1; 0)    => date(2026; 2; 15)
adjdate(date(2026; 1; 31); 0; 1; 0)    => date(2026; 2; 28)
adjdate(date(2026; 7; 21); 0; 0; 1)    => date(2027; 7; 21)
```

The same trap appears wherever `yr` stands in for a calendar year rather than an increment. The average year is 365.25 days, which is the length of no particular year, so measuring against it drifts by a quarter or a half day depending on the year's position in the leap cycle. To count days within a year, count from the start of that year:

```
ddays(floor(report_date; "yr"); report_date) + 1    # the day of the year
```

[`floor`](/math/functions/rounding-and-numeric#snapping-a-date-to-a-boundary) gives 1 January of that year and [`ddays`](/math/functions/dates#day-counts-ddays) counts actual days to the date, so the result is right in every year, leap or not.

The rule of thumb: `mo` and `yr` are for **durations** — spans, rates, terms — and never for moving a date, and never as a stand-in for a calendar year. See [Time units and dates](/math/units#time-units-and-dates).

### Measuring a span in months or years

The averages apply in reverse too. A `date - date` difference is itself exact, since it is a count of seconds, but expressing that difference in `mo` or `yr` divides by an average — so a one-month span does not come back as `1`:

```
(date(2026; 2; 1) - date(2026; 1; 1)) d     => 31 d
(date(2026; 2; 1) - date(2026; 1; 1)) mo    => 1.0185 mo
```

Report an elapsed span in the fixed units — `hr`, `d`, `wk` — where the figure is exact, or count days with [`ddays`](/math/functions/dates#day-counts-ddays), whose `basis` argument covers the 30/360 conventions finance uses for month fractions. For a count of whole calendar months, read the components:

```
(year(end_date) - year(start_date)) * 12 + month(end_date) - month(start_date)
```

### A bare number is not a duration

Adding or subtracting a plain [scalar](/math/types/scalar-numbers) is the one operation a date rejects: `date(2026; 7; 21) + 30` returns `input.incompatible_units`. Write `date(2026; 7; 21) + 30 d` and state the unit you mean.

This is the ordinary [unit rule](/authoring/writing-equations#combining-types) — `5 m + 3` is rejected for the same reason — but it is worth calling out, because a spreadsheet would have taken the bare `30` as 30 days. Here the serial counts seconds, so a bare `30` could only mean half a minute. Requiring the unit removes the ambiguity rather than guessing at it.

Beyond that guard, a date behaves like any other number. Operations with no natural calendar meaning are not blocked — dividing a date by `2`, or adding two dates together, produces a number, exactly as it would in a spreadsheet. Whether the result means anything is yours to decide.

## Comparison

Dates compare like numbers, and the comparison is over the **whole instant**. A date at midnight is not equal to the same calendar day at nine-thirty:

```
date(2026; 7; 21) < date(2026; 12; 25)             => true
date(2026; 7; 21) == date(2026; 7; 21; 9; 30)      => false
```

To compare at a coarser granularity, snap both sides to the same boundary first — this is the idiom for "same day?" and "same month?" tests:

```
floor(date(2026; 7; 21; 9; 30); "d") == date(2026; 7; 21)   => true
```

See [Snapping a date to a boundary](/math/functions/rounding-and-numeric#snapping-a-date-to-a-boundary).

## Writing dates in equations

The examples above are written in the reference style, with a bare unit suffix. Inside an [activity](/concepts/activities) equation a unit literal is wrapped in backticks so it reads as a unit rather than a variable name — `` closing_date + 30`d` ``. See [Units in equations](/authoring/writing-equations#units-in-equations).

A date variable holds an ordinary numeric value, so it takes a default and a display format like any other [variable](/concepts/variables). Giving a variable the `date`, `datetime`, `time`, or `duration` display type is what makes its number render as a calendar value, and how its format and default are set is covered in [Authoring dates and durations](/authoring/dates-and-durations). For how a date is written on input, see [Dates, times, and durations](/concepts/input-formats#dates-times-and-durations). See [Combining types](/authoring/writing-equations#combining-types) for how time values combine with the other types.
