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
- Pass the time zone as a quoted literal: 'America/Los_Angeles' or '+08:00'
- If per-row zones are required, compute shifted timestamps in application code or pre-compute at ingestion
- 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
- Always pass time zones as quoted string literals
- Validate timezone ids with DateTimeZone.forID before building the query
- Avoid column references in time-function timezone arguments
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
- Function[ ] substring must be a string literal
- argument must be a LONG constant
- attempt to get boolean[] null vector from string[] only…
- attempt to get double[] from string[] only scalar binding
- attempt to get long[] from string[] only scalar binding
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)