diff --git a/velox/docs/functions/spark/datetime.rst b/velox/docs/functions/spark/datetime.rst index 708a3349b93b..85ca72628933 100644 --- a/velox/docs/functions/spark/datetime.rst +++ b/velox/docs/functions/spark/datetime.rst @@ -93,6 +93,12 @@ These functions support TIMESTAMP and DATE input types. SELECT from_unixtime(3600, 'yyyy'); -- '1970' SELECT from_unixtime(9223372036854775807, "yyyy-MM-dd HH:mm:ss"); -- '1969-12-31 23:59:59' + If we run the following query in the `Asia/Shanghai` time zone: :: + + SELECT from_unixtime(100, 'yyyy-MM-dd HH:mm:ss'); -- '1970-01-01 08:01:40' + SELECT from_unixtime(3600, 'yyyy'); -- '1970' + SELECT from_unixtime(9223372036854775807, "yyyy-MM-dd HH:mm:ss"); -- '1970-01-01 07:59:59' + .. spark:function:: from_utc_timestamp(timestamp, string) -> timestamp Returns the timestamp value from UTC timezone to the given timezone. :: diff --git a/velox/functions/lib/DateTimeFormatter.cpp b/velox/functions/lib/DateTimeFormatter.cpp index 09d0f5fb70d6..d697b86a6104 100644 --- a/velox/functions/lib/DateTimeFormatter.cpp +++ b/velox/functions/lib/DateTimeFormatter.cpp @@ -1033,7 +1033,7 @@ int32_t DateTimeFormatter::format( bool allowOverflow) const { Timestamp t = timestamp; if (timezone != nullptr) { - t.toTimezone(*timezone); + t.toTimezone(*timezone, allowOverflow); } const auto timePoint = t.toTimePoint(allowOverflow); const auto daysTimePoint = date::floor(timePoint); diff --git a/velox/functions/sparksql/tests/DateTimeFunctionsTest.cpp b/velox/functions/sparksql/tests/DateTimeFunctionsTest.cpp index 131143df3126..b29e20ee0940 100644 --- a/velox/functions/sparksql/tests/DateTimeFunctionsTest.cpp +++ b/velox/functions/sparksql/tests/DateTimeFunctionsTest.cpp @@ -898,6 +898,15 @@ TEST_F(DateTimeFunctionsTest, fromUnixtime) { // 8 hours ahead UTC. setQueryTimeZone("Asia/Shanghai"); +// In debug mode, Timestamp constructor will throw exception if range check +// fails. +#ifdef NDEBUG + // Integer overflow in the internal conversion from seconds to milliseconds. + EXPECT_EQ( + fromUnixTime(std::numeric_limits::max(), "yyyy-MM-dd HH:mm:ss"), + "1970-01-01 07:59:59"); +#endif + EXPECT_EQ(fromUnixTime(0, "yyyy-MM-dd HH:mm:ss"), "1970-01-01 08:00:00"); EXPECT_EQ(fromUnixTime(120, "yyyy-MM-dd HH:mm"), "1970-01-01 08:02"); EXPECT_EQ(fromUnixTime(-59, "yyyy-MM-dd HH:mm:ss"), "1970-01-01 07:59:01"); diff --git a/velox/type/Timestamp.cpp b/velox/type/Timestamp.cpp index 257a5a6cd4ff..1a8a363fbb6c 100644 --- a/velox/type/Timestamp.cpp +++ b/velox/type/Timestamp.cpp @@ -118,8 +118,8 @@ Timestamp::toTimePoint(bool allowOverflow) const { return tp; } -void Timestamp::toTimezone(const date::time_zone& zone) { - auto tp = toTimePoint(); +void Timestamp::toTimezone(const date::time_zone& zone, bool allowOverflow) { + auto tp = toTimePoint(allowOverflow); auto epoch = zone.to_local(tp).time_since_epoch(); // NOTE: Round down to get the seconds of the current time point. seconds_ = std::chrono::floor(epoch).count(); diff --git a/velox/type/Timestamp.h b/velox/type/Timestamp.h index ebb1bc8fbd4c..2e13558c1d3c 100644 --- a/velox/type/Timestamp.h +++ b/velox/type/Timestamp.h @@ -187,7 +187,7 @@ struct Timestamp { /// Due to the limit of std::chrono, throws if timestamp is outside of /// [-32767-01-01, 32767-12-31] range. /// If allowOverflow is true, integer overflow is allowed in converting - /// timestmap to milliseconds. + /// timestamp to milliseconds. std::chrono::time_point toTimePoint(bool allowOverflow = false) const; @@ -311,12 +311,14 @@ struct Timestamp { // Same as above, but accepts PrestoDB time zone ID. void toGMT(int16_t tzID); - // Assuming the timestamp represents a GMT time, converts it to the time at - // the same moment at zone. - // Example: Timestamp ts{0, 0}; - // ts.Timezone("America/Los_Angeles"); - // ts.toString() returns December 31, 1969 16:00:00 - void toTimezone(const date::time_zone& zone); + /// Assuming the timestamp represents a GMT time, converts it to the time at + /// the same moment at zone. + /// @param allowOverflow If true, integer overflow is allowed when converting + /// timestamp to TimePoint. Otherwise, user exception is thrown for overflow. + /// Example: Timestamp ts{0, 0}; + /// ts.Timezone("America/Los_Angeles"); + /// ts.toString() returns December 31, 1969 16:00:00 + void toTimezone(const date::time_zone& zone, bool allowOverflow = false); // Same as above, but accepts PrestoDB time zone ID. void toTimezone(int16_t tzID);