diff --git a/velox/expression/CastExpr.cpp b/velox/expression/CastExpr.cpp index 6bb3eec0c9b8c..a42b17bc9543c 100644 --- a/velox/expression/CastExpr.cpp +++ b/velox/expression/CastExpr.cpp @@ -26,6 +26,7 @@ #include "velox/expression/ScopedVarSetter.h" #include "velox/external/date/tz.h" #include "velox/functions/lib/RowsTranslationUtil.h" +#include "velox/functions/lib/TimeUtils.h" #include "velox/type/Type.h" #include "velox/vector/ComplexVector.h" #include "velox/vector/FunctionVector.h" @@ -181,11 +182,7 @@ VectorPtr CastExpr::castFromDate( case TypeKind::TIMESTAMP: { static const int64_t kMillisPerDay{86'400'000}; const auto& queryConfig = context.execCtx()->queryCtx()->queryConfig(); - const auto sessionTzName = queryConfig.sessionTimezone(); - const auto* timeZone = - (queryConfig.adjustTimestampToTimezone() && !sessionTzName.empty()) - ? date::locate_zone(sessionTzName) - : nullptr; + const auto* timeZone = getTimeZoneFromConfig(queryConfig); auto* resultFlatVector = castResult->as>(); applyToSelectedNoThrowLocal(context, rows, castResult, [&](int row) { auto timestamp = Timestamp::fromMillis( diff --git a/velox/expression/tests/CastExprTest.cpp b/velox/expression/tests/CastExprTest.cpp index 223e4678c722d..7b7e97ee976cd 100644 --- a/velox/expression/tests/CastExprTest.cpp +++ b/velox/expression/tests/CastExprTest.cpp @@ -589,6 +589,26 @@ TEST_F(CastExprTest, stringToTimestamp) { std::nullopt, }; testCast("timestamp", input, expected); + + setTimezone("GMT-8"); + testCast( + "timestamp", + { + "1970-01-01", + "1970-01-01 08:00:00", + "1970-01-01 00:00:00", + "1970-01-01 08:00:11", + "1970-01-01 09:00:00", + std::nullopt, + }, + { + Timestamp(-28800, 0), + Timestamp(0, 0), + Timestamp(-28800, 0), + Timestamp(11, 0), + Timestamp(3600, 0), + std::nullopt, + }); } TEST_F(CastExprTest, timestampToString) { @@ -2385,8 +2405,8 @@ class TestingDictionaryToFewerRowsFunction : public exec::VectorFunction { exec::EvalCtx& context, VectorPtr& result) const override { const auto size = rows.size(); - auto indices = - makeIndices(size, [](auto /*row*/) { return 0; }, context.pool()); + auto indices = makeIndices( + size, [](auto /*row*/) { return 0; }, context.pool()); result = BaseVector::wrapInDictionary(nullptr, indices, size, args[0]); } diff --git a/velox/functions/lib/TimeUtils.h b/velox/functions/lib/TimeUtils.h index 43947134fc97e..239fa5d2c4217 100644 --- a/velox/functions/lib/TimeUtils.h +++ b/velox/functions/lib/TimeUtils.h @@ -26,12 +26,28 @@ inline constexpr int64_t kSecondsInDay = 86'400; inline constexpr int64_t kDaysInWeek = 7; extern const folly::F14FastMap kDayOfWeekNames; +// Format timezone to make it compatible with date::locate_zone. +// For example, converts "GMT+8" to "Etc/GMT-8". +// Here is the list of IANA timezone names: +// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +FOLLY_ALWAYS_INLINE std::string formatTimezone(const std::string& timezone) { + if (timezone.find("GMT") == 0 && timezone.size() > 4) { + std::string prefix; + if (timezone[3] != '+' && timezone[3] != '-') { + return timezone; + } + std::string prefix = timezone[3] == '+' ? "Etc/GMT-" : "Etc/GMT+"; + return prefix + timezone.substr(4); + } + return timezone; +} + FOLLY_ALWAYS_INLINE const date::time_zone* getTimeZoneFromConfig( const core::QueryConfig& config) { if (config.adjustTimestampToTimezone()) { auto sessionTzName = config.sessionTimezone(); if (!sessionTzName.empty()) { - return date::locate_zone(sessionTzName); + return date::locate_zone(formatTimezone(sessionTzName)); } } return nullptr;