Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Types

TimeSpan, DateOnly, TimeOnly

TimeSpan — a duration · DateOnly — a date with no time · TimeOnly — a time with no date

A duration, a bare date, and a bare time of day. Subtracting two DateTimes gives a TimeSpan; adding one back gives a DateTime. All three are exact, and all three work identically in the browser and on the server.

stable2 examples compiled by CItypesdatetimeduration

Summary#

TimeSpan is a duration — an amount of time, with no particular start. DateOnly is a date with no time of day (a birthday, a due date). TimeOnly is a time of day with no date (an opening hour).

Signature#

due - now                       -> TimeSpan     // subtracting two DateTimes
start + TimeSpan.FromHours(2)   -> DateTime     // adding a duration back

TimeSpan.FromDays(n) · FromHours(n) · FromMinutes(n) · FromSeconds(n) · FromMilliseconds(n)
new TimeSpan(hours, minutes, seconds)  ·  new TimeSpan(days, hours, minutes, seconds)
ts.TotalHours · ts.Hours   (they are not the samesee below)

new DateOnly(2024, 3, 15)  ·  DateOnly.Parse(s)  ·  DateOnly.FromDateTime(d)
new TimeOnly(13, 45)       ·  TimeOnly.Parse(s)  ·  TimeOnly.FromDateTime(d)

Description#

Totals and parts are different things#

This is the one that trips people up. For a span of one day, two hours and three minutes:

ValueWhat it is
ts.Days1the days part
ts.Hours2the hours part — never more than 23
ts.TotalHours26.05the whole span, expressed in hours

Hours is a component of the written-out duration; TotalHours is the duration itself, converted. If you want "how long was this, in hours", you want TotalHours.

A negative duration is negative all the way down#

TimeSpan.FromHours(-2) has Hours of -2 — not 22 — and Minutes of 0. Every component carries the sign.

Durations are exact#

A TimeSpan holds exact ticks, so accumulating durations never drifts. TimeSpan.FromSeconds(3.5) is three and a half seconds precisely.

DateOnly and TimeOnly are not DateTimes#

Use DateOnly when a time of day would be meaningless — a birthday is a date, not an instant, and giving it a time invites a timezone to shift it. DateOnly.FromDateTime(d) takes the date part of a DateTime; TimeOnly.FromDateTime(d) takes the time part.

You can still read the parts off either one directly: birthday.Year, opensAt.Hour.

Examples#

string OverdueLabel(DateTime due, DateTime now) {
  var late = now - due;                       // a TimeSpan
  if (late.TotalHours < 24) {
    return $"{late.TotalHours:F1} hours late";
  }
  return $"{late.Days} days late";
}
entity Appointment {
  DateOnly Day;
  TimeOnly StartsAt;
}

bool IsMorning(Appointment a) {
  return a.StartsAt.Hour < 12;
}

See also#

Related

DateTime

A date and time. It is a wall-clock value, not an instant on a timeline, so it is never shifted by anybody's timezone…

execution side

Where a function runs. Osy# infers it from the body: a function that reads data runs on the server, a function that…