{"record":{"id":"2c6301086e65b699","repo":"can1357/oh-my-pi","slug":"format-string-contains-an-unescaped-latin-alphabet","errorCode":null,"errorMessage":"Format string contains an unescaped latin alphabet character `${character}`","messagePattern":"Format string contains an unescaped latin alphabet character `(.+?)`","errorType":"exception","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/dates.ts","lineNumber":138,"sourceCode":"\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tindex++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst rest = pattern.slice(index);\n\t\tconst token = TOKENS.find(candidate => rest.startsWith(candidate));\n\t\tif (token) {\n\t\t\tresult += tokenValue(date, token);\n\t\t\tindex += token.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst character = pattern[index++];\n\t\tif (/[A-Za-z]/.test(character)) {\n\t\t\tthrow new RangeError(`Format string contains an unescaped latin alphabet character \\`${character}\\``);\n\t\t}\n\t\tresult += character;\n\t}\n\n\treturn result;\n}\n\nfunction plural(count: number, singular: string): string {\n\treturn `${count} ${count === 1 ? singular : `${singular}s`}`;\n}\n\nfunction completedMonths(earlier: Date, later: Date): number {\n\tlet months = (later.getFullYear() - earlier.getFullYear()) * 12 + later.getMonth() - earlier.getMonth();\n\tif (months === 0) return 0;\n\n\tconst candidate = new Date(earlier);\n\tcandidate.setDate(1);\n\tcandidate.setFullYear(earlier.getFullYear(), earlier.getMonth() + months, 1);","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/dates.ts#L120-L156","documentation":"The custom date-fns-v4-compatible formatter throws RangeError when the pattern contains an unescaped latin letter it does not recognize as a supported token. date-fns semantics require unknown letters to be quoted with single quotes so they are literal text.","triggerScenarios":"Calling format(date, pattern) with a pattern containing unsupported bare letters, e.g. 'yyyy-MM-dd (UTC)' where 'U','T','C' are unquoted, or patterns using tokens from other libraries (e.g. 'DD', 'hh a' variants) not in the supported set.","commonSituations":"Porting patterns from Moment.js/Luxon/day.js, embedding words like 'at' or 'UTC' directly in the pattern, or copying date-fns patterns that use tokens this shim does not implement.","solutions":["Quote literal letters with single quotes: 'yyyy-MM-dd \"(UTC)\"' becomes \"yyyy-MM-dd '(UTC)'\" in date-fns style.","Replace unsupported tokens with supported v4 tokens (see the formatter's token table).","Wrap whole words in quotes: \"yyyy 'at' HH:mm\".","Escape a literal single quote by doubling it inside a quoted section."],"exampleFix":"// before\nformat(d, \"yyyy-MM-dd (UTC)\"); // RangeError: unescaped `U`\n// after\nformat(d, \"yyyy-MM-dd '(UTC)'\");","handlingStrategy":"validation","validationCode":"// reject unescaped latin letters outside quoted sections before formatting\nif (/(^|')([^']*)[A-Za-z]/.test(pattern.replace(/'[^']*'/g, \"\"))) {\n  throw new Error('Pattern contains unescaped letters; quote literals with single quotes');\n}","typeGuard":null,"tryCatchPattern":"try {\n  return format(date, pattern);\n} catch (err) {\n  if (err instanceof RangeError && err.message.includes('unescaped latin alphabet')) {\n    console.error(`Bad format string \"${pattern}\": ${err.message}`);\n    return '';\n  }\n  throw err;\n}","preventionTips":["Wrap literal text (words like 'at', 'UTC') in single quotes inside the pattern.","Only use tokens documented as supported (date-fns v4 set) when porting from other libraries.","Keep a small test over each production format string."],"tags":["dates","format-string","validation"],"backgroundTag":"invalid-date-format-pattern","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}