{"record":{"id":"5fc01b5a49e7ba74","repo":"jackwener/OpenCLI","slug":"expiration-must-use-yyyy-mm-dd-format","errorCode":null,"errorMessage":"--expiration must use YYYY-MM-DD format","messagePattern":"--expiration must use YYYY-MM-DD format","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/barchart/greeks.js","lineNumber":23,"sourceCode":" */\nimport { cli, Strategy } from '@jackwener/opencli/registry';\nimport { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';\n\nconst DEFAULT_LIMIT = 10;\nconst MIN_LIMIT = 1;\nconst MAX_LIMIT = 100;\n\nfunction normalizeSymbol(value) {\n    const symbol = String(value ?? '').trim().toUpperCase();\n    if (!symbol) throw new ArgumentError('symbol is required');\n    return symbol;\n}\n\nfunction normalizeExpiration(value) {\n    const expiration = String(value ?? '').trim();\n    if (!expiration) return '';\n    if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(expiration)) {\n        throw new ArgumentError('--expiration must use YYYY-MM-DD format');\n    }\n    const parsed = new Date(`${expiration}T00:00:00Z`);\n    if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== expiration) {\n        throw new ArgumentError('--expiration must be a valid calendar date');\n    }\n    return expiration;\n}\n\nfunction parseLimit(value) {\n    if (value === undefined || value === null || value === '') return DEFAULT_LIMIT;\n    const limit = Number(value);\n    if (!Number.isInteger(limit) || limit < MIN_LIMIT || limit > MAX_LIMIT) {\n        throw new ArgumentError(`--limit must be an integer between ${MIN_LIMIT} and ${MAX_LIMIT}`);\n    }\n    return limit;\n}\n\nfunction unwrapBrowserResult(value) {","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/barchart/greeks.js#L5-L41","documentation":"normalizeExpiration() accepts an optional --expiration value but enforces strict YYYY-MM-DD formatting when one is provided. Any string that does not match the /^\\d{4}-\\d{2}-\\d{2}$/ pattern throws this ArgumentError. It is a format-only check; the calendar-validity check is a separate error.","triggerScenarios":"Passing --expiration values like 12/20/2025, 2025-12-20T00:00:00, 20251220, 25-12-20, or any other string not exactly four digits, dash, two digits, dash, two digits.","commonSituations":"Using US-style MM/DD/YYYY dates from spreadsheets; copy-pasting a full ISO timestamp including time; locale-formatted dates from scripts; passing an expiration with surrounding units or labels like '2025-12-20T'.","solutions":["Reformat the value to YYYY-MM-DD, e.g. --expiration 2025-12-20.","In scripts, convert locale-formatted dates with a formatting step (e.g. date.toISOString().slice(0,10)) before passing them.","Quote the argument in the shell so dashes/spaces are not mangled."],"exampleFix":"// before\n--expiration 12/20/2025\n// after\n--expiration 2025-12-20","handlingStrategy":"validation","validationCode":"function toIsoDate(d) {\n  const date = d instanceof Date ? d : new Date(d);\n  if (Number.isNaN(date.getTime())) throw new Error(`Invalid date: ${d}`);\n  return date.toISOString().slice(0, 10); // always YYYY-MM-DD\n}\n// call: toIsoDate('12/20/2025') => '2025-12-20'","typeGuard":"function isIsoDateString(v) {\n  return typeof v === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(v);\n}","tryCatchPattern":"try {\n  await greeks({ symbol, expiration });\n} catch (e) {\n  if (e.name === 'ArgumentError' && e.message.includes('YYYY-MM-DD')) {\n    console.error(`--expiration must be YYYY-MM-DD, got: ${expiration}`);\n    process.exitCode = 2;\n    return;\n  }\n  throw e;\n}","preventionTips":["Normalize all dates to YYYY-MM-DD via toISOString().slice(0,10) before passing","Never pass locale-formatted (MM/DD/YYYY) dates directly","Quote date arguments in shell scripts"],"tags":["validation","argument-error","date-format","cli"],"backgroundTag":"invalid-date-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}