jackwener/OpenCLI · error · ArgumentError
--${name} is not a real calendar date: ${value}
Error message
--${name} is not a real calendar date: ${value} What it means
parseIsoDate performs a final UTC round-trip check: it constructs Date.UTC(year, month-1, day) and verifies the components round-trip exactly. This catches pattern-valid, range-valid but nonexistent calendar dates such as 2026-02-30 or 2025-02-29 (non-leap year), which would otherwise silently roll over to March.
Source
Thrown at clis/ctrip/utils.js:226
export function parseIsoDate(name, raw) {
if (raw === undefined || raw === null || raw === '' || String(raw).trim() === '') {
throw new ArgumentError(`--${name} is required (YYYY-MM-DD)`);
}
const value = String(raw);
const m = ISO_DATE_RE.exec(value);
if (!m) {
throw new ArgumentError(`--${name} must be YYYY-MM-DD, got ${JSON.stringify(raw)}`);
}
const year = Number(m[1]);
const month = Number(m[2]);
const day = Number(m[3]);
if (month < 1 || month > 12 || day < 1 || day > 31) {
throw new ArgumentError(`--${name} has invalid month/day: ${value}`);
}
// Cross-check via UTC date math so 2026-02-30 doesn't pass.
const parsed = new Date(Date.UTC(year, month - 1, day));
if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== month - 1 || parsed.getUTCDate() !== day) {
throw new ArgumentError(`--${name} is not a real calendar date: ${value}`);
}
return value;
}
/**
* Validate a 3-letter IATA airport / metro code, return uppercase.
* Ctrip URL accepts both single-airport (PEK / PVG) and metro-group (BJS / SHA) codes.
*/
export function parseIataCode(name, raw) {
if (raw === undefined || raw === null || raw === '') {
throw new ArgumentError(`--${name} is required (3-letter IATA code, e.g. PEK, SHA)`);
}
const value = String(raw).trim().toUpperCase();
if (!/^[A-Z]{3}$/.test(value)) {
throw new ArgumentError(`--${name} must be a 3-letter IATA code, got ${JSON.stringify(raw)}`);
}
return value;
}View on GitHub (pinned to 49907e53dc)
Solutions
- Pick a date that exists in the calendar (2026-02 has 28 days; 2025 is not a leap year)
- Use a date library (e.g. Temporal.PlainDate or dayjs with strict parsing) to validate before calling
- Compute dates via Date arithmetic rather than hand-assembling strings
- Clamp day to the last day of the month when generating dates
Example fix
// before --checkin 2026-02-30 // after --checkin 2026-02-28
Defensive patterns
Strategy: validation
Validate before calling
function isRealDate(y, m, d) {
const dt = new Date(Date.UTC(y, m - 1, d));
return dt.getUTCFullYear() === y && dt.getUTCMonth() === m - 1 && dt.getUTCDate() === d;
}
if (!isRealDate(2026, 2, 30)) throw new Error('not a real calendar date'); Type guard
function isRealIsoDate(v) {
if (!/^\d{4}-\d{2}-\d{2}$/.test(v)) return false;
const [y, m, d] = v.split('-').map(Number);
const dt = new Date(Date.UTC(y, m - 1, d));
return dt.getUTCFullYear() === y && dt.getUTCMonth() === m - 1 && dt.getUTCDate() === d;
} Try / catch
try {
const date = parseIsoDate('checkout', raw);
} catch (err) {
if (err instanceof ArgumentError && /not a real calendar date/.test(err.message)) {
// clamp to last valid day of the month
}
throw err;
} Prevention
- Verify leap years before using Feb 29 (2025 no, 2028 yes)
- Clamp day to the month's last day when computing dates
- Use strict date parsing (Temporal.PlainDate, dayjs strict mode)
- Never hand-assemble end-of-month dates
When it happens
Trigger: Calling parseIsoDate(name, raw) with impossible dates like 2026-02-30, 2025-02-29, 2026-04-31, or 2026-06-31 — regex and range checks pass but Date.UTC normalization shifts the date.
Common situations: Hand-typed end-of-month dates (31st typed for a 30-day month); leap-year assumption errors; naive date arithmetic that overflowed a month boundary.
Related errors
- archive snapshots ${key} must be a digit-only timestamp (YYY
- ${label} is required (YYYY-MM-DD)
- --${name} must be YYYY-MM-DD, got ${JSON.stringify(raw)}
- --${name} has invalid month/day: ${value}
- huodongxing ${label} must be a valid YYYY-MM-DD date
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/1fcbb1234c468bb9.
Report an issue: GitHub.