monthDayCountSafe static method
Returns the number of days in month, tolerating a null year and
an out-of-range month without throwing.
This is the null-year-tolerant, non-throwing companion to monthDayCount. Two contract differences make it safe for partial or untrusted date parts:
- Nullable
year: whenyearisnull, February returns28because a leap year cannot be resolved without a year. A known leap year still yields29. (monthDayCount requires a non-null year.) - No throw on bad
month: anymonththat is not February and not in the 31-day set returns30, including out-of-range values such as0,13,-1, or very large ints. (monthDayCount throws ArgumentError formonthoutside 1–12.)
The silent 30 for an invalid month is intentional but is a footgun:
validate month separately (e.g. via isValidDateParts) when you need
to reject bad input rather than coerce it. Negative years are accepted
and follow the proleptic Gregorian leap rule (e.g. -4 is a leap year).
Example:
DateTimeUtils.monthDayCountSafe(year: 2024, month: 2); // 29 (leap)
DateTimeUtils.monthDayCountSafe(year: 2023, month: 2); // 28
DateTimeUtils.monthDayCountSafe(year: null, month: 2); // 28 (year unknown)
DateTimeUtils.monthDayCountSafe(year: null, month: 1); // 31
DateTimeUtils.monthDayCountSafe(year: 2024, month: 13); // 30 (no throw)
Audited: 2026-06-12 11:26 EDT
Implementation
@useResult
static int monthDayCountSafe({required int? year, required int month}) {
// February is the only month whose length depends on the year, so it is
// the only branch that consults the leap-year rule — and only when the
// year is known. An unknown year cannot resolve leap-ness, so it falls
// back to the always-safe 28 (the minimum February length).
if (month == 2) {
if (year != null && isLeapYear(year: year)) {
return DateConstants.daysInFebLeapYear;
}
return DateConstants.minDaysInAnyMonth;
}
// Non-February: a membership test (not a table index) keeps the lookup
// total over all ints, so out-of-range months coerce to 30 instead of
// throwing — the deliberate no-throw contract.
return _thirtyOneDayMonths.contains(month)
? DateConstants.daysInThirtyOneDayMonth
: DateConstants.daysInThirtyDayMonth;
}