Skip to content

Commit

Permalink
Add year, month, day, minute, second Presto functions
Browse files Browse the repository at this point in the history
Reviewed By: pedroerp

Differential Revision: D31625055

fbshipit-source-id: b3a7bbc6f11f9b506f69810baf1e4a0bae6b04cb
  • Loading branch information
mbasmanova authored and facebook-github-bot committed Oct 14, 2021
1 parent 04b489e commit 7ac665a
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 31 deletions.
35 changes: 31 additions & 4 deletions velox/docs/functions/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,41 @@ Date and Time Functions
Returns the UNIX timestamp ``unixtime`` as a timestamp with time zone
using ``string`` for the time zone.

... function:: hour(unixtime) -> bigint
.. function:: to_unixtime(timestamp) -> double

Returns ``timestamp`` as a UNIX timestamp.

Convenience Extraction Functions
--------------------------------

.. function:: day(timestamp) -> bigint

Returns the day of the month from ``timestamp``.

.. function:: day_of_month(timestamp) -> bigint

This is an alias for :func:`day`.

... function:: hour(timestamp) -> bigint

Returns the hour of the day from ``unixtime``. The value ranges from 0 to 23.
Returns the hour of the day from ``timestamp``. The value ranges from 0 to 23.

.. function:: millisecond(timestamp) -> int64

Returns the millisecond of the second from ``timestamp``.

.. function:: to_unixtime(timestamp) -> double
.. function:: minute(timestamp) -> bigint

Returns ``timestamp`` as a UNIX timestamp.
Returns the minute of the hour from ``timestamp``.

.. function:: month(timestamp) -> bigint

Returns the month of the year from ``timestamp``.

.. function:: second(timestamp) -> bigint

Returns the second of the minute from ``timestamp``.

.. function:: year(timestamp) -> bigint

