Skip to content

Commit 746d257

Browse files
pratikpugaliameta-codesync[bot]
authored andcommitted
feat: InputGenerator Support For Time with Time zone (#15476)
Summary: Pull Request resolved: #15476 Add fuzzer input generation support for TIME WITH TIME ZONE type and move type implementation to resolve circular dependency. Changes ------- * Add input generation support for TIME WITH TIME ZONE type * New `TimeWithTimezoneInputGenerator` class generates valid packed time+timezone values * New `TimeWithTimezoneInputGeneratorTest` for validation * Refactor `TimeWithTimezoneType` to break circular dependency * Move `serialize()` and `valueToString()` implementations to new `TimeWithTimezoneType.cpp` * Allows fuzzer_utils to use correct type (TIME_WITH_TIME_ZONE) * Enable intermediate type transform to varchar in Time With Timezone for prestoQueryRunner Reviewed By: kgpai Differential Revision: D86822113 fbshipit-source-id: 7326b7068f0bc2933d4a37d71073a770c6b8e2f9
1 parent c746114 commit 746d257

File tree

9 files changed

+290
-74
lines changed

9 files changed

+290
-74
lines changed

velox/exec/fuzzer/PrestoQueryRunnerIntermediateTypeTransforms.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "velox/functions/prestosql/types/QDigestType.h"
2626
#include "velox/functions/prestosql/types/SfmSketchType.h"
2727
#include "velox/functions/prestosql/types/TDigestType.h"
28+
#include "velox/functions/prestosql/types/TimeWithTimezoneType.h"
2829
#include "velox/functions/prestosql/types/TimestampWithTimeZoneType.h"
2930
#include "velox/parse/Expressions.h"
3031
#include "velox/parse/TypeResolver.h"
@@ -69,6 +70,9 @@ intermediateTypeTransforms() {
6970
{TIME(),
7071
std::make_shared<IntermediateTypeTransformUsingCast>(
7172
TIME(), VARCHAR())},
73+
{TIME_WITH_TIME_ZONE(),
74+
std::make_shared<IntermediateTypeTransformUsingCast>(
75+
TIME_WITH_TIME_ZONE(), VARCHAR())},
7276
{BINGTILE(),
7377
std::make_shared<IntermediateTypeTransformUsingCast>(
7478
BINGTILE(), BIGINT())},

velox/functions/prestosql/types/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ velox_add_library(
3232
TDigestRegistration.cpp
3333
TimestampWithTimeZoneRegistration.cpp
3434
TimeWithTimezoneRegistration.cpp
35+
TimeWithTimezoneType.cpp
3536
SetDigestRegistration.cpp
3637
UuidRegistration.cpp
3738
VarcharEnumRegistration.cpp

velox/functions/prestosql/types/TimeWithTimezoneRegistration.cpp

Lines changed: 4 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -18,85 +18,14 @@
1818
#include "velox/expression/CastExpr.h"
1919
#include "velox/external/tzdb/time_zone.h"
2020
#include "velox/functions/prestosql/types/TimeWithTimezoneType.h"
21-
#include "velox/functions/prestosql/types/TimestampWithTimeZoneType.h"
21+
#include "velox/functions/prestosql/types/fuzzer_utils/TimeWithTimezoneInputGenerator.h"
2222
#include "velox/type/Time.h"
2323
#include "velox/type/Type.h"
2424
#include "velox/type/tz/TimeZoneMap.h"
2525
#include "velox/vector/DecodedVector.h"
2626

2727
namespace facebook::velox {
2828

29-
folly::dynamic TimeWithTimezoneType::serialize() const {
30-
folly::dynamic obj = folly::dynamic::object;
31-
obj["name"] = "Type";
32-
obj["type"] = name();
33-
return obj;
34-
}
35-
36-
StringView TimeWithTimezoneType::valueToString(
37-
int64_t value,
38-
char* const startPos) const {
39-
// TIME WITH TIME ZONE is encoded similarly to TIMESTAMP WITH TIME ZONE
40-
// with the most significnat 52 bits representing the time component and the
41-
// least 12 bits representing the timezone minutes. This is different from
42-
// TIMESTAMP WITH TIMEZONE where the last 12 bits represent the timezone
43-
// offset. The timezone offset minutes are stored by value, encoded in the
44-
// type itself. This allows the type to be used in a timezone-agnostic manner.
45-
//
46-
// The time component is a 52 bit value representing the number of
47-
// milliseconds since midnight in UTC.
48-
49-
int64_t millisUtc = util::unpackMillisUtc(value);
50-
51-
// Ensure time component is within valid range
52-
VELOX_CHECK_GE(millisUtc, 0, "Time component is negative");
53-
VELOX_CHECK_LE(millisUtc, util::kMillisInDay, "Time component is too large");
54-
55-
// TimeZone's are encoded as a 12 bit value.
56-
// This represents a range of -14:00 to +14:00, with 0 representing UTC.
57-
// The range is from -840 to 840 minutes, we thus encode by doing bias
58-
// encoding and taking 840 as the bias.
59-
auto timezoneMinutes = util::unpackZoneKeyId(value);
60-
61-
VELOX_CHECK_GE(timezoneMinutes, 0, "Timezone offset is less than -14:00");
62-
VELOX_CHECK_LE(
63-
timezoneMinutes, 1680, "Timezone offset is greater than +14:00");
64-
65-
// Decode timezone offset from bias-encoded value
66-
int16_t offsetMinutes = util::decodeTimezoneOffset(timezoneMinutes);
67-
auto decodedMinutes = std::abs(offsetMinutes);
68-
69-
const auto isBehindUTCString = (offsetMinutes >= 0) ? "+" : "-";
70-
71-
// Convert UTC time to local time using utility function
72-
// Example: If UTC time is 06:30:00 and timezone is +05:30,
73-
// the local time is 12:00:00
74-
int64_t millisLocal = util::utcToLocalTime(millisUtc, offsetMinutes);
75-
76-
int64_t hours = millisLocal / util::kMillisInHour;
77-
int64_t remainingMs = millisLocal % util::kMillisInHour;
78-
int64_t minutes = remainingMs / util::kMillisInMinute;
79-
remainingMs = remainingMs % util::kMillisInMinute;
80-
int64_t seconds = remainingMs / util::kMillisInSecond;
81-
int64_t millis = remainingMs % util::kMillisInSecond;
82-
83-
int16_t offsetHours = decodedMinutes / util::kMinutesInHour;
84-
int16_t remainingOffsetMinutes = decodedMinutes % util::kMinutesInHour;
85-
86-
fmt::format_to_n(
87-
startPos,
88-
kTimeWithTimezoneToVarcharRowSize,
89-
"{:02d}:{:02d}:{:02d}.{:03d}{}{:02d}:{:02d}",
90-
hours,
91-
minutes,
92-
seconds,
93-
millis,
94-
isBehindUTCString,
95-
offsetHours,
96-
remainingOffsetMinutes);
97-
return StringView{startPos, kTimeWithTimezoneToVarcharRowSize};
98-
}
99-
10029
namespace {
10130
void castToTime(
10231
const BaseVector& input,
@@ -202,7 +131,7 @@ inline int64_t packTimeWithTimeZone(
202131
int64_t timeMillis,
203132
int16_t timezoneOffsetMinutes) {
204133
auto encodedOffset = util::biasEncode(timezoneOffsetMinutes);
205-
return pack(timeMillis, encodedOffset);
134+
return util::pack(timeMillis, encodedOffset);
206135
}
207136

208137
void castFromTime(
@@ -350,7 +279,8 @@ class TimeWithTimezoneTypeFactory : public CustomTypeFactory {
350279

351280
AbstractInputGeneratorPtr getInputGenerator(
352281
const InputGeneratorConfig& config) const override {
353-
return nullptr;
282+
return std::make_shared<fuzzer::TimeWithTimezoneInputGenerator>(
283+
config.seed_, config.nullRatio_);
354284
}
355285
};
356286

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "velox/functions/prestosql/types/TimeWithTimezoneType.h"
18+
19+
#include "velox/type/Time.h"
20+
21+
namespace facebook::velox {
22+
23+
folly::dynamic TimeWithTimezoneType::serialize() const {
24+
folly::dynamic obj = folly::dynamic::object;
25+
obj["name"] = "Type";
26+
obj["type"] = name();
27+
return obj;
28+
}
29+
30+
StringView TimeWithTimezoneType::valueToString(
31+
int64_t value,
32+
char* const startPos) const {
33+
// TIME WITH TIME ZONE is encoded similarly to TIMESTAMP WITH TIME ZONE
34+
// with the most significnat 52 bits representing the time component and the
35+
// least 12 bits representing the timezone minutes. This is different from
36+
// TIMESTAMP WITH TIMEZONE where the last 12 bits represent the timezone
37+
// offset. The timezone offset minutes are stored by value, encoded in the
38+
// type itself. This allows the type to be used in a timezone-agnostic manner.
39+
//
40+
// The time component is a 52 bit value representing the number of
41+
// milliseconds since midnight in UTC.
42+
43+
int64_t millisUtc = util::unpackMillisUtc(value);
44+
45+
// Ensure time component is within valid range
46+
VELOX_CHECK_GE(millisUtc, 0, "Time component is negative");
47+
VELOX_CHECK_LE(millisUtc, util::kMillisInDay, "Time component is too large");
48+
49+
// TimeZone's are encoded as a 12 bit value.
50+
// This represents a range of -14:00 to +14:00, with 0 representing UTC.
51+
// The range is from -840 to 840 minutes, we thus encode by doing bias
52+
// encoding and taking 840 as the bias.
53+
auto timezoneMinutes = util::unpackZoneKeyId(value);
54+
55+
VELOX_CHECK_GE(timezoneMinutes, 0, "Timezone offset is less than -14:00");
56+
VELOX_CHECK_LE(
57+
timezoneMinutes, 1680, "Timezone offset is greater than +14:00");
58+
59+
// Decode timezone offset from bias-encoded value
60+
int16_t offsetMinutes = util::decodeTimezoneOffset(timezoneMinutes);
61+
auto decodedMinutes = std::abs(offsetMinutes);
62+
63+
const auto isBehindUTCString = (offsetMinutes >= 0) ? "+" : "-";
64+
65+
// Convert UTC time to local time using utility function
66+
// Example: If UTC time is 06:30:00 and timezone is +05:30,
67+
// the local time is 12:00:00
68+
int64_t millisLocal = util::utcToLocalTime(millisUtc, offsetMinutes);
69+
70+
int64_t hours = millisLocal / util::kMillisInHour;
71+
int64_t remainingMs = millisLocal % util::kMillisInHour;
72+
int64_t minutes = remainingMs / util::kMillisInMinute;
73+
remainingMs = remainingMs % util::kMillisInMinute;
74+
int64_t seconds = remainingMs / util::kMillisInSecond;
75+
int64_t millis = remainingMs % util::kMillisInSecond;
76+
77+
int16_t offsetHours = decodedMinutes / util::kMinutesInHour;
78+
int16_t remainingOffsetMinutes = decodedMinutes % util::kMinutesInHour;
79+
80+
fmt::format_to_n(
81+
startPos,
82+
kTimeWithTimezoneToVarcharRowSize,
83+
"{:02d}:{:02d}:{:02d}.{:03d}{}{:02d}:{:02d}",
84+
hours,
85+
minutes,
86+
seconds,
87+
millis,
88+
isBehindUTCString,
89+
offsetHours,
90+
remainingOffsetMinutes);
91+
return StringView{startPos, kTimeWithTimezoneToVarcharRowSize};
92+
}
93+
94+
} // namespace facebook::velox

velox/functions/prestosql/types/fuzzer_utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
velox_add_library(
1515
velox_presto_types_fuzzer_utils
1616
TimestampWithTimeZoneInputGenerator.cpp
17+
TimeWithTimezoneInputGenerator.cpp
1718
HyperLogLogInputGenerator.cpp
1819
P4HyperLogLogInputGenerator.cpp
1920
SfmSketchInputGenerator.cpp
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "velox/functions/prestosql/types/fuzzer_utils/TimeWithTimezoneInputGenerator.h"
18+
19+
#include "velox/common/fuzzer/Utils.h"
20+
#include "velox/functions/prestosql/types/TimeWithTimezoneType.h"
21+
#include "velox/type/Time.h"
22+
#include "velox/type/Variant.h"
23+
24+
namespace facebook::velox::fuzzer {
25+
26+
TimeWithTimezoneInputGenerator::TimeWithTimezoneInputGenerator(
27+
size_t seed,
28+
double nullRatio)
29+
: AbstractInputGenerator(
30+
seed,
31+
TIME_WITH_TIME_ZONE(),
32+
nullptr /*leafGenerator*/,
33+
nullRatio) {}
34+
35+
variant TimeWithTimezoneInputGenerator::generate() {
36+
if (coinToss(rng_, nullRatio_)) {
37+
return variant::null(type_->kind());
38+
}
39+
40+
const int64_t timeMillis = rand<int64_t>(rng_, 0, util::kMillisInDay - 1);
41+
42+
int16_t timezoneOffsetMinutes;
43+
// 25% of the time, pick from frequently used timezone offsets
44+
if (coinToss(rng_, 0.25)) {
45+
timezoneOffsetMinutes = kFrequentlyUsedOffsets[rand<size_t>(
46+
rng_, 0, kFrequentlyUsedOffsets.size() - 1)];
47+
} else {
48+
// rest, pick a random timezone offset for variety
49+
timezoneOffsetMinutes =
50+
rand<int16_t>(rng_, -util::kTimeZoneBias, util::kTimeZoneBias);
51+
}
52+
53+
const int16_t biasEncodedTimezone = util::biasEncode(timezoneOffsetMinutes);
54+
return util::pack(timeMillis, biasEncodedTimezone);
55+
}
56+
57+
} // namespace facebook::velox::fuzzer
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <array>
20+
21+
#include "velox/type/Type.h"
22+
23+
namespace facebook::velox::fuzzer {
24+
25+
/// Input generator for TIME WITH TIME ZONE type.
26+
/// Generates valid TIME WITH TIME ZONE values by combining:
27+
/// - Time component: milliseconds since midnight (0 to 86399999)
28+
/// - Timezone offset: bias-encoded minutes (-840 to 840 -> 0 to 1680)
29+
/// Biases selection toward US timezone offsets (including DST) for better
30+
/// coverage of DST-related edge cases.
31+
class TimeWithTimezoneInputGenerator : public AbstractInputGenerator {
32+
public:
33+
// Frequently used timezone offsets in minutes (US timezones including DST)
34+
static constexpr std::array<int16_t, 7> kFrequentlyUsedOffsets = {
35+
-240, // EDT (Eastern Daylight Time)
36+
-300, // EST (Eastern Standard Time) / CDT (Central Daylight Time)
37+
-360, // CST (Central Standard Time) / MDT (Mountain Daylight Time)
38+
-420, // MST (Mountain Standard Time) / PDT (Pacific Daylight Time)
39+
-480, // PST (Pacific Standard Time) / AKDT (Alaska Daylight Time)
40+
-540, // AKST (Alaska Standard Time) / HDT (Hawaii-Aleutian Daylight Time)
41+
-600, // HST (Hawaii-Aleutian Standard Time)
42+
};
43+
44+
TimeWithTimezoneInputGenerator(size_t seed, double nullRatio);
45+
46+
Variant generate() override;
47+
};
48+
49+
} // namespace facebook::velox::fuzzer

velox/functions/prestosql/types/fuzzer_utils/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
add_executable(
1616
velox_presto_types_fuzzer_utils_test
1717
TimestampWithTimeZoneInputGeneratorTest.cpp
18+
TimeWithTimezoneInputGeneratorTest.cpp
1819
HyperLogLogInputGeneratorTest.cpp
1920
P4HyperLogLogInputGeneratorTest.cpp
2021
SfmSketchInputGeneratorTest.cpp

0 commit comments

Comments
 (0)