apache/druid · error · IllegalArgumentException

Time zone must be a literal

Error message

Time zone must be a literal

What it means

ExprUtils.toTimeZone() converts a time-zone argument expression (used by functions like timestamp_shift, time_floor, time_parse) into a DateTimeZone. The argument must be a literal (constant string or null); a dynamic expression such as a column reference cannot be resolved at planning time, so Druid throws this IAE.

Solutions

  1. Pass the time zone as a quoted literal: 'America/Los_Angeles' or '+08:00'
  2. If per-row zones are required, compute shifted timestamps in application code or pre-compute at ingestion
  3. Use NULL literal explicitly if UTC is desired

Example fix

// before
TIME_FLOOR(__time, 'P1D', NULL, tz_col)
// after
TIME_FLOOR(__time, 'P1D', NULL, 'America/Los_Angeles')
Defensive patterns

Strategy: validation

Validate before calling

if (tzArg != null && !tzArg.matches("^([A-Za-z/_+-]+|[+-]\d\d:?\d\d)$")) {
  throw new IllegalArgumentException("timezone must be a literal id or offset");
}

Type guard

boolean isTzLiteral = expr != null && expr.isLiteral() && (expr.getLiteralValue() == null || expr.getLiteralValue() instanceof String);

Try / catch

try {
  expr = "timestamp_shift(__time, 'P1D', 1, 'UTC')";
} catch (IAE e) {
  if (e.getMessage().equals("Time zone must be a literal")) { /* inline the tz value */ }
}

Prevention

When it happens

Trigger: Passing a non-literal (column, arithmetic expression, nested function) as the timezone argument of a time function, e.g. TIME_FLOOR(ts, 'P1D', NULL, tzColumn).

Common situations: Trying to make the time zone configurable per-row; forgetting quotes so a timezone string is parsed as an identifier/expression.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/9e363e137b1e05ca. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/query/expression/ExprUtils.java:40

import org.apache.druid.error.InvalidInput;
import org.apache.druid.java.util.common.DateTimes;
import org.apache.druid.java.util.common.IAE;
import org.apache.druid.java.util.common.granularity.PeriodGranularity;
import org.apache.druid.math.expr.Expr;
import org.joda.time.Chronology;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Period;
import org.joda.time.chrono.ISOChronology;

import javax.annotation.Nullable;

public class ExprUtils
{
  static DateTimeZone toTimeZone(final Expr timeZoneArg)
  {
    if (!timeZoneArg.isLiteral()) {
      throw new IAE("Time zone must be a literal");
    }

    final Object literalValue = timeZoneArg.getLiteralValue();
    return literalValue == null ? DateTimeZone.UTC : DateTimes.inferTzFromString((String) literalValue);
  }

  static PeriodGranularity toPeriodGranularity(
      final Expr wrappingExpr,
      final Expr periodArg,
      @Nullable final Expr originArg,
      @Nullable final Expr timeZoneArg,
      final Expr.ObjectBinding bindings
  )
  {
    final Period period;
    try {
      period = new Period(periodArg.eval(bindings).asString());
    }

View on GitHub (pinned to 9b90983fd2)