Returns the year from ``timestamp``.
7 changes: 6 additions & 1 deletion velox/functions/prestosql/CoreFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,13 @@ void registerFunctions() {
registerFunction<udf_to_unixtime, double, Timestamp>(
{"to_unixtime", "to_unix_timestamp"});
registerFunction<udf_from_unixtime, Timestamp, double>();
registerFunction<udf_millisecond, int64_t, Timestamp>();
registerFunction<udf_year, int64_t, Timestamp>();
registerFunction<udf_month, int64_t, Timestamp>();
registerFunction<udf_day, int64_t, Timestamp>({"day", "day_of_month"});
registerFunction<udf_hour, int64_t, Timestamp>();
registerFunction<udf_minute, int64_t, Timestamp>();
registerFunction<udf_second, int64_t, Timestamp>();
registerFunction<udf_millisecond, int64_t, Timestamp>();

registerArithmeticFunctions();
registerCheckedArithmeticFunctions();
Expand Down
118 changes: 105 additions & 13 deletions velox/functions/prestosql/DateTimeFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,37 +41,129 @@ FOLLY_ALWAYS_INLINE bool call(
}
VELOX_UDF_END();

VELOX_UDF_BEGIN(hour)
const date::time_zone* timeZone_ = nullptr;

FOLLY_ALWAYS_INLINE void initialize(const core::QueryConfig& config) {
namespace {
FOLLY_ALWAYS_INLINE const date::time_zone* getTimeZoneFromConfig(
const core::QueryConfig& config) {
if (config.adjustTimestampToTimezone()) {
auto sessionTzName = config.sessionTimezone();
if (!sessionTzName.empty()) {
timeZone_ = date::locate_zone(sessionTzName);
return date::locate_zone(sessionTzName);
}
}
return nullptr;
}

FOLLY_ALWAYS_INLINE int64_t
getSeconds(Timestamp timestamp, const date::time_zone* timeZone) {
if (timeZone != nullptr) {
timestamp.toTimezoneUTC(*timeZone);
return timestamp.getSeconds();
} else {
return timestamp.getSeconds();
}
}
} // namespace

VELOX_UDF_BEGIN(year)
const date::time_zone* timeZone_ = nullptr;

FOLLY_ALWAYS_INLINE void initialize(const core::QueryConfig& config) {
timeZone_ = getTimeZoneFromConfig(config);
}

FOLLY_ALWAYS_INLINE bool call(
int64_t& result,
const arg_type<Timestamp>& timestamp) {
int64_t seconds = getSeconds(timestamp, timeZone_);
std::tm dateTime;
gmtime_r((const time_t*)&seconds, &dateTime);
result = 1900 + dateTime.tm_year;
return true;
}
VELOX_UDF_END();

VELOX_UDF_BEGIN(month)
const date::time_zone* timeZone_ = nullptr;

FOLLY_ALWAYS_INLINE void initialize(const core::QueryConfig& config) {
timeZone_ = getTimeZoneFromConfig(config);
}

FOLLY_ALWAYS_INLINE bool call(
int64_t& result,
const arg_type<Timestamp>& timestamp) {
int64_t seconds = getSeconds(timestamp, timeZone_);
std::tm dateTime;
gmtime_r((const time_t*)&seconds, &dateTime);
result = 1 + dateTime.tm_mon;
return true;
}
VELOX_UDF_END();

VELOX_UDF_BEGIN(day)
const date::time_zone* timeZone_ = nullptr;

FOLLY_ALWAYS_INLINE void initialize(const core::QueryConfig& config) {
timeZone_ = getTimeZoneFromConfig(config);
}

FOLLY_ALWAYS_INLINE bool call(
int64_t& result,
const arg_type<Timestamp>& timestamp) {
int64_t seconds = getSeconds(timestamp);
int64_t seconds = getSeconds(timestamp, timeZone_);
std::tm dateTime;
gmtime_r((const time_t*)&seconds, &dateTime);
result = dateTime.tm_mday;
return true;
}
VELOX_UDF_END();

VELOX_UDF_BEGIN(hour)
const date::time_zone* timeZone_ = nullptr;

FOLLY_ALWAYS_INLINE void initialize(const core::QueryConfig& config) {
timeZone_ = getTimeZoneFromConfig(config);
}

FOLLY_ALWAYS_INLINE bool call(
int64_t& result,
const arg_type<Timestamp>& timestamp) {
int64_t seconds = getSeconds(timestamp, timeZone_);
std::tm dateTime;
gmtime_r((const time_t*)&seconds, &dateTime);
result = dateTime.tm_hour;
return true;
}
VELOX_UDF_END();

FOLLY_ALWAYS_INLINE int64_t getSeconds(Timestamp timestamp) {
if (timeZone_ != nullptr) {
timestamp.toTimezoneUTC(*timeZone_);
return timestamp.getSeconds();
} else {
return timestamp.getSeconds();
}
VELOX_UDF_BEGIN(minute)
const date::time_zone* timeZone_ = nullptr;

FOLLY_ALWAYS_INLINE void initialize(const core::QueryConfig& config) {
timeZone_ = getTimeZoneFromConfig(config);
}

FOLLY_ALWAYS_INLINE bool call(
int64_t& result,
const arg_type<Timestamp>& timestamp) {
int64_t seconds = getSeconds(timestamp, timeZone_);
std::tm dateTime;
gmtime_r((const time_t*)&seconds, &dateTime);
result = dateTime.tm_min;
return true;
}
VELOX_UDF_END();

VELOX_UDF_BEGIN(second)
FOLLY_ALWAYS_INLINE bool call(
int64_t& result,
const arg_type<Timestamp>& timestamp) {
int64_t seconds = timestamp.getSeconds();
std::tm dateTime;
gmtime_r((const time_t*)&seconds, &dateTime);
result = dateTime.tm_sec;
return true;
}
VELOX_UDF_END();

VELOX_UDF_BEGIN(millisecond)
Expand Down
112 changes: 99 additions & 13 deletions velox/functions/prestosql/tests/DateTimeFunctionsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@

using namespace facebook::velox;

class DateTimeFunctionsTest : public functions::test::FunctionBaseTest {};
class DateTimeFunctionsTest : public functions::test::FunctionBaseTest {
protected:
void setQueryTimeZone(const std::string& timeZone) {
queryCtx_->setConfigOverridesUnsafe({
{core::QueryConfig::kSessionTimezone, timeZone},
{core::QueryConfig::kAdjustTimestampToTimezone, "true"},
});
}
};

// Test cases from PrestoDB [1] are covered here as well:
// Timestamp(998474645, 321000000) from "TIMESTAMP '2001-08-22 03:04:05.321'"
Expand Down Expand Up @@ -120,15 +128,50 @@ TEST_F(DateTimeFunctionsTest, fromUnixtime) {
std::nullopt, fromUnixtime(std::numeric_limits<double>::quiet_NaN()));
}

TEST_F(DateTimeFunctionsTest, millisecond) {
const auto millisecond = [&](std::optional<Timestamp> timestamp) {
return evaluateOnce<int64_t>("millisecond(c0)", timestamp);
TEST_F(DateTimeFunctionsTest, year) {
const auto year = [&](std::optional<Timestamp> date) {
return evaluateOnce<int64_t>("year(c0)", date);
};
EXPECT_EQ(std::nullopt, millisecond(std::nullopt));
EXPECT_EQ(0, millisecond(Timestamp(0, 0)));
EXPECT_EQ(0, millisecond(Timestamp(4000000000, 0)));
EXPECT_EQ(123, millisecond(Timestamp(-1, 123000000)));
EXPECT_EQ(12300, millisecond(Timestamp(-1, 12300000000)));
EXPECT_EQ(std::nullopt, year(std::nullopt));
EXPECT_EQ(1970, year(Timestamp(0, 0)));
EXPECT_EQ(1969, year(Timestamp(-1, 9000)));
EXPECT_EQ(2096, year(Timestamp(4000000000, 0)));
EXPECT_EQ(2096, year(Timestamp(4000000000, 123000000)));
EXPECT_EQ(2001, year(Timestamp(998474645, 321000000)));
EXPECT_EQ(2001, year(Timestamp(998423705, 321000000)));

setQueryTimeZone("Pacific/Apia");

EXPECT_EQ(std::nullopt, year(std::nullopt));
EXPECT_EQ(1969, year(Timestamp(0, 0)));
EXPECT_EQ(1969, year(Timestamp(-1, 12300000000)));
EXPECT_EQ(2096, year(Timestamp(4000000000, 0)));
EXPECT_EQ(2096, year(Timestamp(4000000000, 123000000)));
EXPECT_EQ(2001, year(Timestamp(998474645, 321000000)));
EXPECT_EQ(2001, year(Timestamp(998423705, 321000000)));
}

TEST_F(DateTimeFunctionsTest, month) {
const auto month = [&](std::optional<Timestamp> date) {
return evaluateOnce<int64_t>("month(c0)", date);
};
EXPECT_EQ(std::nullopt, month(std::nullopt));
EXPECT_EQ(1, month(Timestamp(0, 0)));
EXPECT_EQ(12, month(Timestamp(-1, 9000)));
EXPECT_EQ(10, month(Timestamp(4000000000, 0)));
EXPECT_EQ(10, month(Timestamp(4000000000, 123000000)));
EXPECT_EQ(8, month(Timestamp(998474645, 321000000)));
EXPECT_EQ(8, month(Timestamp(998423705, 321000000)));

setQueryTimeZone("Pacific/Apia");

EXPECT_EQ(std::nullopt, month(std::nullopt));
EXPECT_EQ(12, month(Timestamp(0, 0)));
EXPECT_EQ(12, month(Timestamp(-1, 12300000000)));
EXPECT_EQ(10, month(Timestamp(4000000000, 0)));
EXPECT_EQ(10, month(Timestamp(4000000000, 123000000)));
EXPECT_EQ(8, month(Timestamp(998474645, 321000000)));
EXPECT_EQ(8, month(Timestamp(998423705, 321000000)));
}

TEST_F(DateTimeFunctionsTest, hour) {
Expand All @@ -143,10 +186,8 @@ TEST_F(DateTimeFunctionsTest, hour) {
EXPECT_EQ(10, hour(Timestamp(998474645, 321000000)));
EXPECT_EQ(19, hour(Timestamp(998423705, 321000000)));

queryCtx_->setConfigOverridesUnsafe({
{core::QueryConfig::kSessionTimezone, "Pacific/Apia"},
{core::QueryConfig::kAdjustTimestampToTimezone, "true"},
});
setQueryTimeZone("Pacific/Apia");

EXPECT_EQ(std::nullopt, hour(std::nullopt));
EXPECT_EQ(13, hour(Timestamp(0, 0)));
EXPECT_EQ(12, hour(Timestamp(-1, 12300000000)));
Expand All @@ -155,3 +196,48 @@ TEST_F(DateTimeFunctionsTest, hour) {
EXPECT_EQ(23, hour(Timestamp(998474645, 321000000)));
EXPECT_EQ(8, hour(Timestamp(998423705, 321000000)));
}

TEST_F(DateTimeFunctionsTest, minute) {
const auto minute = [&](std::optional<Timestamp> date) {
return evaluateOnce<int64_t>("minute(c0)", date);
};
EXPECT_EQ(std::nullopt, minute(std::nullopt));
EXPECT_EQ(0, minute(Timestamp(0, 0)));
EXPECT_EQ(59, minute(Timestamp(-1, 9000)));
EXPECT_EQ(6, minute(Timestamp(4000000000, 0)));
EXPECT_EQ(6, minute(Timestamp(4000000000, 123000000)));
EXPECT_EQ(4, minute(Timestamp(998474645, 321000000)));
EXPECT_EQ(55, minute(Timestamp(998423705, 321000000)));

setQueryTimeZone("Asia/Kolkata");

EXPECT_EQ(std::nullopt, minute(std::nullopt));
EXPECT_EQ(30, minute(Timestamp(0, 0)));
EXPECT_EQ(29, minute(Timestamp(-1, 9000)));
EXPECT_EQ(36, minute(Timestamp(4000000000, 0)));
EXPECT_EQ(36, minute(Timestamp(4000000000, 123000000)));
EXPECT_EQ(34, minute(Timestamp(998474645, 321000000)));
EXPECT_EQ(25, minute(Timestamp(998423705, 321000000)));
}

TEST_F(DateTimeFunctionsTest, second) {
const auto second = [&](std::optional<Timestamp> timestamp) {
return evaluateOnce<int64_t>("second(c0)", timestamp);
};
EXPECT_EQ(std::nullopt, second(std::nullopt));
EXPECT_EQ(0, second(Timestamp(0, 0)));
EXPECT_EQ(40, second(Timestamp(4000000000, 0)));
EXPECT_EQ(59, second(Timestamp(-1, 123000000)));
EXPECT_EQ(59, second(Timestamp(-1, 12300000000)));
}

TEST_F(DateTimeFunctionsTest, millisecond) {
const auto millisecond = [&](std::optional<Timestamp> timestamp) {
return evaluateOnce<int64_t>("millisecond(c0)", timestamp);
};
EXPECT_EQ(std::nullopt, millisecond(std::nullopt));
EXPECT_EQ(0, millisecond(Timestamp(0, 0)));
EXPECT_EQ(0, millisecond(Timestamp(4000000000, 0)));
EXPECT_EQ(123, millisecond(Timestamp(-1, 123000000)));
EXPECT_EQ(12300, millisecond(Timestamp(-1, 12300000000)));
}

0 comments on commit 7ac665a

Please sign in to comment.