prestodb/presto · error · IllegalArgumentException
Invalid time '%s'
Error message
Invalid time '%s'
What it means
DateTimeUtils.timeHasTimeZone parses a TIME literal to determine whether it includes a time-zone offset. When the value matches neither the time-with-tz nor the time-without-tz formatter, the underlying parse exception is replaced by an IllegalArgumentException formatting the bad value into "Invalid time '%s'".
Source
Thrown at presto-main-base/src/main/java/com/facebook/presto/util/DateTimeUtils.java:420
public static String printTimeWithoutTimeZone(TimeZoneKey timeZoneKey, long value)
{
return TIME_FORMATTER.withZone(getDateTimeZone(timeZoneKey)).print(value);
}
public static boolean timeHasTimeZone(String value)
{
try {
try {
parseTimeWithTimeZone(value);
return true;
}
catch (RuntimeException e) {
parseTimeWithoutTimeZone(value);
return false;
}
}
catch (RuntimeException e) {
throw new IllegalArgumentException(format("Invalid time '%s'", value));
}
}
private static final int YEAR_FIELD = 0;
private static final int MONTH_FIELD = 1;
private static final int DAY_FIELD = 3;
private static final int HOUR_FIELD = 4;
private static final int MINUTE_FIELD = 5;
private static final int SECOND_FIELD = 6;
private static final int MILLIS_FIELD = 7;
private static final PeriodFormatter INTERVAL_DAY_SECOND_FORMATTER = cretePeriodFormatter(IntervalField.DAY, IntervalField.SECOND);
private static final PeriodFormatter INTERVAL_DAY_MINUTE_FORMATTER = cretePeriodFormatter(IntervalField.DAY, IntervalField.MINUTE);
private static final PeriodFormatter INTERVAL_DAY_HOUR_FORMATTER = cretePeriodFormatter(IntervalField.DAY, IntervalField.HOUR);
private static final PeriodFormatter INTERVAL_DAY_FORMATTER = cretePeriodFormatter(IntervalField.DAY, IntervalField.DAY);
private static final PeriodFormatter INTERVAL_HOUR_SECOND_FORMATTER = cretePeriodFormatter(IntervalField.HOUR, IntervalField.SECOND);
private static final PeriodFormatter INTERVAL_HOUR_MINUTE_FORMATTER = cretePeriodFormatter(IntervalField.HOUR, IntervalField.MINUTE);View on GitHub (pinned to 55bb57d202)
Solutions
- Use the canonical format 'HH:mm:ss[.fff]' e.g. '12:30:00.000' or with offset '12:30:00 +01:00'
- Use try_cast(value AS TIME) to surface NULLs instead of exceptions
- Trim/normalize the string before parsing and validate with a regex like ^\\d{2}:\\d{2}:\\d{2}
- If you have a full timestamp, strip the date portion first
Example fix
-- before SELECT TIME '12:30 PM'; -- after SELECT TIME '12:30:00.000';
Defensive patterns
Strategy: validation
Validate before calling
boolean ok = value != null && value.matches("\\d{1,2}:\\d{2}:\\d{2}(\\.\\d{1,3})?([+-]\\d{2}:?\\d{2})? *$"); Try / catch
try { DateTimeUtils.timeHasTimeZone(value); } catch (IllegalArgumentException e) { return null; } Prevention
- Use 24-hour 'HH:mm:ss[.SSS]' format only
- Strip date portions before passing to TIME parsers
- Use try_cast(value AS TIME) to surface NULLs instead of exceptions
When it happens
Trigger: Passing a malformed time string (e.g. '25:00:00', '12:00', 'noon', or a full timestamp string) to APIs that classify time literals, such as parsing TIME literals in SQL or type coercion paths.
Common situations: Users writing '12:30 PM' style times in SQL; accidentally passing a timestamp where a time is expected; locale formats with AM/PM or fractional fields in the wrong place; leading/trailing whitespace.
Related errors
- Invalid timestamp '%s'
- Invalid day second interval qualifier: to
- Invalid year month interval qualifier: to
- INVALID_FUNCTION_ARGUMENT
- QualifiedObjectName should have exactly 3 parts, found %s: %
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/20da193082c15b51.
Report an issue: GitHub.