{"record":{"id":"b0f6c2215dd37630","repo":"koala73/worldmonitor","slug":"use-a-valid-date-from-1900-through-9998","errorCode":null,"errorMessage":"Use a valid date from 1900 through 9998.","messagePattern":"Use a valid date from 1900 through 9998\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/utils/operational-balance.ts","lineNumber":23,"sourceCode":"export const MAX_OPERATIONAL_JSON_BYTES = 65_536;\nconst DAY_MS = 86_400_000;\nconst round = (value: number) => Math.round(value * 1e6) / 1e6;\nconst dateAt = (start: string, offset: number) => new Date(Date.parse(start) + offset * DAY_MS).toISOString().slice(0, 10);\n\nfunction record(value: unknown): Record<string, unknown> {\n  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Expected a worksheet object.');\n  return value as Record<string, unknown>;\n}\nfunction quantity(value: unknown, label: string): number {\n  if (value === '' || value === null || value === undefined) throw new Error(`${label} is required.`);\n  if (typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 1e6 || round(value) !== value) {\n    throw new Error(`${label} must be between 0 and 1 million with at most 6 decimal places.`);\n  }\n  return value;\n}\nfunction date(value: unknown): string {\n  if (typeof value !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/.test(value) || !Number.isFinite(Date.parse(value)) || new Date(value).toISOString().slice(0, 10) !== value || value < '1900-01-01' || value > '9998-12-31') {\n    throw new Error('Use a valid date from 1900 through 9998.');\n  }\n  return value;\n}\n\nexport function parseOperationalInput(value: unknown): OperationalInput {\n  const input = record(value);\n  if (typeof input.operation !== 'string' || !input.operation.trim() || input.operation.length > 100) throw new Error('Enter an operation name of 1-100 characters.');\n  if (typeof input.unit !== 'string' || !/^[\\p{L}\\p{N} %./-]{1,24}$/u.test(input.unit) || !input.unit.trim()) throw new Error('Enter one quantity unit of 1-24 characters.');\n  const unit = input.unit.trim();\n  const startDate = date(input.startDate);\n  if (!Number.isInteger(input.horizonDays) || (input.horizonDays as number) < 1 || (input.horizonDays as number) > MAX_OPERATIONAL_DAYS) throw new Error('Horizon must be 1-90 whole days.');\n  const horizonDays = input.horizonDays as number;\n  if (input.basis !== 'example' && input.basis !== 'user') throw new Error('Input basis must be example or user.');\n  const deliveries = (value: unknown): OperationalDelivery[] => {\n    if (!Array.isArray(value) || value.length > MAX_OPERATIONAL_DELIVERIES) throw new Error('Use at most 30 deliveries per list.');\n    return value.map(value => {\n      const row = record(value);\n      const deliveryDate = date(row.date);","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/src/utils/operational-balance.ts#L5-L41","documentation":"The date() helper in operational-balance.ts validates startDate/deliveryDate as strict ISO calendar dates: the string must match /^\\d{4}-\\d{2}-\\d{2}$/, parse as a finite Date, round-trip through toISOString() unchanged, and fall between 1900-01-01 and 9998-12-31. Any other value throws 'Use a valid date from 1900 through 9998.'","triggerScenarios":"Passing '2026-13-40' (invalid month/day), '2026-1-1' (no zero padding), a full timestamp '2026-01-01T00:00:00Z', '01/01/2026', or a Date object instead of a 'YYYY-MM-DD' string.","commonSituations":"US-format dates from locale pickers; datetime-local inputs submitted with a time component; date inputs bound to Date objects rather than strings; epoch timestamps passed as numbers.","solutions":["Convert to a canonical calendar string: new Date(v).toISOString().slice(0, 10), ensuring the local date maps correctly.","Use <input type=\"date\"> whose value is already 'YYYY-MM-DD'.","Pre-validate with the same regex and range check before calling the parser."],"exampleFix":"// before\nparseOperationalInput({ ..., startDate: '01/05/2026' });\n// after\nparseOperationalInput({ ..., startDate: '2026-01-05' });","handlingStrategy":"validation","validationCode":"const ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}$/;\nfunction isValidWorksheetDate(v: unknown): v is string {\n  return typeof v === 'string' && ISO_DATE.test(v) &&\n    Number.isFinite(Date.parse(v)) &&\n    new Date(v).toISOString().slice(0, 10) === v &&\n    v >= '1900-01-01' && v <= '9998-12-31';\n}","typeGuard":"const isIsoCalendarDate = (v: unknown): v is string =>\n  typeof v === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(v) &&\n  Number.isFinite(Date.parse(v)) && new Date(v).toISOString().slice(0, 10) === v;","tryCatchPattern":"try {\n  const input = parseOperationalInput(raw);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('valid date from 1900')) {\n    console.error('Date must be a plain YYYY-MM-DD calendar string:', e.message);\n  } else throw e;\n}","preventionTips":["Use <input type=\"date\"> whose value is already YYYY-MM-DD.","Strip time components: new Date(v).toISOString().slice(0, 10).","Never pass Date objects, timestamps, or locale-formatted strings to the parser.","Pre-validate dates with the regex plus range check before submission."],"tags":["validation","date","format"],"backgroundTag":"invalid-date-format","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}