# DateOnly and TimeOnly

> `DateOnly` is a calendar date (no time); `TimeOnly` is a time of day (no date) — the C# types. Build them with `new DateOnly(y, m, d)` / `new TimeOnly(h, m[, s])`, extract one from a `DateTime` with `FromDateTime`, read parts (`.Year`/`.Month`/`.Day`, `.Hour`/`.Minute`/`.Second`), parse from strings, and store them on an entity.

<!-- id: types-date-and-time · area: types · stability: preview · html: https://osysharp.com/reference/types/date-and-time/ -->

## Summary        {#summary}
`DateOnly` is a **calendar date** with no time component; `TimeOnly` is a **time of day** with no date — the same
split as C#. Both are first-class scalars you can declare, pass, and store on an entity (alongside `DateTime` and
[`TimeSpan`](https://osysharp.com/reference/types/timespan/)).

## Signature      {#signature}
```osy syntax
new DateOnly(year, month, day)                 // a calendar date
new TimeOnly(hour, minute)                      // a time of day
new TimeOnly(hour, minute, second)
DateOnly.FromDateTime(dt) / TimeOnly.FromDateTime(dt)   // the date / time part of a DateTime
d.ToDateTime(t)                                 // …and back: a date + a time of day = a DateTime
DateOnly.Parse(s) / TimeOnly.Parse(s)           // from a string (throws on a bad value)
d.Year / d.Month / d.Day / d.DayOfWeek          // DateOnly parts
d.DayNumber                                     // days since 0001-01-01 — subtract two to count days between
t.Hour / t.Minute / t.Second                    // TimeOnly parts
a < b · a <= b · a > b · a >= b · a == b         // ordering + equality, on two dates or two times of day
```

## Description    {#description}
Construct with the C# spelling — `new DateOnly(2026, 3, 15)` / `new TimeOnly(9, 30)` (a three-argument `TimeOnly`
adds seconds). `FromDateTime` splits a `DateTime` into its date or time half. `Parse` reads one from a string
(invariant format; it throws on an unparseable value). Read the parts with the same member names as C#
(`.Year`/`.Month`/`.Day`; `.Hour`/`.Minute`/`.Second`), and format with `.ToString("fmt")`.

### Which came first?     {#ordering}
Compare them with `<`, `<=`, `>`, `>=` — the ordinary operators, exactly as in C#. Two dates order by calendar day;
two times of day order by clock time. `==` and `!=` compare the same way.

```osy title="a date and a time of day, ordered" test app=date-only-ordering
entity Sighting { [Required] DateOnly Night; [Required] TimeOnly Culmination; }

bool IsInThePast(DateOnly night) {
  return night < DateOnly.FromDateTime(DateTime.UtcNow);
}

bool IsBeforeDawn(TimeOnly culmination) {
  return culmination < new TimeOnly(6, 0);
}
```

Compare **like with like**: two `DateOnly`s, or two `TimeOnly`s. A `DateOnly` beside a `DateTime` is a different
question and the compiler refuses the pairing rather than guessing which one you meant — take the date out of the
`DateTime` first with `FromDateTime`.

The same comparison works inside a query, where it becomes part of the SQL:
`Sighting.Where(s => s.Night >= today)`.

### How many days between two dates?     {#days-between}
Subtract their `DayNumber`s — C#'s own spelling, and the only one there is:

```osy test app=date-only-days-between
entity Item { [Required, MaxLength(60)] string Name; [Required] DateOnly AddedOn; }

int DaysIn(DateOnly addedOn) {
  return DateOnly.FromDateTime(DateTime.UtcNow).DayNumber - addedOn.DayNumber;
}
```

`DayNumber` is the count of days since 0001-01-01, so the difference between two of them is the number of days
between the two dates. **Two dates cannot be subtracted directly** — a `DateOnly` carries no time, so there is no
duration between them, and C# has no such operator either. When you do want a duration with hours and minutes in
it, subtract two `DateTime`s instead: that gives a [`TimeSpan`](https://osysharp.com/reference/entity/properties/).

⚠ A date read out of a `DateTime` counts the same as the date itself: `DayNumber` is taken from the calendar day,
so a value stamped at 23:59 and one stamped at 00:01 the same morning answer the same number.

Both are computed **in memory** and stored as ordinary columns (a `DateOnly` as a SQL `date`, a `TimeOnly` as a
`time`).

## Examples       {#examples}
```osy title="construct, convert, and read parts" test app=date-and-time
DateOnly Christmas() {
  return new DateOnly(2026, 12, 25);
}

int MeetingHour() {
  var t = new TimeOnly(9, 30, 0);
  return t.Hour;                                 // 9
}

DateOnly DatePart(DateTime dt) {
  return DateOnly.FromDateTime(dt);              // the calendar date, time dropped
}

int MinuteOf(DateTime dt) {
  return TimeOnly.FromDateTime(dt).Minute;
}
```

A `DateOnly` / `TimeOnly` is also an **ordinary entity column** — no conversion to write, and no `DateTime` to fall
back on. This example is COMPILED by the docs gate, so the claim is checked rather than asserted:

```osy title="the same two types as entity columns" test app=date-and-time
entity Appointment {
  DateOnly Day;                  // a SQL `date` — no time of day to get wrong
  TimeOnly StartsAt;             // a SQL `time` — no date attached
}

DateOnly DayOf(Appointment a) { return a.Day; }        // reads back as itself, not as a DateTime
TimeOnly StartOf(Appointment a) { return a.StartsAt; }
```

## See also       {#see-also}
- [entity members](https://osysharp.com/reference/entity/properties/) — every type an entity member may hold, in one table
- [TimeSpan (durations)](https://osysharp.com/reference/types/timespan/) — durations, and `DateTime`/`TimeSpan` arithmetic
- [Current time (DateTime.UtcNow, DurableClock.Now)](https://osysharp.com/reference/function/current-time/) — reading the clock (`DateTime.UtcNow`), parsing, and formatting
