diff --git a/web/src/views/Standups.vue b/web/src/views/Standups.vue index 6b34606..c0053b5 100644 --- a/web/src/views/Standups.vue +++ b/web/src/views/Standups.vue @@ -336,21 +336,49 @@ export default { // Treat as date-only value to avoid timezone shifts if (!dateString) return '' + // Helper function for fallback parsing + const fallbackParse = () => { + try { + const date = new Date(dateString) + if (isNaN(date.getTime())) return 'Invalid date' + return date.toLocaleDateString('en-US', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric' + }) + } catch (e) { + return 'Invalid date' + } + } + const dateOnly = dateString.includes('T') ? dateString.split('T')[0] : dateString const parts = dateOnly.split('-') if (parts.length !== 3) { // Fallback to standard parsing if format is unexpected - return new Date(dateString).toLocaleDateString('en-US', { - weekday: 'long', - year: 'numeric', - month: 'long', - day: 'numeric' - }) + return fallbackParse() } const [year, month, day] = parts.map(Number) - const date = new Date(year, month - 1, day) // Use local timezone with specific date parts + + // Validate that year, month, and day are finite numbers within expected ranges + if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day) || + year < 1000 || year > 9999 || month < 1 || month > 12 || day < 1 || day > 31) { + // Values are invalid, return error message + return 'Invalid date' + } + + // Construct date and validate it matches the input values + const date = new Date(year, month - 1, day) + if (isNaN(date.getTime()) || + date.getFullYear() !== year || + date.getMonth() !== month - 1 || + date.getDate() !== day) { + // Date rolled over (e.g., Feb 31 -> Mar 3), so it's invalid + return 'Invalid date' + } + return date.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric',