From 906fec210fbaa5d2010223f9b84cad677f836442 Mon Sep 17 00:00:00 2001 From: OldTruckDriver Date: Wed, 3 Jun 2026 17:10:15 +1000 Subject: [PATCH] [CALCITE-7559] SqlParserUtil.parseTimeTzLiteral should reject unknown time zones --- .../calcite/sql/parser/SqlParserUtil.java | 14 ++++++++-- .../calcite/sql/parser/SqlParserUtilTest.java | 28 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java index 1ba1933fc70e..c48bb846fb8b 100644 --- a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java @@ -371,16 +371,24 @@ public static SqlTimeTzLiteral parseTimeTzLiteral( final String timeZone = s.substring(lastSpace + 1); final String time = s.substring(0, lastSpace); - final TimeZone tz = TimeZone.getTimeZone(timeZone); - if (tz != null) { + try { + ZoneId zoneId = ZoneId.of(timeZone); + TimeZone tz = TimeZone.getTimeZone(zoneId); pt = DateTimeUtils.parsePrecisionDateTimeLiteral(time, Format.get().time, tz, -1); + } catch (DateTimeException e) { + String message = e.getMessage(); + if (message == null) { + message = "Error parsing TIME ZONE"; + } + throw SqlUtil.newContextException(pos, + RESOURCE.illegalLiteral("TIME WITH TIME ZONE", s, message)); } } if (pt == null) { throw SqlUtil.newContextException(pos, RESOURCE.illegalLiteral("TIME WITH TIME ZONE", s, - RESOURCE.badFormat(DateTimeUtils.TIME_FORMAT_STRING).str())); + RESOURCE.badFormat(DateTimeUtils.TIME_FORMAT_STRING + " zone").str())); } final TimeWithTimeZoneString t = TimeWithTimeZoneString.fromCalendarFields(pt.getCalendar()) .withFraction(pt.getFraction()); diff --git a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserUtilTest.java b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserUtilTest.java index 03de04f860da..2a78715c019e 100644 --- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserUtilTest.java +++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserUtilTest.java @@ -19,6 +19,7 @@ import org.apache.calcite.avatica.util.TimeUnit; import org.apache.calcite.runtime.CalciteContextException; import org.apache.calcite.sql.SqlIntervalQualifier; +import org.apache.calcite.sql.SqlTimeTzLiteral; import org.apache.calcite.sql.SqlTimestampTzLiteral; import org.junit.jupiter.api.Test; @@ -94,6 +95,33 @@ public class SqlParserUtilTest { } } + @Test void testTimeWithTimeZone() { + SqlParserPos pos = new SqlParserPos(2, 3); + SqlTimeTzLiteral lit = + SqlParserUtil.parseTimeTzLiteral("10:10:10 GMT", pos); + assertThat(lit, hasToString("TIME WITH TIME ZONE '10:10:10 UTC'")); + + // Like parseTimestampTzLiteral, parseTimeTzLiteral should reject unknown + // time zones instead of silently falling back to GMT. + try { + SqlParserUtil.parseTimeTzLiteral("10:10:10 incorrect_zone", pos); + fail("Should be unreachable"); + } catch (CalciteContextException ex) { + assertThat( + ex.getMessage(), is("At line 2, column 3: Illegal TIME WITH TIME ZONE literal " + + "'10:10:10 incorrect_zone': Unknown time-zone ID: incorrect_zone")); + } + + try { + SqlParserUtil.parseTimeTzLiteral("10:10:10", pos); + fail("Should be unreachable"); + } catch (CalciteContextException ex) { + assertThat( + ex.getMessage(), is("At line 2, column 3: Illegal TIME WITH TIME ZONE literal " + + "'10:10:10': not in format 'HH:mm:ss zone'")); + } + } + @Test void testMinuteToSecondIntervalToMillis() { final SqlIntervalQualifier qualifier = new SqlIntervalQualifier(TimeUnit.MINUTE, TimeUnit.SECOND, POSITION);