Skip to content

Commit 867bf03

Browse files
kevinwilfongfeilong-liu
authored andcommitted
Fix second UDF not respecting sub-minute time zone offsets
1 parent 60941a7 commit 867bf03

File tree

4 files changed

+33
-18
lines changed

4 files changed

+33
-18
lines changed

presto-function-server/src/test/java/com/facebook/presto/tests/TestRestRemoteFunctions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ public void testRemoteFunctions()
7575
.getMaterializedRows().get(0).getField(0).toString(),
7676
"1230");
7777
assertEquals(
78-
computeActual(session, "select rest.default.second(CAST('2001-01-02 03:04:05' as timestamp))")
78+
computeActual(session, "select rest.default.day(interval '2' day)")
7979
.getMaterializedRows().get(0).getField(0).toString(),
80-
"5");
80+
"2");
8181
assertEquals(
8282
computeActual(session, "select rest.default.length(CAST('AB' AS VARBINARY))")
8383
.getMaterializedRows().get(0).getField(0).toString(),

presto-main-base/src/main/java/com/facebook/presto/operator/scalar/DateTimeFunctions.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -851,41 +851,43 @@ public static long millisecondFromInterval(@SqlType(StandardTypes.INTERVAL_DAY_T
851851
@Description("second of the minute of the given timestamp")
852852
@ScalarFunction("second")
853853
@SqlType(StandardTypes.BIGINT)
854-
public static long secondFromTimestamp(@SqlType(StandardTypes.TIMESTAMP) long timestamp)
854+
public static long secondFromTimestamp(SqlFunctionProperties properties, @SqlType(StandardTypes.TIMESTAMP) long timestamp)
855855
{
856-
// No need to check isLegacyTimestamp:
857-
// * Under legacy semantics, the session zone matters. But a zone always has offset of whole minutes.
858-
// * Under new semantics, timestamp is agnostic to the session zone.
859-
return SECOND_OF_MINUTE.get(timestamp);
856+
if (properties.isLegacyTimestamp()) {
857+
return getChronology(properties.getTimeZoneKey()).secondOfMinute().get(timestamp);
858+
}
859+
else {
860+
return SECOND_OF_MINUTE.get(timestamp);
861+
}
860862
}
861863

862864
@Description("second of the minute of the given timestamp")
863865
@ScalarFunction("second")
864866
@SqlType(StandardTypes.BIGINT)
865867
public static long secondFromTimestampWithTimeZone(@SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long timestampWithTimeZone)
866868
{
867-
// No need to check the associated zone here. A zone always has offset of whole minutes.
868-
return SECOND_OF_MINUTE.get(unpackMillisUtc(timestampWithTimeZone));
869+
return unpackChronology(timestampWithTimeZone).secondOfMinute().get(unpackMillisUtc(timestampWithTimeZone));
869870
}
870871

871872
@Description("second of the minute of the given time")
872873
@ScalarFunction("second")
873874
@SqlType(StandardTypes.BIGINT)
874-
public static long secondFromTime(@SqlType(StandardTypes.TIME) long time)
875+
public static long secondFromTime(SqlFunctionProperties properties, @SqlType(StandardTypes.TIME) long time)
875876
{
876-
// No need to check isLegacyTimestamp:
877-
// * Under legacy semantics, the session zone matters. But a zone always has offset of whole minutes.
878-
// * Under new semantics, time is agnostic to the session zone.
879-
return SECOND_OF_MINUTE.get(time);
877+
if (properties.isLegacyTimestamp()) {
878+
return getChronology(properties.getTimeZoneKey()).secondOfMinute().get(time);
879+
}
880+
else {
881+
return SECOND_OF_MINUTE.get(time);
882+
}
880883
}
881884

882885
@Description("second of the minute of the given time")
883886
@ScalarFunction("second")
884887
@SqlType(StandardTypes.BIGINT)
885-
public static long secondFromTimeWithTimeZone(@SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long time)
888+
public static long secondFromTimeWithTimeZone(@SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long timeWithTimeZone)
886889
{
887-
// No need to check the associated zone here. A zone always has offset of whole minutes.
888-
return SECOND_OF_MINUTE.get(unpackMillisUtc(time));
890+
return unpackChronology(timeWithTimeZone).secondOfMinute().get(unpackMillisUtc(timeWithTimeZone));
889891
}
890892

891893
@Description("second of the minute of the given interval")

presto-main-base/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsBase.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,19 @@ public void testPartFunctions()
382382
assertFunction("hour(" + INTERVAL_LITERAL + ")", BIGINT, DAY_TO_SECOND_INTERVAL.getSeconds() / 3600 % 24);
383383
}
384384

385+
@Test
386+
public void testPartFunctionsWithSecondsOffset()
387+
{
388+
// Prior to 1920 Asia/Kathmandu's offset was 5:41:16
389+
DateTime dateTime = new DateTime(1910, 1, 22, 3, 4, 5, 678, KATHMANDU_ZONE);
390+
String dateTimeLiteral = "TIMESTAMP '1910-01-22 03:04:05.678 Asia/Kathmandu'";
391+
392+
assertFunction("millisecond(" + dateTimeLiteral + ")", BIGINT, (long) dateTime.getMillisOfSecond());
393+
assertFunction("second(" + dateTimeLiteral + ")", BIGINT, (long) dateTime.getSecondOfMinute());
394+
assertFunction("minute(" + dateTimeLiteral + ")", BIGINT, (long) dateTime.getMinuteOfHour());
395+
assertFunction("hour(" + dateTimeLiteral + ")", BIGINT, (long) dateTime.getHourOfDay());
396+
}
397+
385398
@Test
386399
public void testYearOfWeek()
387400
{

presto-main-base/src/test/java/com/facebook/presto/sql/gen/TestExpressionCompiler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1536,7 +1536,7 @@ private static long callExtractFunction(ConnectorSession session, long value, Fi
15361536
case MINUTE:
15371537
return DateTimeFunctions.minuteFromTimestamp(session.getSqlFunctionProperties(), value);
15381538
case SECOND:
1539-
return DateTimeFunctions.secondFromTimestamp(value);
1539+
return DateTimeFunctions.secondFromTimestamp(session.getSqlFunctionProperties(), value);
15401540
case TIMEZONE_MINUTE:
15411541
return DateTimeFunctions.timeZoneMinuteFromTimestampWithTimeZone(packDateTimeWithZone(value, session.getSqlFunctionProperties().getTimeZoneKey()));
15421542
case TIMEZONE_HOUR:

0 commit comments

Comments
 (0)