{"record":{"id":"55e29c9e72da0522","repo":"badges/shields","slug":"invalid-date","errorCode":null,"errorMessage":"invalid date","messagePattern":"invalid date","errorType":"exception","errorClass":"InvalidResponse","httpStatus":null,"severity":"error","filePath":"services/date.js","lineNumber":42,"sourceCode":" * @throws {InvalidResponse} - Error if validation fails\n * @see https://day.js.org/docs/en/parse/string\n * @see https://day.js.org/docs/en/parse/string-format\n * @see https://day.js.org/docs/en/parse/is-valid\n * @example\n * parseDate('2024-01-01')\n * parseDate('31/01/2024', 'DD/MM/YYYY')\n * parseDate('2018 Enero 15', 'YYYY MMMM DD', 'es')\n */\nfunction parseDate(...args) {\n  let date\n  if (args.length >= 2) {\n    // always use strict mode if format arg is supplied\n    date = dayjs(...args, true)\n  } else {\n    date = dayjs(...args)\n  }\n  if (!date.isValid()) {\n    throw new InvalidResponse({ prettyMessage: 'invalid date' })\n  }\n  return date\n}\n\n/**\n * Returns a formatted date string without the year based on the value of input date param d.\n *\n * @param {Date | string | number | dayjs } d JS Date object, string, unix timestamp or dayjs object\n * @returns {string} Formatted date string\n */\nfunction formatDate(d) {\n  const date = parseDate(d)\n  const dateString = date.calendar(null, {\n    lastDay: '[yesterday]',\n    sameDay: '[today]',\n    lastWeek: '[last] dddd',\n    sameElse: 'MMMM YYYY',\n  })","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/badges/shields/blob/766fd8bc89a90b8534dc573ab72dec30215ab1ec/services/date.js#L24-L60","documentation":"The shared date utility throws InvalidResponse with prettyMessage 'invalid date' in parseDate when dayjs cannot parse the supplied input into a valid date. It is thrown for any badge service that passes an unparsable date string (or a date failing the strict format check) into dayjs helpers.","triggerScenarios":"An upstream API returns a date string in an unexpected format, an empty string, null, or a string not matching the supplied strict format, so dayjs(...).isValid() is false.","commonSituations":"Upstream changed their timestamp format (e.g. epoch seconds vs ISO 8601); localized date formats; null last_updated fields for brand-new resources; passing epoch numbers as strings.","solutions":["Log the raw upstream value to see the actual format being passed to parseDate","Parse epoch values with dayjs.unix/Number conversion before passing the string to the badge service","If using the format argument, ensure the string exactly matches the strict format tokens","Handle the new upstream format before calling parseDate if the upstream format changed"],"exampleFix":"// before\nparseDate(String(createdAtSeconds))  // invalid date\n// after\nparseDate(new Date(Number(createdAtSeconds) * 1000).toISOString())","handlingStrategy":"validation","validationCode":"function isParsableDate(v) {\n  if (v == null || v === '') return false\n  const d = dayjs(v)\n  return d.isValid()\n}","typeGuard":"function isValidDateString(v) {\n  return typeof v === 'string' && v.length > 0 && !Number.isNaN(Date.parse(v))\n}","tryCatchPattern":"try {\n  const d = parseDate(input)\n} catch (err) {\n  if (err.prettyMessage === 'invalid date') {\n    d = null // render fallback badge\n  } else throw err\n}","preventionTips":["Inspect raw upstream values and normalize (epoch -> ISO) before passing to date helpers","Use strict format only when the input format is guaranteed","Guard against null/empty last_updated fields from upstream APIs","Watch for upstream timestamp format changes and add tests around parsing"],"tags":["date","dayjs","invalid-input","parsing"],"backgroundTag":"invalid-date-format","analyzedSha":"766fd8bc89a90b8534dc573ab72dec30215ab1ec","analyzedAt":"2026-08-30T01:40:27.499Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}