CkDate
Job: read calendar days out of the mixed shapes real data arrives in, count and move them, and write them back out in the one shape you actually need.
A day is a date without a time of day: a birth date, a due date, the edge of a billing
period. Its twin, CkDateTime, works in moments — a date and a time:
when a record was created, when a token expires. The column type picks the class: a date
column comes here, a datetime column goes there. Every question here is answered to the
day — CkDate.IsPast is false for anything dated today — where CkDateTime answers to
the moment.
Dates show up in many costumes: "2026-08-31", "8/31/2026", "31.08.2026", a bare
20260831, a Unix timestamp, an Excel day number, a boxed DateTime from a database.
CkDate reads them all with one set of rules — strict, culture-invariant, and never a
guess; a time of day in the value is read, then dropped. A value that cannot be read
answers the year-one sentinel, DateOnly.MinValue (0001-01-01) — a date no real record
carries, so bad data stands out instead of hiding as today. The OrNull twin answers null
instead, which is what a nullable database column wants.
Quick look
using SafeCoder.CommonKit;
using SafeCoder.CommonKit.Enums;
CkDate.ToDateOnly("8/31/2026 2:05 PM"); // -> 2026-08-31 (the time is dropped)
CkDate.ToDateOnlyOrNull("1900-01-01"); // -> null (an SQL placeholder, not a real date)
CkDate.ToDateOnly("junk"); // -> 0001-01-01 (rejected — the year-one sentinel)
CkDate.Iso8601("8/31/2026"); // -> "2026-08-31"
CkDate.ToDateOnly("2026-09-01T01:00:00Z", zone: "America/Chicago"); // -> 2026-08-31 (the customer's day)
CkDate.IsBefore(form["start"], form["end"]); // -> true when start is on an earlier day
CkDate.IsFuture("9/1/2026", asOf: "8/31/2026"); // -> true
CkDate.IsWeekend("2026-08-29"); // -> true (a Saturday)
CkDate.AddMonths("2026-01-31", 1); // -> 2026-02-28 (never 3 March)
CkDate.AddWeekdays("2026-09-04", 5); // -> 2026-09-11 (five business days)
CkDate.Explain("5/6/26"); // -> "a two-digit year is ambiguous"
Methods
ToDateOnly and its OrNull twin understand the same shapes as every other method here and
in CkDateTime: ISO 8601 text (the international standard, "2026-08-31"
or "2026-08-31T14:05:00Z"), bare "yyyyMMdd" as text or number, slashed / dashed /
dotted numeric dates, an optional 24-hour or AM/PM time after the date, Unix timestamps
(seconds or milliseconds since 1970), OLE automation day numbers (how Excel and older
databases count days), and boxed .NET date types.
The plain reader takes a fallback date to use when the value cannot be read — and the
fallback is checked the same way, so a placeholder fallback is no fallback. When both fail,
you get the year-one sentinel.
ToDateOnly
Read a calendar date.
DateOnly ToDateOnly(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text in any supported shape. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates — 0001-01-01, 1899-12-30, 1900-01-01, 2079-06-06, 9999-12-31 — as missing rather than real. |
fallback |
DateOnly? |
null |
Date to use when the value cannot be read. It is checked the same way — a placeholder fallback is no fallback. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
The value (or the checked fallback) reads as a date |
The calendar date: 2026-08-31 |
The calendar date: 2026-08-31 |
Rejected: missing, unreadable, an impossible date under the declared order, or a placeholder read as missing — and no usable fallback |
Year one (DateOnly.MinValue, 0001-01-01) |
Throws CkDateException — carries Input, Expected and Reason |
Two-digit years are rejected outright — never assigned a century by guess.
ToDateOnly answers a DateOnly — a calendar date with no time.
An all-numeric date like "3/4/2026" is ambiguous — March 4th or April 3rd? The
order parameter decides, and month-first is the default. For European input, pass
DateOrder.DayFirst. There is no guessing: an impossible date under the declared order
is rejected, never quietly re-read the other way.
CkDate.ToDateOnly("2026-08-31"); // -> 2026-08-31
CkDate.ToDateOnly("20260831"); // -> 2026-08-31 (the compact shape)
CkDate.ToDateOnly("3/4/2026"); // -> 2026-03-04 (month-first by default)
CkDate.ToDateOnly(
input: "3/4/2026",
order: DateOrder.DayFirst
); // -> 2026-04-03 (declared day-first)
CkDate.ToDateOnly("13/5/2026"); // -> 0001-01-01 (no month 13 — rejected, not re-read)
Whose day? A moment belongs to different days in different places, and the day you get
is the UTC one unless you say otherwise. assumeZone answers "which clock was this written
on"; zone answers "which day was this for the customer":
CkDate.ToDateOnly("2026-09-01T01:00:00Z"); // -> 2026-09-01
CkDate.ToDateOnly("2026-09-01T01:00:00Z", zone: "America/Chicago"); // -> 2026-08-31 (still the evening before, there)
CkDate.ToDateOnly("2026-09-01", zone: "America/Chicago"); // -> 2026-09-01 (a date alone is never moved)
ToDateOnlyOrNull
The same reader, answering null.
DateOnly? ToDateOnlyOrNull(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text in any supported shape. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format the text must match, when one is declared. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
A time zone id for zone-less values, converted to UTC — see CkDateTime.ToDateTime. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Reads SQL placeholder dates as missing, so they come back as null too. |
Returns
| When | You get |
|---|---|
| The value reads as a date | The calendar date: 1985-04-12 |
Missing (null or DBNull), unreadable, or a placeholder date read as missing |
null — this method never throws |
There is no fallback parameter — null is the answer for missing.
The honest version for nullable columns. It reads exactly like
ToDateOnly, but answers null for anything that is
not a real value: a missing input (null or DBNull), one that cannot be read, or —
by default — an SQL placeholder date such as 1900-01-01. There is no fallback
parameter: null is the answer for missing.
CkDate.ToDateOnlyOrNull("1985-04-12"); // -> 1985-04-12
CkDate.ToDateOnlyOrNull("1900-01-01"); // -> null (a placeholder means "nobody entered anything")
CkDate.ToDateOnlyOrNull("junk"); // -> null
CkDate.ToDateOnlyOrNull(
input: "1900-01-01",
treatSentinelsAsMissing: false
); // -> 1900-01-01 (you declared placeholders real here)
This is the reader for pulling values out of a payload bag with CkDictionary and for writing entity properties before a save.
Iso8601
A date as ISO 8601 text: "2026-08-31".
string Iso8601(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
string? |
null |
A date to write when the value cannot be read; it is read the same way. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering empty text. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | "yyyy-MM-dd" |
The text |
| Rejected: unreadable, and no usable fallback | "" |
Throws CkDateException — carries Input, Expected and Reason |
The form a JSON date field, an SQL date literal and an HTML date input all take. A time of
day in the value is read and dropped: the day is the UTC day. For a moment —
"2026-08-31T14:05:00Z" — see CkDateTime.Iso8601.
CkDate.Iso8601("8/31/2026"); // -> "2026-08-31"
CkDate.Iso8601("2026-08-31T14:05:00Z"); // -> "2026-08-31"
CkDate.Iso8601("junk"); // -> ""
Format
A date in any .NET date format.
string Format(
object? input = null,
string? into = "yyyy-MM-dd",
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
into |
string? |
"yyyy-MM-dd" |
The .NET date format to write. One that writes a time of day is refused. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
string? |
null |
A date to write when the value cannot be read; it is read the same way. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering empty text. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The date, in the invariant culture | The text |
| Rejected: unreadable and no usable fallback, a broken format, or a format with a clock in it | "" |
Throws CkDateException — carries Input, Expected and Reason |
Month and day names are English on every machine. A format that writes a time of day —
"yyyy-MM-dd HH:mm" — is refused, with the reason "the format writes a time of day, and a
date has none": a day would only ever print 00:00 there, which looks like data and is not.
Moments are written by CkDateTime.Format.
CkDate.Format("8/31/2026", into: "dd.MM.yyyy"); // -> "31.08.2026"
CkDate.Format("2026-08-31", into: "dddd, d MMMM yyyy"); // -> "Monday, 31 August 2026"
CkDate.Format("2026-08-31", into: "yyyy-MM-dd HH:mm"); // -> "" (rejected — a day has no clock)
Age
Whole years between two dates.
int Age(
object? birth = null,
object? asOf = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
int fallback = -1,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
birth |
object? |
null |
The birth date: a date value, or text in any supported shape. |
asOf |
object? |
null |
The date to measure at, read the same way. null means today (UTC) — pass a value for reproducible results and tests. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
int |
-1 |
What comes back when the age cannot be computed — an age nobody has. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of returning the fallback, when a date cannot be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| Both dates read, and the birth date is not after the measuring date | The age in whole years: 36 |
The age in whole years: 36 |
Rejected: an unreadable birth or asOf, or a birth date after the measuring date |
The fallback (default -1) |
Throws CkDateException — carries Input, Expected and Reason |
A birth date after the measuring date is bad data, not a negative age.
The age in whole years at asOf — or at today (UTC) when you give no asOf. That
"today" is the one impure spot in the class; pass an asOf for reproducible results
and tests. Bad input answers the fallback, -1 by default — an age nobody has. A
birth date after the measuring date is bad data, not a negative age.
CkDate.Age(
birth: "8/31/1990",
asOf: "8/31/2026"
); // -> 36 (the birthday itself counts)
CkDate.Age(
birth: "2/29/2000",
asOf: "2/28/2026"
); // -> 25 (leap-day birthday, not yet reached)
CkDate.Age("junk"); // -> -1 (the default fallback)
IsBetween
Is a date inside a range.
bool IsBetween(
object? input = null,
object? min = null,
object? max = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The date to test. |
min |
object? |
null |
The lower bound, inclusive. |
max |
object? |
null |
The upper bound, inclusive. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing — for input only; the bounds are always taken literally. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says which value is invalid and why, instead of returning false. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
All three read as dates and input lies inside the bounds, both ends included |
true |
true |
All three read as dates and input lies outside the bounds |
false |
false |
| Rejected: an unreadable value or bound, or bounds that contradict each other | false |
Throws CkDateException — carries Input, Expected and Reason |
The bounds never treat placeholder dates as missing, so DateOnly.MaxValue works as an open upper bound.
True when the date lies between min and max, inclusive on both ends — "is this
service date inside the coverage period". False for an unreadable value or bound, and
for bounds that contradict each other. The bounds are always taken literally — they
never treat placeholder dates as missing — so DateOnly.MaxValue works as an open
upper bound.
CkDate.IsBetween(
input: "6/15/2026",
min: "1/1/2026",
max: "12/31/2026"
); // -> true
CkDate.IsBetween(
input: "12/31/2026",
min: "1/1/2026",
max: "12/31/2026"
); // -> true (both ends count)
IsDate
Does it read as a date at all?
bool IsDate(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
bool treatSentinelsAsMissing = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering false, when the value cannot be read. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a date under the readers' rules | true |
true |
| Rejected: unreadable, an impossible date in the declared order, a two-digit year, a placeholder | false |
Throws CkDateException — carries Input, Expected and Reason |
The yes-or-no form of ToDateOnly and CkDateTime.ToDateTime: any supported
shape, strict, never a guess, the placeholder dates counting as missing by default. When
the value is going to be stored, read it with an OrNull reader instead and check for
null — that answers the same question and hands you the value.
CkDate.IsDate("8/31/2026"); // -> true
CkDate.IsDate(20260831); // -> true
CkDate.IsDate("13/5/2026"); // -> false (impossible month-first; never re-read)
CkDate.IsDate("1900-01-01"); // -> false (a placeholder)
IsPlaceholder
Is it one of the "nobody entered anything" dates?
bool IsPlaceholder(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering false, when the value is not a date at all. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a date and it is 0001-01-01, 1899-12-30, 1900-01-01, 2079-06-06 or 9999-12-31 | true |
true |
| Any other date | false |
false |
| Rejected: not a date | false |
Throws CkDateException — carries Input, Expected and Reason |
The readers turn the placeholder dates into "missing" silently; this method says so out
loud. For data-quality checks — "how many rows hold 1900-01-01" — and for deciding what
a source really sent. There is no treatSentinelsAsMissing here: the placeholders must
read as dates, that being the whole question.
CkDate.IsPlaceholder("1900-01-01"); // -> true
CkDate.IsPlaceholder("1/1/1900"); // -> true
CkDate.IsPlaceholder(2.0); // -> true (Excel day 2 is 1900-01-01)
CkDate.IsPlaceholder("2026-08-31"); // -> false
IsBefore
Does a date fall on an earlier day than another?
bool IsBefore(
object? input = null,
object? other = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The date to test. |
other |
object? |
null |
The date it is compared with. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering false, when either value cannot be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
Both read, and input is on an earlier day |
true |
true |
| Both read, otherwise — the same day included | false |
false |
| Rejected: either unreadable | false |
Throws CkDateException — carries Input, Expected and Reason |
By calendar day, in UTC: two moments on the same day are neither before nor after each other. To compare to the moment see CkDateTime.IsBefore.
CkDate.IsBefore(form["start"], form["end"]); // -> true when start is on an earlier day
CkDate.IsBefore("2026-08-31T09:00:00Z", "2026-08-31T17:00:00Z"); // -> false (the same day)
IsAfter
Does a date fall on a later day than another?
bool IsAfter(
object? input = null,
object? other = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The date to test. |
other |
object? |
null |
The date it is compared with. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering false, when either value cannot be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
Both read, and input is on a later day |
true |
true |
| Both read, otherwise — the same day included | false |
false |
| Rejected: either unreadable | false |
Throws CkDateException — carries Input, Expected and Reason |
By calendar day, in UTC: two moments on the same day are neither before nor after each other. To compare to the moment see CkDateTime.IsAfter.
CkDate.IsAfter(form["start"], form["end"]); // -> true when start is on a later day
CkDate.IsAfter("2026-08-31T09:00:00Z", "2026-08-31T17:00:00Z"); // -> false (the same day)
IsPast
Is it before today?
bool IsPast(
object? input = null,
object? asOf = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The date to test. |
asOf |
object? |
null |
The day to compare with, read the same way. null means today (UTC) — pass a value for reproducible results and tests. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering false, when a date cannot be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
Both read and input falls on an earlier calendar day than asOf |
true |
true |
Both read, and input is the same day or later |
false |
false |
| Rejected: either date cannot be read | false |
Throws CkDateException — carries Input, Expected and Reason |
A birth date, a service already rendered, an expiry gone by. The comparison is by
calendar day, in UTC: a value dated today is neither past nor future, whatever its time
of day. "Today" is UTC, the same as Age; pass asOf for a local today. To compare
moments, use IsBefore.
CkDate.IsPast("8/30/2026", asOf: "8/31/2026"); // -> true
CkDate.IsPast("8/31/2026", asOf: "8/31/2026"); // -> false (today is not the past)
CkDate.IsPast("2000-01-01"); // -> true (against today)
IsFuture
Is it after today?
bool IsFuture(
object? input = null,
object? asOf = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The date to test. |
asOf |
object? |
null |
The day to compare with, read the same way. null means today (UTC) — pass a value for reproducible results and tests. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering false, when a date cannot be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
Both read and input falls on a later calendar day than asOf |
true |
true |
Both read, and input is the same day or earlier |
false |
false |
| Rejected: either date cannot be read | false |
Throws CkDateException — carries Input, Expected and Reason |
An expiry still ahead, a booking not yet due. Same rules as IsPast: calendar
days, UTC, today is neither. A placeholder such as 9999-12-31 is missing by default; pass
treatSentinelsAsMissing: false when it means "never expires".
CkDate.IsFuture("9/1/2026", asOf: "8/31/2026"); // -> true
CkDate.IsFuture("2026-08-31T23:59:59Z", asOf: "8/31/2026"); // -> false (still today)
IsToday
Is it today?
bool IsToday(
object? input = null,
object? asOf = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The date to test. |
asOf |
object? |
null |
The day to compare with, read the same way. null means today (UTC) — pass a value for reproducible results and tests. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering false, when a date cannot be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| Both read and fall on the same calendar day | true |
true |
| Both read, on different days | false |
false |
| Rejected: either date cannot be read | false |
Throws CkDateException — carries Input, Expected and Reason |
The appointment is today, the record was created today. Calendar days in UTC, so the
time of day never matters. With an explicit asOf it is a "same day" check between any
two dates.
CkDate.IsToday(DateTime.UtcNow); // -> true
CkDate.IsToday("2026-08-31T23:59:59Z", asOf: "8/31/2026"); // -> true
IsAdult
Old enough?
bool IsAdult(
object? birth = null,
object? asOf = null,
int minAge = CkSettings.DefaultAdultAge,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
birth |
object? |
null |
The birth date: a date value, or text in any supported shape. |
asOf |
object? |
null |
The date to measure at, read the same way. null means today (UTC) — pass a value for reproducible results and tests. |
minAge |
int |
CkSettings.DefaultAdultAge (18) |
The age, in whole years, from which a person counts as an adult. Below 0 acts as 0. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering false, when a date cannot be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
Both dates read and the age in whole years is at least minAge |
true |
true |
Both read, and the age is below minAge |
false |
false |
| Rejected: an unreadable date, or a birth date after the as-of date | false |
Throws CkDateException — carries Input, Expected and Reason |
The arithmetic of Age, as a yes or no. The birthday itself counts. A birth date
after the as-of date is bad data, not a minor, so it is rejected rather than answered
false quietly — the flag makes it say so.
CkDate.IsAdult("8/31/2008", asOf: "8/31/2026"); // -> true (18 today)
CkDate.IsAdult("9/1/2008", asOf: "8/31/2026"); // -> false (tomorrow)
CkDate.IsAdult("8/31/2006", asOf: "8/31/2026", minAge: 21); // -> false (20)
IsWeekday
Monday to Friday?
bool IsWeekday(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering false, when the value cannot be read. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a date on a Monday to Friday | true |
true |
| A Saturday or Sunday | false |
false |
| Rejected: not a date | false |
Throws CkDateException — carries Input, Expected and Reason |
The working days, for delivery, settlement and scheduling checks. The day is the UTC
calendar day of the moment read; a zone-less date is taken as written. Holidays are not
known here — they differ by country and change — so that list stays in your application.
IsWeekend is the twin; both answer false for an unreadable value, so
neither is the other's negation.
CkDate.IsWeekday("2026-08-31"); // -> true (a Monday)
CkDate.IsWeekday("2026-08-29"); // -> false (a Saturday)
IsWeekend
Saturday or Sunday?
bool IsWeekend(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering false, when the value cannot be read. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a date on a Saturday or Sunday | true |
true |
| A weekday | false |
false |
| Rejected: not a date | false |
Throws CkDateException — carries Input, Expected and Reason |
Saturday and Sunday, the ISO 8601 week's rest days — what "weekend" means here
regardless of country. Same rules as IsWeekday. Note that with assumeZone
the UTC day decides: Friday 22:00 in Chicago is Saturday 03:00 UTC, and a weekend.
CkDate.IsWeekend("2026-08-29"); // -> true
CkDate.IsWeekend("junk"); // -> false (and so is IsWeekday)
IsLeapYear
Is it a leap year?
bool IsLeapYear(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a year on its own, a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads: month first or day first. |
format |
string? |
null |
An exact .NET format string, e.g. "dd.MM.yyyy". When given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. null means zone-less digits count as UTC. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering false, when the value is neither a year nor a date. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| A bare year, or the year of a date the readers accept, is a leap year | true |
true |
| It is not | false |
false |
| Rejected: neither a year nor a date | false |
Throws CkDateException — carries Input, Expected and Reason |
2024 yes, 2026 no, 1900 no, 2000 yes. The value may be a year on its own — 2024,
"2024" — or any date the readers accept, whose year is taken. A bare whole number from
1 to 9999 is a year here, not Unix seconds: nobody asks whether the first hours of 1970
were a leap year.
CkDate.IsLeapYear(2024); // -> true
CkDate.IsLeapYear("2/29/2024"); // -> true
CkDate.IsLeapYear("1900"); // -> false (a century, not divisible by 400)
DaysBetween
Whole days from one date to another.
int DaysBetween(
object? from = null,
object? to = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
int fallback = int.MinValue,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
from |
object? |
null |
The earlier date, or the later one for a negative answer. |
to |
object? |
null |
The date to count to. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
int |
int.MinValue |
What comes back when a date cannot be read — a distance no two dates have. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the fallback, when a date cannot be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| Both dates read | The signed number of calendar days, the time of day ignored | The days |
| Rejected: either date cannot be read | The fallback |
Throws CkDateException — carries Input, Expected and Reason — Expected names which date |
The companion of Age, which counts years. "Days until the deadline", "days since the last order", "nights of a stay". Calendar days, not 24-hour spans: 23:00 to 01:00 the next day is one day.
CkDate.DaysBetween("1/1/2026", "1/31/2026"); // -> 30
CkDate.DaysBetween("1/31/2026", "1/1/2026"); // -> -30
CkDate.DaysBetween("junk", "1/1/2026"); // -> int.MinValue
Add
Years, months and days, in one move.
DateOnly Add(
object? input = null,
int years = 0,
int months = 0,
int days = 0,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
years |
int |
0 |
Years to add; negative to go back. |
months |
int |
0 |
Months to add; negative to go back. |
days |
int |
0 |
Days to add, after the years and months; negative to go back. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be used. A placeholder fallback is no fallback. It comes back as given, never moved. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The day reached | The day |
| Rejected: unreadable, or the result is outside the calendar (years 1 to 9999), and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
Read the way ToDateOnly reads a value. The years and months are added together, as months, and the day is held to the end of the month when the month reached is shorter: the 31st plus one month is the 28th, 29th or 30th — never the 3rd of the month after, which is what adding "a month" by hand, or in many libraries, gives. The days are added last. One rule follows from holding the day: the end of the month is not sticky — 28 February plus one month is 28 March, not the 31st.
AddDays, AddMonths and AddYears are the same move with one unit each.
CkDate.Add("2026-01-31", months: 1); // -> 2026-02-28
CkDate.Add("2024-02-29", years: 1); // -> 2025-02-28
CkDate.Add(
input: "2026-01-31",
months: 1,
days: 1
); // -> 2026-03-01 (28 February, then one more day)
CkDate.Add("2026-08-31", days: -30); // -> 2026-08-01
CkDate.Add("9999-06-15", years: 1); // -> 0001-01-01 (rejected — outside the calendar)
AddDays
A number of days on, or back.
DateOnly AddDays(
object? input = null,
int days = 0,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
days |
int |
0 |
Days to add; negative to go back. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be used. A placeholder fallback is no fallback. It comes back as given, never moved. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The day reached | The day |
| Rejected: unreadable, or the result is outside the calendar (years 1 to 9999), and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
Calendar days. For days that skip weekends and holidays see AddWeekdays.
CkDate.AddDays("2026-08-31", 30); // -> 2026-09-30
CkDate.AddDays("2026-08-31", -30); // -> 2026-08-01
AddMonths
A number of months on, the day held to the month's end.
DateOnly AddMonths(
object? input = null,
int months = 0,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
months |
int |
0 |
Months to add; negative to go back. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be used. A placeholder fallback is no fallback. It comes back as given, never moved. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The day reached | The day |
| Rejected: unreadable, or the result is outside the calendar (years 1 to 9999), and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
A subscription started on the 31st renews on the last day of a shorter month, never in the month after it. See Add for the rule in full.
CkDate.AddMonths("2026-01-31", 1); // -> 2026-02-28
CkDate.AddMonths("2026-03-31", -1); // -> 2026-02-28
CkDate.AddMonths("2026-08-15", 12); // -> 2027-08-15
AddYears
A number of years on.
DateOnly AddYears(
object? input = null,
int years = 0,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
years |
int |
0 |
Years to add; negative to go back. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be used. A placeholder fallback is no fallback. It comes back as given, never moved. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The day reached | The day |
| Rejected: unreadable, or the result is outside the calendar (years 1 to 9999), and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
A leap day lands on 28 February in a year without one.
CkDate.AddYears("2024-02-29", 1); // -> 2025-02-28
CkDate.AddYears("2024-02-29", 4); // -> 2028-02-29
AddWeekdays
Business days on: weekends and your holidays stepped over.
DateOnly AddWeekdays(
object? input = null,
int days = 0,
object? holidays = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
days |
int |
0 |
Weekdays to add; negative to go back. At most CkSettings.MaxWeekdaysToAdd (10,000) either way. |
holidays |
object? |
null |
Days that do not count: one date, or a list of them (string[], List<DateOnly>…), each read like the value itself. null for none. One that cannot be read rejects the call. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be used. A placeholder fallback is no fallback. It comes back as given, never moved. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The weekday reached | The day |
| Rejected: unreadable, a holiday that cannot be read, more than 10,000 weekdays, or the result is outside the calendar (years 1 to 9999), and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
Monday to Friday count; Saturday, Sunday and the days you name in holidays do not.
CommonKit knows no country's holidays — they differ by country, by region and by year,
so they are your list, from your database or your configuration. Zero days answers the date
as it is, a Saturday included. A holiday that cannot be read rejects the call: one silently
dropped would move a due date.
CkDate.AddWeekdays("2026-09-04", 1); // -> 2026-09-07 (Friday to Monday)
CkDate.AddWeekdays("2026-09-07", -1); // -> 2026-09-04
string[] holidays = { "2026-12-25", "2027-01-01" };
CkDate.AddWeekdays(
input: "2026-12-23",
days: 5,
holidays: holidays
); // -> 2026-12-31
MonthsBetween
Whole months from one date to another.
int MonthsBetween(
object? from = null,
object? to = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
int fallback = int.MinValue,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
from |
object? |
null |
The earlier date, or the later one for a negative answer. |
to |
object? |
null |
The date to count to. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
int |
int.MinValue |
What comes back when a date cannot be read — a number no two dates are apart. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says which date cannot be used and why, instead of answering the fallback. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| Both dates read | The signed number of whole months | The count |
| Rejected: either date unreadable | The fallback |
Throws CkDateException — carries Input, Expected and Reason |
The time of day is ignored. A month is complete when the day of the month comes round again — the rule Age counts years by — so from 31 January, 28 February is still 0 and 1 March is 1. That differs on purpose from AddMonths, which has to land somewhere and picks the 28th.
CkDate.MonthsBetween("2026-01-15", "2026-08-14"); // -> 6
CkDate.MonthsBetween("2026-01-15", "2026-08-15"); // -> 7
CkDate.MonthsBetween("2026-08-15", "2026-01-15"); // -> -7
CkDate.MonthsBetween("2026-01-31", "2026-02-28"); // -> 0
WeekdaysBetween
Business days from one date to another.
int WeekdaysBetween(
object? from = null,
object? to = null,
object? holidays = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
int fallback = int.MinValue,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
from |
object? |
null |
The earlier date, or the later one for a negative answer. |
to |
object? |
null |
The date to count to. |
holidays |
object? |
null |
Days that do not count: one date, or a list of them (string[], List<DateOnly>…), each read like the value itself. null for none. One that cannot be read rejects the call. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
int |
int.MinValue |
What comes back when a date cannot be read — a number no two dates are apart. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says which date cannot be used and why, instead of answering the fallback. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| Both dates read | The signed number of weekdays, the first date counted and the second not | The count |
| Rejected: either date unreadable, or a holiday that cannot be read | The fallback |
Throws CkDateException — carries Input, Expected and Reason |
DaysBetween with the weekends left out, and with them the days you name
in holidays. Monday to Friday is 4, as DaysBetween has it; Friday to the Monday after
is 1, the Friday.
CkDate.WeekdaysBetween("2026-09-04", "2026-09-07"); // -> 1
CkDate.WeekdaysBetween("2026-09-07", "2026-09-14"); // -> 5
CkDate.WeekdaysBetween(
from: "2026-09-04",
to: "2026-09-09",
holidays: "2026-09-07"
); // -> 2
StartOfMonth
The first of the month.
DateOnly StartOfMonth(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be read. A placeholder fallback is no fallback. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | That day of its month | The day |
| Rejected: unreadable, and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
Read the way ToDateOnly reads a value. For statement periods, "this month's orders" and the ends of a range — the off-by-one, answered once.
CkDate.StartOfMonth("8/31/2026"); // -> 2026-08-01
EndOfMonth
The last day of the month, whatever it is.
DateOnly EndOfMonth(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be read. A placeholder fallback is no fallback. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | That day of its month | The day |
| Rejected: unreadable, and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
Read the way ToDateOnly reads a value. For statement periods, "this month's orders" and the ends of a range — the off-by-one, answered once.
CkDate.EndOfMonth("2/10/2024"); // -> 2024-02-29 (a leap year)
CkDate.EndOfMonth("2/10/2026"); // -> 2026-02-28
StartOfWeek
The first day of the week.
DateOnly StartOfWeek(
object? input = null,
DayOfWeek firstDay = DayOfWeek.Monday,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
firstDay |
DayOfWeek |
DayOfWeek.Monday |
The day a week starts on. Monday, as ISO 8601 has it; pass DayOfWeek.Sunday for the US habit. Never read from the machine's culture. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be used. A placeholder fallback is no fallback. It comes back as given, never moved. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The first day of its week | The day |
| Rejected: unreadable, or the result is outside the calendar (years 1 to 9999), and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
The week starts on firstDay — said by you, never taken from the machine's culture, which would give one answer on a developer's laptop and another on the server.
CkDate.StartOfWeek("2026-09-02"); // -> 2026-08-31 (a Wednesday; the Monday before)
CkDate.StartOfWeek("2026-09-02", DayOfWeek.Sunday); // -> 2026-08-30
EndOfWeek
The last day of the week.
DateOnly EndOfWeek(
object? input = null,
DayOfWeek firstDay = DayOfWeek.Monday,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
firstDay |
DayOfWeek |
DayOfWeek.Monday |
The day a week starts on. Monday, as ISO 8601 has it; pass DayOfWeek.Sunday for the US habit. Never read from the machine's culture. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be used. A placeholder fallback is no fallback. It comes back as given, never moved. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The last day of its week | The day |
| Rejected: unreadable, or the result is outside the calendar (years 1 to 9999), and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
The day before the next firstDay.
CkDate.EndOfWeek("2026-09-02"); // -> 2026-09-06
CkDate.EndOfWeek("2026-09-02", DayOfWeek.Sunday); // -> 2026-09-05
StartOfQuarter
The first day of the quarter.
DateOnly StartOfQuarter(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be used. A placeholder fallback is no fallback. It comes back as given, never moved. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The first day of its quarter | The day |
| Rejected: unreadable, and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
Calendar quarters, as Quarter counts them.
CkDate.StartOfQuarter("2026-08-10"); // -> 2026-07-01
EndOfQuarter
The last day of the quarter.
DateOnly EndOfQuarter(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be used. A placeholder fallback is no fallback. It comes back as given, never moved. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The last day of its quarter | The day |
| Rejected: unreadable, and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
Calendar quarters, as Quarter counts them.
CkDate.EndOfQuarter("2026-08-10"); // -> 2026-09-30
StartOfYear
The first of January.
DateOnly StartOfYear(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be used. A placeholder fallback is no fallback. It comes back as given, never moved. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The first day of its year | The day |
| Rejected: unreadable, and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
The lower edge of a yearly report.
CkDate.StartOfYear("2026-08-10"); // -> 2026-01-01
EndOfYear
The thirty-first of December.
DateOnly EndOfYear(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
DateOnly? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
DateOnly? |
null |
A date to use when the value cannot be used. A placeholder fallback is no fallback. It comes back as given, never moved. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the year-one sentinel, when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The last day of its year | The day |
| Rejected: unreadable, and no usable fallback | DateOnly.MinValue, the year-one sentinel |
Throws CkDateException — carries Input, Expected and Reason |
The upper edge of a yearly report.
CkDate.EndOfYear("2026-08-10"); // -> 2026-12-31
Quarter
Which quarter of the year.
int Quarter(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
int fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
int |
0 |
What comes back when the value cannot be read — no quarter is 0. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the fallback, when the value cannot be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | 1 to 4, January to March being 1 | The quarter |
| Rejected: unreadable | The fallback |
Throws CkDateException — carries Input, Expected and Reason |
Calendar quarters. A fiscal year that starts elsewhere is the application's arithmetic on top of this.
CkDate.Quarter("2026-08-31"); // -> 3
CkDate.Quarter("junk"); // -> 0
WeekOfYear
The ISO 8601 week number.
int WeekOfYear(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
string? zone = CkSettings.DefaultZone,
bool treatSentinelsAsMissing = true,
int fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
zone |
string? |
CkSettings.DefaultZone (null) |
The time zone whose calendar day counts, as an IANA or Windows id; null for the UTC day. Only a value with a time of day is placed — "today" included: a date alone is that day everywhere, and no zone moves it. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
fallback |
int |
0 |
What comes back when the value cannot be read — no week is 0. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkDateException that says why, instead of answering the fallback, when the value cannot be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | 1 to 53 | The week |
| Rejected: unreadable | The fallback |
Throws CkDateException — carries Input, Expected and Reason |
Weeks start on Monday, and week 1 is the one holding the year's first Thursday. The days around New Year can therefore belong to the other year's weeks, which is where a hand-written week number goes wrong.
CkDate.WeekOfYear("2026-08-31"); // -> 36
CkDate.WeekOfYear("2027-01-01"); // -> 53 (a Friday: still the last week of 2026)
CkDate.WeekOfYear("2025-12-29"); // -> 1 (a Monday: already the first week of 2026)
Explain
Why not — in words, without an exception.
string Explain(
object? input = null,
DateOrder order = CkSettings.DefaultDateOrder,
string? format = null,
string? assumeZone = CkSettings.DefaultAssumeZone,
bool treatSentinelsAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a date, a number, or text. |
order |
DateOrder |
CkSettings.DefaultDateOrder (DateOrder.MonthFirst) |
How an ambiguous all-numeric date like "3/4/2026" reads. |
format |
string? |
null |
An exact .NET format string; when given, text must match it and nothing else is tried. |
assumeZone |
string? |
CkSettings.DefaultAssumeZone (null) |
The time zone the value's digits are wall-clock in, when the value declares no zone of its own. A date alone is never moved by it, as everywhere in this class. An id the library does not know counts as UTC. |
treatSentinelsAsMissing |
bool |
true |
Counts the placeholder dates as missing rather than real. |
Returns
| When | You get |
|---|---|
| The value passes the reader | "" |
| It does not | The reason, the same words the exception would carry — an answer, not a failure; it never throws |
The form-friendly twin of throwIfInvalid: the message to show beside the field,
without a try and catch. Every class with a reader has one, on the reader's own
rules, so the reason is exactly what the reader would say.
CkDate.Explain("8/31/2026"); // -> ""
CkDate.Explain("5/6/26"); // -> "a two-digit year is ambiguous"
CkDate.Explain("1900-01-01"); // -> "a placeholder date that means missing (1900-01-01)"
Your time zone, set once
Two constants in CkSettings stand behind the two zone parameters, so an application that
lives in one zone says so in one place. Both ship as null, which means UTC, and a call can
always pass its own value — "UTC" included.
| Constant | Stands behind | Decides |
|---|---|---|
CkSettings.DefaultZone |
every zone parameter, here and in CkDateTime |
Whose calendar day counts. What "today" is for IsToday, IsPast, IsFuture, Age, IsAdult; which day a moment belongs to in every method on this page; on whose wall clock CkDateTime.Add keeps the time of day; where CkDateTime.StartOfDay falls; what CkDateTime.InZone shows when told nothing. |
CkSettings.DefaultAssumeZone |
every assumeZone parameter |
Which clock a zone-less value was written on. "2026-08-31 14:05" from a form is 14:05 in Bucharest, not in UTC. |
// with DefaultZone = "Europe/Bucharest"
CkDate.IsToday("2026-09-01"); // true from midnight in Bucharest — 21:00 UTC the evening before
CkDate.ToDateOnly("2026-08-31T22:30:00Z"); // -> 2026-09-01 (the day it was there)
CkDate.ToDateOnly("2026-08-31"); // -> 2026-08-31 (a date alone is never moved)
CkDateTime.StartOfDay(DateTime.UtcNow); // -> Bucharest's midnight, as a UTC moment
CkDateTime.InZone(order.CreatedAt); // -> Bucharest's wall clock
DefaultZone is safe to set: it changes which day an answer names and never a stored
value — every DateTime handed out stays UTC, and a date with no time of day is never moved
by either zone.
DefaultAssumeZone — only when your database stores local time
CommonKit hands out UTC. You save 11:05. The database gives it back with no zone — SQL
Server as a DateTime of unspecified kind, MySQL as text. With DefaultAssumeZone set
to Bucharest, that value is read as Bucharest's clock and becomes 08:05 — and three hours
more on every save and load. If the database stores UTC, leave this null and pass
assumeZone: on the calls that read what people typed. If it stores local time, this
setting is exactly right.
The constants are compiled in: set them in your copy of the source. An application that references the package says the zone per call instead.
Good to know
- Days here, moments in CkDateTime. Every comparison on this page —
IsBefore,IsAfter,IsBetween,IsPast,IsFuture,IsToday,IsWeekday,IsWeekend— is by calendar day: a value dated today is neither past nor future whatever its time of day, and two moments on the same day are neither before nor after each other. The same names in CkDateTime compare to the moment. - The day of a moment is the UTC day — unless the
zoneparameter, orCkSettings.DefaultZonebehind it, names whose day is wanted. A date alone is never moved, by that zone or byassumeZone:"2026-08-31"is 31 August on every clock. - Month-first is the default order.
"3/4/2026"reads as March 4th unless you passorder: DateOrder.DayFirst. An impossible date under the declared order —"13/5/2026"month-first — is rejected, never silently re-read the other way. Declare the order once per data source and stay honest. - Two-digit years are rejected.
"5/6/26"answers the year-one sentinel rather than being assigned a century by guess. - Placeholder dates count as missing by default. 0001-01-01, 1899-12-30,
1900-01-01, 2079-06-06 and 9999-12-31 are the dates databases use for "nobody entered
anything". Pass
treatSentinelsAsMissing: falsewhen one of them is real data. - Zone-less digits count as UTC. A value with no zone of its own is declared UTC,
never shifted — unless you name the wall clock it was written in with
assumeZone. A value that declares its own zone always wins. - Fallbacks are checked too. A
fallbackgoes through the same reading; a placeholder fallback is no fallback, so a bad default cannot slip through. - Today is today in the
zone— UTC when none is set.Age,IsAdult,IsPast,IsFutureandIsTodaymeasure against this moment when you give noasOf; at one in the morning in Bucharest it is still yesterday in UTC, which is whatzoneis for. PassasOfin tests. - Holidays are yours.
AddWeekdaysandWeekdaysBetweenknow Saturdays and Sundays and nothing else: no country's holidays are written into the library. - Reader versus sentinel. CkSentinel
.ToNullnever touches the value, it only nulls the placeholders. Use the reader when parsing, the sentinel in save hooks.
If you want errors instead
Pass throwIfInvalid: true and a value that cannot be used raises a CkDateException
(from SafeCoder.CommonKit.Exceptions) instead of a sentinel, empty text or a fallback.
The exception carries the Input, what was Expected and a short Reason, flattened
into a one-line message. The OrNull twin has no such switch — it never throws;
null is its answer for anything unusable. When a bad date should be an error, read
it with the plain sibling and throwIfInvalid: true.
CkDate.ToDateOnly(
input: "junk",
throwIfInvalid: true
); // throws CkDateException — the message says why
CkDate.ToDateOnlyOrNull("junk"); // -> null (the OrNull twin never throws)