{"record":{"id":"e857f6094ba2d669","repo":"koala73/worldmonitor","slug":"quiethourstimezone-must-be-a-valid-iana-timezone","errorCode":null,"errorMessage":"quietHoursTimezone must be a valid IANA timezone (e.g. America/New_York)","messagePattern":"quietHoursTimezone must be a valid IANA timezone \\(e\\.g\\. America/New_York\\)","errorType":"validation","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/alertRules.ts","lineNumber":434,"sourceCode":"  quietHoursOverride: v.optional(quietHoursOverrideValidator),\n} as const;\n\nfunction validateQuietHoursArgs(args: {\n  quietHoursStart?: number;\n  quietHoursEnd?: number;\n  quietHoursTimezone?: string;\n}) {\n  if (args.quietHoursStart !== undefined && (args.quietHoursStart < 0 || args.quietHoursStart > 23 || !Number.isInteger(args.quietHoursStart))) {\n    throw new ConvexError(\"quietHoursStart must be an integer 0–23\");\n  }\n  if (args.quietHoursEnd !== undefined && (args.quietHoursEnd < 0 || args.quietHoursEnd > 23 || !Number.isInteger(args.quietHoursEnd))) {\n    throw new ConvexError(\"quietHoursEnd must be an integer 0–23\");\n  }\n  if (args.quietHoursTimezone !== undefined) {\n    try {\n      Intl.DateTimeFormat(undefined, { timeZone: args.quietHoursTimezone });\n    } catch {\n      throw new ConvexError(\"quietHoursTimezone must be a valid IANA timezone (e.g. America/New_York)\");\n    }\n  }\n}\n\nexport const setQuietHours = mutation({\n  args: QUIET_HOURS_ARGS,\n  handler: async (ctx, args) => {\n    const identity = await ctx.auth.getUserIdentity();\n    if (!identity) throw new ConvexError(\"UNAUTHENTICATED\");\n    const userId = identity.subject;\n    await assertProEntitlement(ctx, userId);\n    validateQuietHoursArgs(args);\n\n    const existing = await ctx.db\n      .query(\"alertRules\")\n      .withIndex(\"by_user_variant\", (q) =>\n        q.eq(\"userId\", userId).eq(\"variant\", args.variant),\n      )","sourceCodeStart":416,"sourceCodeEnd":452,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/convex/alertRules.ts#L416-L452","documentation":"The third check in validateQuietHoursArgs (convex/alertRules.ts:430): when quietHoursTimezone is supplied, the server constructs Intl.DateTimeFormat(undefined, { timeZone }) and any RangeError is rethrown as ConvexError 'quietHoursTimezone must be a valid IANA timezone (e.g. America/New_York)'. Only IANA zone identifiers are accepted — abbreviations and fixed offsets are not.","triggerScenarios":"setQuietHours (or setQuietHoursForUser) with quietHoursTimezone 'EST', 'CET', 'UTC+5:30', 'GMT-8', a typo'd or wrongly-cased zone name, or a Windows tz name like 'Pacific Standard Time'.","commonSituations":"Reusing a digest timezone string from another system that stores abbreviations; free-text timezone inputs; syncing from calendar apps that expose Windows zone names; storing offsets like '+02:00' from ISO timestamps.","solutions":["Send IANA identifiers ('America/Los_Angeles', 'Asia/Tokyo', 'UTC') or omit the field.","Default from the browser: Intl.DateTimeFormat().resolvedOptions().timeZone.","Validate with the same Intl.DateTimeFormat call client-side before submitting.","Map Windows timezone names to IANA when importing from Microsoft-ecosystem data."],"exampleFix":"// before\nawait mutateAPI.alertRules.setQuietHours({ variant: 'default', quietHoursEnabled: true, quietHoursStart: 22, quietHoursEnd: 7, quietHoursTimezone: 'PST' });\n// ConvexError: quietHoursTimezone must be a valid IANA timezone (e.g. America/New_York)\n\n// after\nawait mutateAPI.alertRules.setQuietHours({ variant: 'default', quietHoursEnabled: true, quietHoursStart: 22, quietHoursEnd: 7, quietHoursTimezone: 'America/Los_Angeles' });","handlingStrategy":"validation","validationCode":"function isValidTimezone(tz: string): boolean {\n  try {\n    Intl.DateTimeFormat(undefined, { timeZone: tz });\n    return true;\n  } catch {\n    return false;\n  }\n}\nif (quietHoursTimezone !== undefined && !isValidTimezone(quietHoursTimezone)) {\n  setFieldError('quietHoursTimezone', 'Use an IANA zone such as America/New_York');\n  return;\n}\nawait mutateAPI.alertRules.setQuietHours({ variant, quietHoursEnabled, quietHoursStart, quietHoursEnd, quietHoursTimezone });","typeGuard":"const isIanaTimezone = (tz: string): boolean => {\n  try {\n    Intl.DateTimeFormat(undefined, { timeZone: tz });\n    return true;\n  } catch {\n    return false;\n  }\n};","tryCatchPattern":"try {\n  await mutateAPI.alertRules.setQuietHours(args);\n} catch (err) {\n  if (err instanceof ConvexError && typeof err.data === 'string' && err.data.includes('quietHoursTimezone')) {\n    setFieldError('quietHoursTimezone', 'Use an IANA zone such as America/New_York');\n    return;\n  }\n  throw err;\n}","preventionTips":["Reuse the user's digest timezone (already IANA-validated) as the quiet-hours default.","Never accept timezone abbreviations or numeric offsets from users or imports.","Convert Windows zone names to IANA at the import boundary."],"tags":["convex","validation","timezone","iana","quiet-hours"],"backgroundTag":"invalid-timezone","analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}