Skip to content

Commit

Permalink
Add support for z, zz, zzz in format_datetime (#11323)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #11323

This diff adds support for JODA's z, zz, zzz patterns (all equivalent) in Presto's
forma_datetime function.

This is used to format time zone abbreviations.

Differential Revision: D64774281
  • Loading branch information
Kevin Wilfong authored and facebook-github-bot committed Oct 24, 2024
1 parent af56035 commit 310267d
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 13 deletions.
30 changes: 23 additions & 7 deletions velox/functions/lib/DateTimeFormatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1167,10 +1167,17 @@ uint32_t DateTimeFormatter::maxResultSize(const tz::TimeZone* timezone) const {
size += std::max((int)token.pattern.minRepresentDigits, 9);
break;
case DateTimeFormatSpecifier::TIMEZONE:
VELOX_NYI(
"Date format specifier is not yet implemented: {} ({})",
getSpecifierName(token.pattern.specifier),
token.pattern.minRepresentDigits);
if (token.pattern.minRepresentDigits <= 3) {
// The longest abbreviation according to here is 5, e.g. some time
// zones use the offset as the abbreviation, like +0530.
// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
size += 5;
} else {
VELOX_NYI(
"Date format specifier is not yet implemented: {} ({})",
getSpecifierName(token.pattern.specifier),
token.pattern.minRepresentDigits);
}

break;
case DateTimeFormatSpecifier::TIMEZONE_OFFSET_ID:
Expand Down Expand Up @@ -1415,9 +1422,18 @@ int32_t DateTimeFormatter::format(
} break;

case DateTimeFormatSpecifier::TIMEZONE: {
// TODO: implement short name time zone, need a map from full name
// to short name
VELOX_UNSUPPORTED("time zone name is not yet supported");
VELOX_USER_CHECK_NOT_NULL(
timezone,
"The time zone cannot be formatted if it is not present.");
if (token.pattern.minRepresentDigits <= 3) {
const std::string& abbrev = timezone->getShortName(
std::chrono::seconds(timestamp.getSeconds()));
std::memcpy(result, abbrev.data(), abbrev.length());
result += abbrev.length();
} else {
// TODO: implement full name time zone
VELOX_NYI("full time zone name is not yet supported");
}
} break;

case DateTimeFormatSpecifier::TIMEZONE_OFFSET_ID: {
Expand Down
29 changes: 23 additions & 6 deletions velox/functions/prestosql/tests/DateTimeFunctionsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3269,6 +3269,24 @@ TEST_F(DateTimeFunctionsTest, formatDateTime) {
EXPECT_EQ("+05:30", formatDatetime(parseTimestamp("1970-01-01"), "ZZ"));
EXPECT_EQ("+0530", formatDatetime(parseTimestamp("1970-01-01"), "Z"));

EXPECT_EQ("IST", formatDatetime(parseTimestamp("1970-01-01"), "zzz"));
EXPECT_EQ("IST", formatDatetime(parseTimestamp("1970-01-01"), "zz"));
EXPECT_EQ("IST", formatDatetime(parseTimestamp("1970-01-01"), "z"));

// Test daylight savings.
setQueryTimeZone("America/Los_Angeles");
EXPECT_EQ("PST", formatDatetime(parseTimestamp("1970-01-01"), "z"));
EXPECT_EQ("PDT", formatDatetime(parseTimestamp("1970-10-01"), "z"));

// Test a long abbreviation.
setQueryTimeZone("Asia/Colombo");
EXPECT_EQ("+0530", formatDatetime(parseTimestamp("1970-10-01"), "z"));

setQueryTimeZone("Asia/Kolkata");
// We don't support more than 3 'z's yet.
EXPECT_THROW(
formatDatetime(parseTimestamp("1970-01-01"), "zzzz"), VeloxRuntimeError);

// Literal test cases.
EXPECT_EQ("hello", formatDatetime(parseTimestamp("1970-01-01"), "'hello'"));
EXPECT_EQ("'", formatDatetime(parseTimestamp("1970-01-01"), "''"));
Expand Down Expand Up @@ -3313,15 +3331,14 @@ TEST_F(DateTimeFunctionsTest, formatDateTime) {
EXPECT_THROW(
formatDatetime(parseTimestamp("1970-01-01"), "'abcd"), VeloxUserError);

// System errors for patterns we haven't implemented yet.
// Time zone name patterns aren't supported when there isn't a time zone
// available.
EXPECT_THROW(
formatDatetime(parseTimestamp("1970-01-01"), "z"), VeloxRuntimeError);
formatDatetime(parseTimestamp("1970-01-01"), "z"), VeloxUserError);
EXPECT_THROW(
formatDatetime(parseTimestamp("1970-01-01"), "zz"), VeloxRuntimeError);
formatDatetime(parseTimestamp("1970-01-01"), "zz"), VeloxUserError);
EXPECT_THROW(
formatDatetime(parseTimestamp("1970-01-01"), "zzz"), VeloxRuntimeError);
EXPECT_THROW(
formatDatetime(parseTimestamp("1970-01-01"), "zzzz"), VeloxRuntimeError);
formatDatetime(parseTimestamp("1970-01-01"), "zzz"), VeloxUserError);
}

TEST_F(DateTimeFunctionsTest, formatDateTimeTimezone) {
Expand Down
4 changes: 4 additions & 0 deletions velox/type/tz/TimeZoneMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,4 +361,8 @@ TimeZone::seconds TimeZone::to_local(TimeZone::seconds timestamp) const {
return date::zoned_time{tz_, timePoint}.get_local_time().time_since_epoch();
}

std::string TimeZone::getShortName(TimeZone::seconds timestamp) const {
return tz_->get_info(date::local_seconds(timestamp)).first.abbrev;
}

} // namespace facebook::velox::tz
2 changes: 2 additions & 0 deletions velox/type/tz/TimeZoneMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ class TimeZone {
return tz_;
}

std::string getShortName(TimeZone::seconds timestamp) const;

private:
const date::time_zone* tz_{nullptr};
const std::chrono::minutes offset_{0};
Expand Down

0 comments on commit 310267d

Please sign in to comment.