Skip to content

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 zone parameter, or CkSettings.DefaultZone behind it, names whose day is wanted. A date alone is never moved, by that zone or by assumeZone: "2026-08-31" is 31 August on every clock.
  • Month-first is the default order. "3/4/2026" reads as March 4th unless you pass order: 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: false when 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 fallback goes 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, IsFuture and IsToday measure against this moment when you give no asOf; at one in the morning in Bucharest it is still yesterday in UTC, which is what zone is for. Pass asOf in tests.
  • Holidays are yours. AddWeekdays and WeekdaysBetween know Saturdays and Sundays and nothing else: no country's holidays are written into the library.
  • Reader versus sentinel. CkSentinel.ToNull never 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)