{"record":{"id":"7d8e322ee9149ac9","repo":"RocketChat/Rocket.Chat","slug":"invalid-iso-8601-date","errorCode":null,"errorMessage":"invalid ISO 8601 date","messagePattern":"invalid ISO 8601 date","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/ee/server/lib/engagementDashboard/date.ts","lineNumber":14,"sourceCode":"import mem from 'mem';\nimport moment from 'moment';\n\nexport const isDateISOString = mem(\n\t(input: string): input is string => {\n\t\tconst timestamp = Date.parse(input);\n\t\treturn !Number.isNaN(timestamp) && new Date(timestamp).toISOString() === input;\n\t},\n\t{ maxAge: 10000 },\n);\n\nexport const mapDateForAPI = (input: string): Date => {\n\tif (!isDateISOString(input)) {\n\t\tthrow new Error('invalid ISO 8601 date');\n\t}\n\n\treturn new Date(Date.parse(input));\n};\n\nexport const convertDateToInt = (date: Date): number => parseInt(moment(date).clone().format('YYYYMMDD'), 10);\nexport const convertIntToDate = (intValue: number): Date => moment(intValue, 'YYYYMMDD').clone().toDate();\nconst diffBetweenDays = (start: string | number | Date, end: string | number | Date): number =>\n\tmoment(new Date(start)).clone().diff(new Date(end), 'days');\nexport const diffBetweenDaysInclusive = (start: string | number | Date, end: string | number | Date): number =>\n\tdiffBetweenDays(start, end) + 1;\n\nexport const getTotalOfWeekItems = <T extends Record<string, number>>(weekItems: T[], property: keyof T): number =>\n\tweekItems.reduce((acc, item) => {\n\t\tacc += item[property];\n\t\treturn acc;\n\t}, 0);\n","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/lib/engagementDashboard/date.ts#L1-L32","documentation":"mapDateForAPI validates engagement-dashboard date parameters by round-trip: it parses the string with Date.parse and requires new Date(timestamp).toISOString() to equal the input exactly. Only the canonical UTC form with milliseconds and a Z suffix (e.g. 2024-01-01T00:00:00.000Z) passes. Date-only strings, offsets like +02:00, missing milliseconds, or trailing characters all throw 'invalid ISO 8601 date'.","triggerScenarios":"Calling an engagement dashboard endpoint (apps/meteor ee engagement dashboard handlers that use mapDateForAPI) with start/end params such as '2024-01-01' or '2024-01-01T00:00:00+00:00' instead of '2024-01-01T00:00:00.000Z'.","commonSituations":"Hand-built query strings; serializing dates with moment().format('YYYY-MM-DDTHH:mm:ss') without millisecond/Z precision; passing timestamps from systems that emit offset-based ISO strings.","solutions":["Send dates produced by new Date(...).toISOString(), which always yields UTC with .SSSZ","If using moment: moment(date).toISOString()","Validate input with the exported isDateISOString helper (or an equivalent round-trip check) before calling mapDateForAPI"],"exampleFix":"// before\nmapDateForAPI('2024-01-01T00:00:00+00:00'); // throws\n\n// after\nmapDateForAPI(new Date('2024-01-01T00:00:00+00:00').toISOString()); // '2024-01-01T00:00:00.000Z'","handlingStrategy":"type-guard","validationCode":"const start = new Date(Date.now() - 86400000).toISOString();\nconst end = new Date().toISOString();\nif (!isISODateString(start) || !isISODateString(end)) {\n\tthrow new TypeError('dates must be canonical ISO 8601 UTC strings');\n}\nconst range = { start: mapDateForAPI(start), end: mapDateForAPI(end) };","typeGuard":"const isISODateString = (input: string): input is string => {\n\tconst timestamp = Date.parse(input);\n\treturn !Number.isNaN(timestamp) && new Date(timestamp).toISOString() === input;\n};","tryCatchPattern":"try {\n\tconst from = mapDateForAPI(raw);\n} catch (err) {\n\tif (err instanceof Error && err.message === 'invalid ISO 8601 date') {\n\t\t// reject the request with a 400 and a hint to use toISOString()\n\t} else {\n\t\tthrow err;\n\t}\n}","preventionTips":["Always build API date params with new Date(...).toISOString(), never manual string formatting","Remember the round-trip check requires milliseconds and a Z suffix (UTC)","Centralize date serialization in one helper for all dashboard calls"],"tags":["date","iso8601","engagement-dashboard","validation"],"backgroundTag":"iso8601-format-invalid","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}