Ruiqi Dong created CALCITE-7560:
-----------------------------------

             Summary: SqlFunctions.DateParseFunction.parseTimestamp(..., 
timeZone) accepts unknown time zones and silently falls back to GMT
                 Key: CALCITE-7560
                 URL: https://issues.apache.org/jira/browse/CALCITE-7560
             Project: Calcite
          Issue Type: Bug
          Components: core
    Affects Versions: 1.41.0
            Reporter: Ruiqi Dong


*Summary*
Calcite's runtime implementation of BigQuery-style PARSE_TIMESTAMP(format, 
string [, timezone]) accepts unknown time-zone identifiers and interprets them 
as GMT. This is not just parser behavior. It affects runtime evaluation in 
SqlFunctions.DateParseFunction, so a typo in the time-zone argument changes 
query results instead of raising an error.
 
*Affected code*
Files:
core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
{code:java}
/**
 * The "PARSE_TIMESTAMP(string, string [, timezone])" function (BigQuery);
 * Formats a timestamp object according to the specified string.
 */
public static final SqlFunction PARSE_TIMESTAMP = ... {code}
The runtime implementation uses TimeZone.getTimeZone(...) directly:
{code:java}
public long parseTimestamp(String fmtString, String timestamp,
    String timeZone) {
  TimeZone tz = TimeZone.getTimeZone(timeZone);
  final long millisSinceEpoch =
      internalParseDatetime(fmtString, timestamp, timeZone);
  return toLong(new java.sql.Timestamp(millisSinceEpoch), tz);
} {code}
The cached DateFormat path does the same:
{code:java}
TimeZone tz = TimeZone.getTimeZone(timeZone);
parser.setCalendar(Calendar.getInstance(tz, Locale.ROOT)); {code}
Because TimeZone.getTimeZone(...) silently falls back to GMT for unknown IDs, 
the user-supplied bad zone is never rejected.
 
*Reproducer*
Add the following test to 
core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
{code:java}
@Test void testParseTimestampUnknownTimeZoneMatchesGmtFallback() {
  final SqlFunctions.DateParseFunction parse = new 
SqlFunctions.DateParseFunction();

  final long gmt =
      parse.parseTimestamp("%Y-%m-%d %H:%M:%S", "2024-01-01 00:00:00", "GMT");
  final long typo =
      parse.parseTimestamp("%Y-%m-%d %H:%M:%S", "2024-01-01 00:00:00", 
"Asia/Sanghai");

  assertThat(typo, is(gmt));
}

@Test void testParseTimestampRejectsUnknownTimeZone() {
  final SqlFunctions.DateParseFunction parse = new 
SqlFunctions.DateParseFunction();

  assertThrows(RuntimeException.class,
      () -> parse.parseTimestamp("%Y-%m-%d %H:%M:%S",
          "2024-01-01 00:00:00", "Asia/Sanghai"));
} {code}
Run:
{code:java}
./gradlew :core:test \
  --tests 
org.apache.calcite.test.SqlFunctionsTest.testParseTimestampUnknownTimeZoneMatchesGmtFallback
 \
  --tests 
org.apache.calcite.test.SqlFunctionsTest.testParseTimestampRejectsUnknownTimeZone
 {code}
Observed behavior:
The fallback behavior is visible directly * parseTimestamp(..., "Asia/Sanghai") 
returns the same result as parseTimestamp(..., "GMT")
 *  no exception is thrown for the unknown time-zone identifier.

The rejecting test fails with
{code:java}
Expected java.lang.RuntimeException to be thrown, but nothing was thrown. {code}
Expected behavior:

An invalid time-zone identifier should be rejected, rather than being silently 
reinterpreted as GMT.

 
This is a runtime input-validation bug. A misspelled time-zone argument changes 
the meaning of a query result while looking superficially valid, which is worse 
than a hard failure.
 



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to