Summary#
⚠ There is no DateOnly.Today (nor a TimeOnly.Now) — DateOnly/TimeOnly have no member that reads the
clock. For today's date as a DateOnly, take the date part off the current instant instead:
DateOnly.FromDateTime(DateTime.UtcNow) (see Current time (DateTime.UtcNow, DurableClock.Now) for DateTime.UtcNow/DurableClock.UtcNow).
When you have the components rather than a string, these build the value directly. DateTime.New(y, m, d)
makes a date at midnight, and DateTime.New(y, m, d, h, mi, s) includes the time. DateOnly.New(y, m, d)
makes a bare calendar date, and TimeOnly.New(h, m) / TimeOnly.New(h, m, s) makes a bare time of day.
DateOnly.FromDateTime(dt) / TimeOnly.FromDateTime(dt) split a DateTime into its date or time half — the way to
get "today"/"now" as a bare DateOnly/TimeOnly, since neither has its own clock read.
Signature#
DateTime.New(<int> year, <int> month, <int> day) -> DateTime // 00:00:00
DateTime.New(<int> year, <int> month, <int> day, <int> hour, <int> minute, <int> second) -> DateTime
DateTime.New(<int> year, <int> month, <int> day, <int> hour, <int> minute, <int> second, <DateTimeKind> kind) -> DateTime
DateTime.SpecifyKind(<DateTime> value, <DateTimeKind> kind) -> DateTime
DateOnly.New(<int> year, <int> month, <int> day) -> DateOnly
TimeOnly.New(<int> hour, <int> minute) -> TimeOnly // seconds = 0
TimeOnly.New(<int> hour, <int> minute, <int> second) -> TimeOnly
DateOnly.FromDateTime(<DateTime> dt) -> DateOnly // no DateOnly.Today — this is "today"
TimeOnly.FromDateTime(<DateTime> dt) -> TimeOnly // no TimeOnly.Now — this is "now"Description#
DateTime.New builds a DateTime from whole components. With three arguments the time is midnight; the
six-argument form sets the time explicitly. The result is a UTC instant, consistent with the rest of the date
surface (Current time (DateTime.UtcNow, DurableClock.Now) explains why a DateTime here is always UTC).
DateTimeKind — accepted, and what it means here#
new DateTime(y, m, d, h, mi, s, DateTimeKind.Utc) is what a C# author writes for an instant that is unambiguously
UTC, and it compiles. So does DateTime.SpecifyKind(value, DateTimeKind.Utc).
They change nothing, and that is the point: every DateTime in Osy# is already a UTC instant
(Current time (DateTime.UtcNow, DurableClock.Now)), so Utc and Unspecified both name the instant the components describe. The overload
exists so the idiom you already know means what it says instead of failing to compile.
⛔ DateTimeKind.Local is refused, by name. It asks for the machine's own time zone — a fact about where the code
ran, not about the value, so it would mean one thing on the server and another in the browser. For a wall clock in a
particular place use TimeZoneInfo: TimeZoneInfo.ConvertTimeFromUtc(instant, zone) reads one, and zone.GetUtcOffset(instant) its offset.
DateOnly.New and TimeOnly.New build the two "half" values — a date with no time, and a time with no date
(TimeSpan, DateOnly, TimeOnly). TimeOnly.New defaults the seconds to 0 when you pass only hours and
minutes.
Constructing vs parsing. Use New when you have the numeric components; use DateTime.Parse(s) (and
DateOnly.Parse / TimeOnly.Parse) when you have a string — for example an ISO timestamp from an API.
The parsing side lives with the current-time surface, Current time (DateTime.UtcNow, DurableClock.Now).
These are pure, so they run in the browser with no round trip (Reading a date — Year, Month, Day, Hour, Minute, Second, DayOfWeek, DayOfYear, Date reads the components back off the value you build).
Examples#
DateTime MonthStart(int year, int month) {
return DateTime.New(year, month, 1); // day 1, midnight
}[Test]
void Datetime_construct() {
// three args → midnight
var midnight = DateTime.New(2024, 3, 15);
Assert.Equal(15, midnight.Day);
Assert.Equal(0, midnight.Hour);
Assert.Equal(1, MonthStart(2024, 3).Day);
// six args → explicit time
var full = DateTime.New(2024, 3, 15, 13, 45, 30);
Assert.Equal(13, full.Hour);
Assert.Equal(30, full.Second);
// seven args → the same instant, said explicitly. `Unspecified` says it too; `Local` is refused at compile.
var kinded = DateTime.New(2024, 3, 15, 13, 45, 30, DateTimeKind.Utc);
Assert.Equal(full, kinded);
Assert.Equal(full, DateTime.New(2024, 3, 15, 13, 45, 30, DateTimeKind.Unspecified));
Assert.Equal(full, DateTime.SpecifyKind(full, DateTimeKind.Utc));
// the half values
Assert.Equal(29, DateOnly.New(2024, 2, 29).Day); // a valid leap day
Assert.Equal(45, TimeOnly.New(13, 45).Minute);
Assert.Equal(0, TimeOnly.New(13, 45).Second); // seconds default to 0
Assert.Equal(30, TimeOnly.New(13, 45, 30).Second);
// "today" as a bare DateOnly — there is no `DateOnly.Today`; split it off the current instant instead
var today = DateOnly.FromDateTime(DateTime.UtcNow);
Assert.Equal(DateTime.UtcNow.Year, today.Year);
}See also#
- Current time (DateTime.UtcNow, DurableClock.Now) — the current instant, and
DateTime.Parsefor building from a string - Reading a date — Year, Month, Day, Hour, Minute, Second, DayOfWeek, DayOfYear, Date — reading the components back off a value
- TimeSpan, DateOnly, TimeOnly — the
DateOnlyandTimeOnlytypes