TryGhost/Ghost · error · Error
Invalid timezone: ${timezone}
Error message
Invalid timezone: ${timezone} What it means
getDayBoundsInUtc converts a valid PlainDate into UTC instants by combining it with a timezone string via toZonedDateTime(timezone). If the timezone string is not a valid IANA timezone identifier (e.g. 'America/New_York') or a fixed offset, Temporal throws and the catch re-throws with the offending timezone value. This is separate from the date validity check so the two failure modes are distinguishable.
Source
Thrown at apps/admin/src/shared/filters/filter-normalization.ts:44
export function getDayBoundsInUtc(date: string, timezone: string): {start: string; end: string} {
let plainDate: Temporal.PlainDate;
try {
plainDate = Temporal.PlainDate.from(date);
} catch {
throw new Error(`Invalid filter date: ${date}`);
}
try {
const start = plainDate.toPlainDateTime(Temporal.PlainTime.from('00:00:00')).toZonedDateTime(timezone).toInstant();
const end = plainDate.toPlainDateTime(Temporal.PlainTime.from('23:59:59.999')).toZonedDateTime(timezone).toInstant();
return {
start: start.toString({fractionalSecondDigits: 3}),
end: end.toString({fractionalSecondDigits: 3})
};
} catch {
throw new Error(`Invalid timezone: ${timezone}`);
}
}
View on GitHub (pinned to 47d8b0e2ad)
Solutions
- Use a valid IANA timezone identifier: 'America/New_York', 'Europe/London', 'Australia/Sydney', 'UTC'.
- Validate the timezone at the source: Intl.supportedValuesOf('timeZone') (modern browsers) or a known list before persisting it.
- If the timezone comes from Ghost settings, ensure the settings UI only offers IANA identifiers.
- Default to 'UTC' when the configured timezone is missing or suspect, with a logged warning.
Example fix
// before (throws — abbreviation)
getDayBoundsInUtc('2024-01-05', 'EST')
// after
getDayBoundsInUtc('2024-01-05', 'America/New_York') Defensive patterns
Strategy: validation
Validate before calling
function isValidTimezone(tz: string): boolean {
try {
Temporal.TimeZone.from(tz);
return true;
} catch {
return false;
}
}
// or use Intl: Intl.supportedValuesOf?.('timeZone').includes(tz) in modern browsers Type guard
function isIanaTimezone(value: unknown): value is string {
if (typeof value !== 'string') return false;
try {
Temporal.TimeZone.from(value);
return true;
} catch {
return false;
}
} Try / catch
try {
const bounds = getDayBoundsInUtc(date, timezone);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Invalid timezone')) {
// fall back to UTC and log
return getDayBoundsInUtc(date, 'UTC');
}
throw e;
} Prevention
- Store and accept only IANA timezone identifiers (America/New_York, Europe/London, UTC).
- Validate timezone at the settings form using Intl.supportedValuesOf('timeZone') or a known list.
- Default to 'UTC' with a warning when the configured timezone fails validation.
When it happens
Trigger: Calling getDayBoundsInUtc with a timezone argument that is not a valid IANA timezone name. Examples: 'America/NY' (abbreviation, not full name), 'EST' (abbreviation), 'UTC+5' (wrong format — should be 'Etc/GMT-5' or offset syntax), 'Eastern Time', an empty string, or a typo like 'America/New_Yrok'.
Common situations: The site/publication timezone is misconfigured in Ghost settings and stored as an abbreviation or display name instead of an IANA identifier. A user-entered timezone from a custom integration is not validated. A DST abbreviation like 'PDT' is passed where 'America/Los_Angeles' is required.
Related errors
- Invalid filter date: ${date}
- Your Username is not a valid Bluesky Username
- The URL must be in a format like https://www.threads.net/@yo
- The NQL filter you passed was invalid.
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/ef4ab60cdab0d689.
Report an issue: GitHub.