cube-js/cube · error · Error

Unknown timezone: ${timezone}

Error message

Unknown timezone: ${timezone}

What it means

localTimestampToUtc in packages/cubejs-backend-shared/src/time.ts converts a 23- or 26-character local timestamp to UTC using the IANA timezone's offset via moment.tz.zone(timezone). If the timezone name is not a valid IANA zone (moment returns no zone object), it throws 'Unknown timezone'. This prevents silently applying UTC offsets of 0 for unrecognized zones.

Source

Thrown at packages/cubejs-backend-shared/src/time.ts:277

export const FROM_PARTITION_RANGE = '__FROM_PARTITION_RANGE';

export const TO_PARTITION_RANGE = '__TO_PARTITION_RANGE';

export const BUILD_RANGE_START_LOCAL = '__BUILD_RANGE_START_LOCAL';

export const BUILD_RANGE_END_LOCAL = '__BUILD_RANGE_END_LOCAL';

/**
 * Takes timestamp, treat it as time in provided timezone and returns the corresponding timestamp in UTC
 */
export const localTimestampToUtc = (timezone: string, timestampFormat: string, timestamp?: string): string | null => {
  if (!timestamp) {
    return null;
  }
  if (timestamp.length === 23 || timestamp.length === 26) {
    const zone = moment.tz.zone(timezone);
    if (!zone) {
      throw new Error(`Unknown timezone: ${timezone}`);
    }

    const parsedTime = Date.parse(`${timestamp}Z`);
    const offset = zone.utcOffset(parsedTime);
    const inDbTimeZoneDate = new Date(parsedTime + offset * 60 * 1000);

    if (timestampFormat === 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' || timestampFormat === 'YYYY-MM-DDTHH:mm:ss.SSSZ') {
      return inDbTimeZoneDate.toJSON();
    } else if (timestampFormat === 'YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]' || timestampFormat === 'YYYY-MM-DDTHH:mm:ss.SSSSSSZ') {
      const value = inDbTimeZoneDate.toJSON();
      if (value.endsWith('999Z')) {
        // emulate microseconds
        return value.replace('Z', '999Z');
      }

      // emulate microseconds
      return value.replace('Z', '000Z');
    } else if (timestampFormat === 'YYYY-MM-DDTHH:mm:ss.SSS') {

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Pass a valid IANA timezone name, e.g. 'America/New_York', 'Europe/Berlin', 'UTC'.
  2. Validate with moment.tz.zone(tz) (or Intl.supportedValuesOf('timeZone')) before calling.
  3. Convert Windows/CLDR zone names to IANA (e.g. via a CLDR-to-IANA mapping library) before use.
  4. Check casing/whitespace: normalize (trim, exact case) the configured timezone.

Example fix

// before
localTimestampToUtc(ts, 'Eastern Standard Time'); // throws

// after
localTimestampToUtc(ts, 'America/New_York');
Defensive patterns

Strategy: validation

Validate before calling

import moment from 'moment-timezone';
function assertTimezone(tz: string) {
  if (!moment.tz.zone(tz)) {
    throw new Error(`'${tz}' is not a valid IANA timezone; see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones`);
  }
}

Type guard

function isIanaTimezone(tz: unknown): tz is string {
  return typeof tz === 'string' && tz.length > 0 && moment.tz.zone(tz) !== null;
}

Try / catch

try {
  return localTimestampToUtc(ts, tz);
} catch (e) {
  if (/Unknown timezone/.test(String(e))) {
    console.warn(`Unknown timezone '${tz}', falling back to UTC`);
    return localTimestampToUtc(ts, 'UTC');
  }
  throw e;
}

Prevention

When it happens

Trigger: localTimestampToUtc('2024-01-01T00:00:00.000', 'America/NewYork') or any call where timezone is misspelled, a fixed-offset string like 'UTC+2', an empty string, or a Windows zone name like 'Eastern Standard Time' — with a timestamp of length 23 or 26.

Common situations: Timezone read from user config/browser with wrong casing or typos; Windows (CLDR) timezone names instead of IANA names; environment defaults like 'local' or 'system' passed through.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/e5d46594eba86112. Report an issue: GitHub.