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

  1. Use a valid IANA timezone identifier: 'America/New_York', 'Europe/London', 'Australia/Sydney', 'UTC'.
  2. Validate the timezone at the source: Intl.supportedValuesOf('timeZone') (modern browsers) or a known list before persisting it.
  3. If the timezone comes from Ghost settings, ensure the settings UI only offers IANA identifiers.
  4. 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

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


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/ef4ab60cdab0d689. Report an issue: GitHub.