{"record":{"id":"841babf37b95e31a","repo":"can1357/oh-my-pi","slug":"invalid-iso-datetime-empty-string","errorCode":null,"errorMessage":"Invalid ISO datetime: empty string","messagePattern":"Invalid ISO datetime: empty string","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"warning","filePath":"packages/mnemopi/src/util/datetime.ts","lineNumber":12,"sourceCode":"import { recencyHalflifeHours } from \"../config\";\nimport { LruCache } from \"./lru\";\n\nconst TZ_RE = /(?:Z|[+-]\\d\\d:?\\d\\d)$/;\nconst DATE_ONLY_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst TS_CACHE = new LruCache<string, Date>(2000);\n\nexport type QueryTime = string | Date | null | undefined;\n\nexport function parseIsoDateTimeUtc(value: string): Date {\n\tlet text = value.trim();\n\tif (!text) throw new RangeError(\"Invalid ISO datetime: empty string\");\n\tif (DATE_ONLY_RE.test(text)) text += \"T00:00:00Z\";\n\telse if (!TZ_RE.test(text)) text += \"Z\";\n\tconst date = new Date(text);\n\tif (Number.isNaN(date.getTime())) throw new RangeError(`Invalid ISO datetime: ${value}`);\n\treturn date;\n}\n\nexport function normalizeDateTimeUtc(value: Date): Date {\n\tconst time = value.getTime();\n\tif (Number.isNaN(time)) throw new RangeError(\"Invalid Date\");\n\treturn new Date(time);\n}\n\nexport function parseQueryTime(value: QueryTime): Date {\n\tif (value === null || value === undefined) return new Date();\n\treturn typeof value === \"string\" ? parseIsoDateTimeUtc(value) : normalizeDateTimeUtc(value);\n}\n","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/mnemopi/src/util/datetime.ts#L1-L30","documentation":"parseIsoDateTimeUtc() normalizes ISO datetime strings to UTC dates, appending T00:00:00Z for date-only values and Z when no timezone is present. An empty (or whitespace-only) string has no parseable datetime, so it throws a RangeError with a dedicated message distinct from the generic invalid-format error.","triggerScenarios":"Calling parseIsoDateTimeUtc('') or parseIsoDateTimeUtc('   ') — directly, or via parseQueryTime('') when a query/filter parameter was provided but empty.","commonSituations":"Empty query-string parameters (since=), empty env vars or config fields interpolated into time filters, API clients sending \"\" instead of omitting the field, or .trim() results from blank user input.","solutions":["Omit the field entirely instead of sending an empty string so parseQueryTime falls back to now.","Coerce empty/whitespace values to undefined/null before parsing: value?.trim() ? value : undefined.","Validate user/config input at the boundary and reject empty datetimes with a clear client-side message.","If an empty value should mean 'beginning of time', substitute a sentinel date explicitly."],"exampleFix":"// before\nconst since = parseQueryTime(searchParams.get('since')); // '' -> RangeError\n// after\nconst raw = searchParams.get('since');\nconst since = parseQueryTime(raw && raw.trim() ? raw : null); // null -> now","handlingStrategy":"validation","validationCode":"function nonEmptyIsoOrNull(value) {\n  if (typeof value !== 'string') return null;\n  const trimmed = value.trim();\n  return trimmed.length > 0 ? trimmed : null; // null -> parseQueryTime defaults to now\n}\nconst since = parseQueryTime(nonEmptyIsoOrNull(req.query.since));","typeGuard":"function isNonEmptyIsoString(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  const t = parseIsoDateTimeUtc(raw);\n} catch (err) {\n  if (err instanceof RangeError && err.message.includes('empty string')) {\n    return new Date(); // treat blank input as 'now' (parseQueryTime default)\n  }\n  throw err;\n}","preventionTips":["Coerce empty/whitespace query params to undefined before parsing","Reject empty datetime fields at the API boundary with a clear 400 message","Distinguish 'absent' (omit field) from 'empty string' in client code","Trim user input before passing to datetime parsers"],"tags":["datetime","iso8601","validation","range-error"],"backgroundTag":"invalid-iso-datetime","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}