From 900e106032da1535cf63c5382f3cc18d6124c3a6 Mon Sep 17 00:00:00 2001 From: Junwang Zhao Date: Sat, 18 Oct 2025 18:59:27 +0800 Subject: [PATCH 1/2] feat: construct temporal values from structural inputs --- src/iceberg/test/CMakeLists.txt | 1 + src/iceberg/test/bucket_util_test.cc | 58 ++++++--- src/iceberg/test/meson.build | 1 + src/iceberg/test/temporal_util_test.cc | 171 +++++++++++++++++++++++++ src/iceberg/test/transform_test.cc | 112 +++++++++++----- src/iceberg/util/temporal_util.cc | 50 ++++++++ src/iceberg/util/temporal_util.h | 33 +++++ 7 files changed, 378 insertions(+), 48 deletions(-) create mode 100644 src/iceberg/test/temporal_util_test.cc diff --git a/src/iceberg/test/CMakeLists.txt b/src/iceberg/test/CMakeLists.txt index 68af62bf1..6f5530a45 100644 --- a/src/iceberg/test/CMakeLists.txt +++ b/src/iceberg/test/CMakeLists.txt @@ -105,6 +105,7 @@ add_iceberg_test(util_test endian_test.cc formatter_test.cc string_util_test.cc + temporal_util_test.cc truncate_util_test.cc uuid_test.cc visit_type_test.cc) diff --git a/src/iceberg/test/bucket_util_test.cc b/src/iceberg/test/bucket_util_test.cc index 69a04ef54..a75ef632f 100644 --- a/src/iceberg/test/bucket_util_test.cc +++ b/src/iceberg/test/bucket_util_test.cc @@ -24,6 +24,7 @@ #include #include "iceberg/util/decimal.h" +#include "iceberg/util/temporal_util.h" #include "iceberg/util/uuid.h" namespace iceberg { @@ -41,27 +42,54 @@ TEST(BucketUtilsTest, HashHelper) { EXPECT_EQ(BucketUtils::HashBytes(decimal->ToBigEndian()), -500754589); // date hash - std::chrono::sys_days sd = std::chrono::year{2017} / 11 / 16; - std::chrono::sys_days epoch{std::chrono::year{1970} / 1 / 1}; - int32_t days = (sd - epoch).count(); - EXPECT_EQ(BucketUtils::HashInt(days), -653330422); + EXPECT_EQ(BucketUtils::HashInt( + TemporalUtils::CreateDate({.year = 2017, .month = 11, .day = 16})), + -653330422); // time - // 22:31:08 in microseconds - int64_t time_micros = (22 * 3600 + 31 * 60 + 8) * 1000000LL; - EXPECT_EQ(BucketUtils::HashLong(time_micros), -662762989); + EXPECT_EQ(BucketUtils::HashLong( + TemporalUtils::CreateTime({.hour = 22, .minute = 31, .second = 8})), + -662762989); // timestamp // 2017-11-16T22:31:08 in microseconds - std::chrono::system_clock::time_point tp = - std::chrono::sys_days{std::chrono::year{2017} / 11 / 16} + std::chrono::hours{22} + - std::chrono::minutes{31} + std::chrono::seconds{8}; - int64_t timestamp_micros = - std::chrono::duration_cast(tp.time_since_epoch()) - .count(); - EXPECT_EQ(BucketUtils::HashLong(timestamp_micros), -2047944441); + EXPECT_EQ( + BucketUtils::HashLong(TemporalUtils::CreateTimestamp( + {.year = 2017, .month = 11, .day = 16, .hour = 22, .minute = 31, .second = 8})), + -2047944441); + // 2017-11-16T22:31:08.000001 in microseconds - EXPECT_EQ(BucketUtils::HashLong(timestamp_micros + 1), -1207196810); + EXPECT_EQ(BucketUtils::HashLong(TemporalUtils::CreateTimestamp({.year = 2017, + .month = 11, + .day = 16, + .hour = 22, + .minute = 31, + .second = 8, + .microsecond = 1})), + -1207196810); + + // 2017-11-16T14:31:08-08:00 in microseconds + EXPECT_EQ(BucketUtils::HashLong( + TemporalUtils::CreateTimestampTz({.year = 2017, + .month = 11, + .day = 16, + .hour = 14, + .minute = 31, + .second = 8, + .tz_offset_minutes = -480})), + -2047944441); + + // 2017-11-16T14:31:08.000001-08:00 in microseconds + EXPECT_EQ(BucketUtils::HashLong( + TemporalUtils::CreateTimestampTz({.year = 2017, + .month = 11, + .day = 16, + .hour = 14, + .minute = 31, + .second = 8, + .microsecond = 1, + .tz_offset_minutes = -480})), + -1207196810); // string std::string str = "iceberg"; diff --git a/src/iceberg/test/meson.build b/src/iceberg/test/meson.build index 88b16325c..0343c1d7d 100644 --- a/src/iceberg/test/meson.build +++ b/src/iceberg/test/meson.build @@ -74,6 +74,7 @@ iceberg_tests = { 'endian_test.cc', 'formatter_test.cc', 'string_util_test.cc', + 'temporal_util_test.cc', 'truncate_util_test.cc', 'uuid_test.cc', 'visit_type_test.cc', diff --git a/src/iceberg/test/temporal_util_test.cc b/src/iceberg/test/temporal_util_test.cc new file mode 100644 index 000000000..f4a211b34 --- /dev/null +++ b/src/iceberg/test/temporal_util_test.cc @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "iceberg/util/temporal_util.h" + +#include + +namespace iceberg { + +TEST(TemporalUtilTest, CreateDate) { + EXPECT_EQ(TemporalUtils::CreateDate({.year = 1970, .month = 1, .day = 1}), 0); + EXPECT_EQ(TemporalUtils::CreateDate({.year = 1970, .month = 1, .day = 2}), 1); + EXPECT_EQ(TemporalUtils::CreateDate({.year = 1969, .month = 12, .day = 31}), -1); + EXPECT_EQ(TemporalUtils::CreateDate({.year = 2000, .month = 1, .day = 1}), 10957); + EXPECT_EQ(TemporalUtils::CreateDate({.year = 2017, .month = 11, .day = 16}), 17486); + EXPECT_EQ(TemporalUtils::CreateDate({.year = 2052, .month = 2, .day = 20}), 30000); +} + +TEST(TemporalUtilTest, CreateTime) { + EXPECT_EQ(TemporalUtils::CreateTime({.hour = 0, .minute = 0, .second = 0}), 0); + EXPECT_EQ(TemporalUtils::CreateTime({.hour = 1, .minute = 0, .second = 0}), + 3600000000LL); + EXPECT_EQ(TemporalUtils::CreateTime({.hour = 0, .minute = 1, .second = 0}), 60000000LL); + EXPECT_EQ(TemporalUtils::CreateTime({.hour = 0, .minute = 0, .second = 1}), 1000000LL); + EXPECT_EQ(TemporalUtils::CreateTime({.hour = 22, .minute = 31, .second = 8}), + 81068000000LL); + EXPECT_EQ(TemporalUtils::CreateTime({.hour = 23, .minute = 59, .second = 59}), + 86399000000LL); + EXPECT_EQ( + TemporalUtils::CreateTime({.hour = 0, .minute = 0, .second = 0, .microsecond = 1}), + 1LL); + EXPECT_EQ(TemporalUtils::CreateTime( + {.hour = 0, .minute = 0, .second = 0, .microsecond = 999999}), + 999999LL); + EXPECT_EQ( + TemporalUtils::CreateTime({.hour = 0, .minute = 0, .second = 1, .microsecond = 1}), + 1000001LL); + EXPECT_EQ(TemporalUtils::CreateTime( + {.hour = 23, .minute = 59, .second = 59, .microsecond = 999999}), + 86399999999LL); +} + +TEST(TemporalUtilTest, CreateTimestamp) { + EXPECT_EQ( + TemporalUtils::CreateTimestamp( + {.year = 1970, .month = 1, .day = 1, .hour = 0, .minute = 0, .second = 0}), + 0LL); + EXPECT_EQ( + TemporalUtils::CreateTimestamp( + {.year = 1970, .month = 1, .day = 2, .hour = 0, .minute = 0, .second = 0}), + 86400000000LL); + EXPECT_EQ( + TemporalUtils::CreateTimestamp( + {.year = 1969, .month = 12, .day = 31, .hour = 23, .minute = 59, .second = 59}), + -1000000LL); + EXPECT_EQ( + TemporalUtils::CreateTimestamp( + {.year = 2000, .month = 1, .day = 1, .hour = 0, .minute = 0, .second = 0}), + 946684800000000LL); + EXPECT_EQ( + TemporalUtils::CreateTimestamp( + {.year = 2017, .month = 11, .day = 16, .hour = 22, .minute = 31, .second = 8}), + 1510871468000000LL); + EXPECT_EQ(TemporalUtils::CreateTimestamp({.year = 2017, + .month = 11, + .day = 16, + .hour = 22, + .minute = 31, + .second = 8, + .microsecond = 1}), + 1510871468000001LL); + EXPECT_EQ(TemporalUtils::CreateTimestamp({.year = 2023, + .month = 10, + .day = 5, + .hour = 15, + .minute = 45, + .second = 30, + .microsecond = 123456}), + 1696520730123456LL); +} + +TEST(TemporalUtilTest, CreateTimestampTz) { + EXPECT_EQ(TemporalUtils::CreateTimestampTz({.year = 2017, + .month = 11, + .day = 16, + .hour = 14, + .minute = 31, + .second = 8, + .tz_offset_minutes = -480}), + 1510871468000000LL); + EXPECT_EQ(TemporalUtils::CreateTimestampTz({.year = 2017, + .month = 11, + .day = 16, + .hour = 14, + .minute = 31, + .second = 8, + .microsecond = 1, + .tz_offset_minutes = -480}), + 1510871468000001LL); + EXPECT_EQ(TemporalUtils::CreateTimestampTz({.year = 2023, + .month = 10, + .day = 5, + .hour = 15, + .minute = 45, + .second = 30, + .microsecond = 123456, + .tz_offset_minutes = 60}), + 1696517130123456LL); + EXPECT_EQ(TemporalUtils::CreateTimestampTz({.year = 2023, + .month = 10, + .day = 5, + .hour = 15, + .minute = 45, + .second = 30, + .microsecond = 123456, + .tz_offset_minutes = -60}), + 1696524330123456LL); +} + +TEST(TemporalUtilTest, CreateTimestampNanos) { + EXPECT_EQ( + TemporalUtils::CreateTimestampNanos( + {.year = 2017, .month = 11, .day = 16, .hour = 22, .minute = 31, .second = 8}), + 1510871468000000000LL); + EXPECT_EQ(TemporalUtils::CreateTimestampNanos({.year = 2017, + .month = 11, + .day = 16, + .hour = 22, + .minute = 31, + .second = 8, + .nanosecond = 1}), + 1510871468000000001LL); +} + +TEST(TemporalUtilTest, CreateTimestampTzNanos) { + EXPECT_EQ(TemporalUtils::CreateTimestampTzNanos({.year = 2017, + .month = 11, + .day = 16, + .hour = 14, + .minute = 31, + .second = 8, + .tz_offset_minutes = -480}), + 1510871468000000000LL); + EXPECT_EQ(TemporalUtils::CreateTimestampTzNanos({.year = 2017, + .month = 11, + .day = 16, + .hour = 14, + .minute = 31, + .second = 8, + .nanosecond = 1, + .tz_offset_minutes = -480}), + 1510871468000000001LL); +} + +} // namespace iceberg diff --git a/src/iceberg/test/transform_test.cc b/src/iceberg/test/transform_test.cc index 1003b9532..fdc915cf1 100644 --- a/src/iceberg/test/transform_test.cc +++ b/src/iceberg/test/transform_test.cc @@ -27,9 +27,9 @@ #include #include "iceberg/expression/literal.h" -#include "iceberg/transform_function.h" #include "iceberg/type.h" #include "iceberg/util/formatter.h" // IWYU pragma: keep +#include "iceberg/util/temporal_util.h" #include "matchers.h" namespace iceberg { @@ -315,24 +315,38 @@ INSTANTIATE_TEST_SUITE_P( .source = Literal::Decimal(1420, 4, 2), .expected = Literal::Int(3)}, TransformParam{.str = "Date", - // 2017-11-16 .source_type = iceberg::date(), - .source = Literal::Date(17486), + .source = Literal::Date(TemporalUtils::CreateDate( + {.year = 2017, .month = 11, .day = 16})), .expected = Literal::Int(2)}, TransformParam{.str = "Time", - // 22:31:08 in microseconds .source_type = iceberg::time(), - .source = Literal::Time(81068000000), + .source = Literal::Time(TemporalUtils::CreateTime( + {.hour = 22, .minute = 31, .second = 8})), .expected = Literal::Int(3)}, TransformParam{.str = "Timestamp", // 2017-11-16T22:31:08 in microseconds .source_type = iceberg::timestamp(), - .source = Literal::Timestamp(1510871468000000), + .source = Literal::Timestamp( + TemporalUtils::CreateTimestamp({.year = 2017, + .month = 11, + .day = 16, + .hour = 22, + .minute = 31, + .second = 8})), .expected = Literal::Int(3)}, TransformParam{.str = "TimestampTz", - // 2017-11-16T22:31:08.000001 in microseconds + // 2017-11-16T14:31:08.000001-08:00 in microseconds .source_type = iceberg::timestamp_tz(), - .source = Literal::TimestampTz(1510871468000001), + .source = Literal::TimestampTz( + TemporalUtils::CreateTimestampTz({.year = 2017, + .month = 11, + .day = 16, + .hour = 14, + .minute = 31, + .second = 8, + .microsecond = 1, + .tz_offset_minutes = -480})), .expected = Literal::Int(2)}, TransformParam{.str = "String", .source_type = iceberg::string(), @@ -428,19 +442,35 @@ TEST_P(YearTransformTest, YearTransform) { INSTANTIATE_TEST_SUITE_P( YearTransformTests, YearTransformTest, - ::testing::Values(TransformParam{.str = "Timestamp", - // 2021-06-01T11:43:20Z - .source_type = iceberg::timestamp(), - .source = Literal::Timestamp(1622547800000000), - .expected = Literal::Int(2021)}, - TransformParam{.str = "TimestampTz", - .source_type = iceberg::timestamp_tz(), - .source = Literal::TimestampTz(1622547800000000), - .expected = Literal::Int(2021)}, - TransformParam{.str = "Date", - .source_type = iceberg::date(), - .source = Literal::Date(30000), - .expected = Literal::Int(2052)}), + ::testing::Values( + TransformParam{ + .str = "Timestamp", + // 2021-06-01T11:43:20Z + .source_type = iceberg::timestamp(), + .source = Literal::Timestamp(TemporalUtils::CreateTimestamp({.year = 2021, + .month = 6, + .day = 1, + .hour = 11, + .minute = 43, + .second = 20})), + .expected = Literal::Int(2021)}, + TransformParam{.str = "TimestampTz", + // 2021-01-01T07:43:20+08:00, which is 2020-12-31T23:43:20Z + .source_type = iceberg::timestamp_tz(), + .source = Literal::TimestampTz( + TemporalUtils::CreateTimestampTz({.year = 2021, + .month = 1, + .day = 1, + .hour = 7, + .minute = 43, + .second = 20, + .tz_offset_minutes = 480})), + .expected = Literal::Int(2020)}, + TransformParam{.str = "Date", + .source_type = iceberg::date(), + .source = Literal::Date(TemporalUtils::CreateDate( + {.year = 2052, .month = 2, .day = 20})), + .expected = Literal::Int(2052)}), [](const ::testing::TestParamInfo& info) { return info.param.str; }); class MonthTransformTest : public ::testing::TestWithParam {}; @@ -495,18 +525,34 @@ TEST_P(DayTransformTest, DayTransform) { INSTANTIATE_TEST_SUITE_P( DayTransformTests, DayTransformTest, - ::testing::Values(TransformParam{.str = "Timestamp", - .source_type = iceberg::timestamp(), - .source = Literal::Timestamp(1622547800000000), - .expected = Literal::Int(18779)}, - TransformParam{.str = "TimestampTz", - .source_type = iceberg::timestamp_tz(), - .source = Literal::TimestampTz(1622547800000000), - .expected = Literal::Int(18779)}, - TransformParam{.str = "Date", - .source_type = iceberg::date(), - .source = Literal::Date(30000), - .expected = Literal::Int(30000)}), + ::testing::Values( + TransformParam{ + .str = "Timestamp", + .source_type = iceberg::timestamp(), + .source = Literal::Timestamp(TemporalUtils::CreateTimestamp({.year = 2021, + .month = 6, + .day = 1, + .hour = 11, + .minute = 43, + .second = 20})), + .expected = Literal::Int( + TemporalUtils::CreateDate({.year = 2021, .month = 6, .day = 1}))}, + TransformParam{.str = "TimestampTz", + .source_type = iceberg::timestamp_tz(), + .source = Literal::TimestampTz( + TemporalUtils::CreateTimestampTz({.year = 2021, + .month = 1, + .day = 1, + .hour = 7, + .minute = 43, + .second = 20, + .tz_offset_minutes = 480})), + .expected = Literal::Int(TemporalUtils::CreateDate( + {.year = 2020, .month = 12, .day = 31}))}, + TransformParam{.str = "Date", + .source_type = iceberg::date(), + .source = Literal::Date(30000), + .expected = Literal::Int(30000)}), [](const ::testing::TestParamInfo& info) { return info.param.str; }); class HourTransformTest : public ::testing::TestWithParam {}; diff --git a/src/iceberg/util/temporal_util.cc b/src/iceberg/util/temporal_util.cc index 41748c920..e3af9343f 100644 --- a/src/iceberg/util/temporal_util.cc +++ b/src/iceberg/util/temporal_util.cc @@ -20,6 +20,7 @@ #include "iceberg/util/temporal_util.h" #include +#include #include #include "iceberg/expression/literal.h" @@ -236,4 +237,53 @@ Result TemporalUtils::ExtractHour(const Literal& literal) { } } +int32_t TemporalUtils::CreateDate(const TemporalParts& parts) { + year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; + auto days_since_epoch = sys_days(ymd) - kEpochDays; + return static_cast(days_since_epoch.count()); +} + +int64_t TemporalUtils::CreateTime(const TemporalParts& parts) { + return duration_cast(hours(parts.hour) + minutes(parts.minute) + + seconds(parts.second) + + microseconds(parts.microsecond)) + .count(); +} + +int64_t TemporalUtils::CreateTimestamp(const TemporalParts& parts) { + year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; + auto tp = + sys_time{(sys_days(ymd) + hours{parts.hour} + minutes{parts.minute} + + seconds{parts.second} + microseconds{parts.microsecond}) + .time_since_epoch()}; + return tp.time_since_epoch().count(); +} + +int64_t TemporalUtils::CreateTimestampTz(const TemporalParts& parts) { + year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; + auto tp = sys_time{ + (sys_days(ymd) + hours{parts.hour} + minutes{parts.minute} + seconds{parts.second} + + microseconds{parts.microsecond} - minutes{parts.tz_offset_minutes}) + .time_since_epoch()}; + return tp.time_since_epoch().count(); +} + +int64_t TemporalUtils::CreateTimestampNanos(const TemporalParts& parts) { + year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; + auto tp = + sys_time{(sys_days(ymd) + hours{parts.hour} + minutes{parts.minute} + + seconds{parts.second} + nanoseconds{parts.nanosecond}) + .time_since_epoch()}; + return tp.time_since_epoch().count(); +} + +int64_t TemporalUtils::CreateTimestampTzNanos(const TemporalParts& parts) { + year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; + auto tp = sys_time{ + (sys_days(ymd) + hours{parts.hour} + minutes{parts.minute} + seconds{parts.second} + + nanoseconds{parts.nanosecond} - minutes{parts.tz_offset_minutes}) + .time_since_epoch()}; + return tp.time_since_epoch().count(); +} + } // namespace iceberg diff --git a/src/iceberg/util/temporal_util.h b/src/iceberg/util/temporal_util.h index 750c3d8b7..37101cd00 100644 --- a/src/iceberg/util/temporal_util.h +++ b/src/iceberg/util/temporal_util.h @@ -19,12 +19,27 @@ #pragma once +#include + #include "iceberg/iceberg_export.h" #include "iceberg/result.h" #include "iceberg/type_fwd.h" namespace iceberg { +struct ICEBERG_EXPORT TemporalParts { + int32_t year{0}; + uint16_t month{0}; + uint16_t day{0}; + int32_t hour{0}; + int32_t minute{0}; + int32_t second{0}; + int32_t microsecond{0}; + int32_t nanosecond{0}; + // e.g. -480 for PST (UTC-8:00), +480 for Asia/Shanghai (UTC+8:00) + int32_t tz_offset_minutes; +}; + class ICEBERG_EXPORT TemporalUtils { public: /// \brief Extract a date or timestamp year, as years from 1970 @@ -38,6 +53,24 @@ class ICEBERG_EXPORT TemporalUtils { /// \brief Extract a timestamp hour, as hours from 1970-01-01 00:00:00 static Result ExtractHour(const Literal& literal); + + /// \brief Construct a Calendar date without timezone or time + static int32_t CreateDate(const TemporalParts& parts); + + /// \brief Construct a time-of-day, microsecond precision, without date, timezone + static int64_t CreateTime(const TemporalParts& parts); + + /// \brief Construct a timestamp, microsecond precision, without timezone + static int64_t CreateTimestamp(const TemporalParts& parts); + + /// \brief Construct a timestamp, microsecond precision, with timezone + static int64_t CreateTimestampTz(const TemporalParts& parts); + + /// \brief Construct a timestamp, nanosecond precision, without timezone + static int64_t CreateTimestampNanos(const TemporalParts& parts); + + /// \brief Construct a timestamp, nanosecond precision, with timezone + static int64_t CreateTimestampTzNanos(const TemporalParts& parts); }; } // namespace iceberg From a4138ec57a6691268f662770d6fead4fcf9a2f27 Mon Sep 17 00:00:00 2001 From: Junwang Zhao Date: Tue, 21 Oct 2025 23:15:11 +0800 Subject: [PATCH 2/2] resolve review comments --- src/iceberg/test/CMakeLists.txt | 1 - src/iceberg/test/bucket_util_test.cc | 55 ++++---- src/iceberg/test/meson.build | 1 - src/iceberg/test/temporal_test_helper.h | 128 ++++++++++++++++++ src/iceberg/test/temporal_util_test.cc | 171 ------------------------ src/iceberg/test/transform_test.cc | 135 ++++++++++--------- src/iceberg/util/temporal_util.cc | 49 ------- src/iceberg/util/temporal_util.h | 33 ----- 8 files changed, 225 insertions(+), 348 deletions(-) create mode 100644 src/iceberg/test/temporal_test_helper.h delete mode 100644 src/iceberg/test/temporal_util_test.cc diff --git a/src/iceberg/test/CMakeLists.txt b/src/iceberg/test/CMakeLists.txt index 6f5530a45..68af62bf1 100644 --- a/src/iceberg/test/CMakeLists.txt +++ b/src/iceberg/test/CMakeLists.txt @@ -105,7 +105,6 @@ add_iceberg_test(util_test endian_test.cc formatter_test.cc string_util_test.cc - temporal_util_test.cc truncate_util_test.cc uuid_test.cc visit_type_test.cc) diff --git a/src/iceberg/test/bucket_util_test.cc b/src/iceberg/test/bucket_util_test.cc index a75ef632f..8c80f04c2 100644 --- a/src/iceberg/test/bucket_util_test.cc +++ b/src/iceberg/test/bucket_util_test.cc @@ -24,8 +24,8 @@ #include #include "iceberg/util/decimal.h" -#include "iceberg/util/temporal_util.h" #include "iceberg/util/uuid.h" +#include "temporal_test_helper.h" namespace iceberg { @@ -43,52 +43,53 @@ TEST(BucketUtilsTest, HashHelper) { // date hash EXPECT_EQ(BucketUtils::HashInt( - TemporalUtils::CreateDate({.year = 2017, .month = 11, .day = 16})), + TemporalTestHelper::CreateDate({.year = 2017, .month = 11, .day = 16})), -653330422); // time EXPECT_EQ(BucketUtils::HashLong( - TemporalUtils::CreateTime({.hour = 22, .minute = 31, .second = 8})), + TemporalTestHelper::CreateTime({.hour = 22, .minute = 31, .second = 8})), -662762989); // timestamp // 2017-11-16T22:31:08 in microseconds EXPECT_EQ( - BucketUtils::HashLong(TemporalUtils::CreateTimestamp( + BucketUtils::HashLong(TemporalTestHelper::CreateTimestamp( {.year = 2017, .month = 11, .day = 16, .hour = 22, .minute = 31, .second = 8})), -2047944441); // 2017-11-16T22:31:08.000001 in microseconds - EXPECT_EQ(BucketUtils::HashLong(TemporalUtils::CreateTimestamp({.year = 2017, - .month = 11, - .day = 16, - .hour = 22, - .minute = 31, - .second = 8, - .microsecond = 1})), - -1207196810); + EXPECT_EQ( + BucketUtils::HashLong(TemporalTestHelper::CreateTimestamp({.year = 2017, + .month = 11, + .day = 16, + .hour = 22, + .minute = 31, + .second = 8, + .microsecond = 1})), + -1207196810); // 2017-11-16T14:31:08-08:00 in microseconds EXPECT_EQ(BucketUtils::HashLong( - TemporalUtils::CreateTimestampTz({.year = 2017, - .month = 11, - .day = 16, - .hour = 14, - .minute = 31, - .second = 8, - .tz_offset_minutes = -480})), + TemporalTestHelper::CreateTimestampTz({.year = 2017, + .month = 11, + .day = 16, + .hour = 14, + .minute = 31, + .second = 8, + .tz_offset_minutes = -480})), -2047944441); // 2017-11-16T14:31:08.000001-08:00 in microseconds EXPECT_EQ(BucketUtils::HashLong( - TemporalUtils::CreateTimestampTz({.year = 2017, - .month = 11, - .day = 16, - .hour = 14, - .minute = 31, - .second = 8, - .microsecond = 1, - .tz_offset_minutes = -480})), + TemporalTestHelper::CreateTimestampTz({.year = 2017, + .month = 11, + .day = 16, + .hour = 14, + .minute = 31, + .second = 8, + .microsecond = 1, + .tz_offset_minutes = -480})), -1207196810); // string diff --git a/src/iceberg/test/meson.build b/src/iceberg/test/meson.build index 0343c1d7d..88b16325c 100644 --- a/src/iceberg/test/meson.build +++ b/src/iceberg/test/meson.build @@ -74,7 +74,6 @@ iceberg_tests = { 'endian_test.cc', 'formatter_test.cc', 'string_util_test.cc', - 'temporal_util_test.cc', 'truncate_util_test.cc', 'uuid_test.cc', 'visit_type_test.cc', diff --git a/src/iceberg/test/temporal_test_helper.h b/src/iceberg/test/temporal_test_helper.h new file mode 100644 index 000000000..0f2904891 --- /dev/null +++ b/src/iceberg/test/temporal_test_helper.h @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#pragma once + +#include +#include + +namespace iceberg { + +using namespace std::chrono; // NOLINT + +struct DateParts { + int32_t year{0}; + uint8_t month{0}; + uint8_t day{0}; +}; + +struct TimeParts { + int32_t hour{0}; + int32_t minute{0}; + int32_t second{0}; + int32_t microsecond{0}; +}; + +struct TimestampParts { + int32_t year{0}; + uint8_t month{0}; + uint8_t day{0}; + int32_t hour{0}; + int32_t minute{0}; + int32_t second{0}; + int32_t microsecond{0}; + // e.g. -480 for PST (UTC-8:00), +480 for Asia/Shanghai (UTC+8:00) + int32_t tz_offset_minutes{0}; +}; + +struct TimestampNanosParts { + int32_t year{0}; + uint8_t month{0}; + uint8_t day{0}; + int32_t hour{0}; + int32_t minute{0}; + int32_t second{0}; + int32_t nanosecond{0}; + // e.g. -480 for PST (UTC-8:00), +480 for Asia/Shanghai (UTC+8:00) + int32_t tz_offset_minutes{0}; +}; + +class TemporalTestHelper { + static constexpr auto kEpochDays = sys_days(year{1970} / January / 1); + + public: + /// \brief Construct a Calendar date without timezone or time + static int32_t CreateDate(const DateParts& parts) { + return static_cast( + (sys_days(year{parts.year} / month{parts.month} / day{parts.day}) - kEpochDays) + .count()); + } + + /// \brief Construct a time-of-day, microsecond precision, without date, timezone + static int64_t CreateTime(const TimeParts& parts) { + return duration_cast(hours(parts.hour) + minutes(parts.minute) + + seconds(parts.second) + + microseconds(parts.microsecond)) + .count(); + } + + /// \brief Construct a timestamp, microsecond precision, without timezone + static int64_t CreateTimestamp(const TimestampParts& parts) { + year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; + auto tp = sys_time{(sys_days(ymd) + hours{parts.hour} + + minutes{parts.minute} + seconds{parts.second} + + microseconds{parts.microsecond}) + .time_since_epoch()}; + return tp.time_since_epoch().count(); + } + + /// \brief Construct a timestamp, microsecond precision, with timezone + static int64_t CreateTimestampTz(const TimestampParts& parts) { + year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; + auto tp = sys_time{(sys_days(ymd) + hours{parts.hour} + + minutes{parts.minute} + seconds{parts.second} + + microseconds{parts.microsecond} - + minutes{parts.tz_offset_minutes}) + .time_since_epoch()}; + return tp.time_since_epoch().count(); + } + + /// \brief Construct a timestamp, nanosecond precision, without timezone + static int64_t CreateTimestampNanos(const TimestampNanosParts& parts) { + year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; + auto tp = + sys_time{(sys_days(ymd) + hours{parts.hour} + minutes{parts.minute} + + seconds{parts.second} + nanoseconds{parts.nanosecond}) + .time_since_epoch()}; + return tp.time_since_epoch().count(); + } + + /// \brief Construct a timestamp, nanosecond precision, with timezone + static int64_t CreateTimestampTzNanos(const TimestampNanosParts& parts) { + year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; + auto tp = + sys_time{(sys_days(ymd) + hours{parts.hour} + minutes{parts.minute} + + seconds{parts.second} + nanoseconds{parts.nanosecond} - + minutes{parts.tz_offset_minutes}) + .time_since_epoch()}; + return tp.time_since_epoch().count(); + } +}; + +} // namespace iceberg diff --git a/src/iceberg/test/temporal_util_test.cc b/src/iceberg/test/temporal_util_test.cc deleted file mode 100644 index f4a211b34..000000000 --- a/src/iceberg/test/temporal_util_test.cc +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include "iceberg/util/temporal_util.h" - -#include - -namespace iceberg { - -TEST(TemporalUtilTest, CreateDate) { - EXPECT_EQ(TemporalUtils::CreateDate({.year = 1970, .month = 1, .day = 1}), 0); - EXPECT_EQ(TemporalUtils::CreateDate({.year = 1970, .month = 1, .day = 2}), 1); - EXPECT_EQ(TemporalUtils::CreateDate({.year = 1969, .month = 12, .day = 31}), -1); - EXPECT_EQ(TemporalUtils::CreateDate({.year = 2000, .month = 1, .day = 1}), 10957); - EXPECT_EQ(TemporalUtils::CreateDate({.year = 2017, .month = 11, .day = 16}), 17486); - EXPECT_EQ(TemporalUtils::CreateDate({.year = 2052, .month = 2, .day = 20}), 30000); -} - -TEST(TemporalUtilTest, CreateTime) { - EXPECT_EQ(TemporalUtils::CreateTime({.hour = 0, .minute = 0, .second = 0}), 0); - EXPECT_EQ(TemporalUtils::CreateTime({.hour = 1, .minute = 0, .second = 0}), - 3600000000LL); - EXPECT_EQ(TemporalUtils::CreateTime({.hour = 0, .minute = 1, .second = 0}), 60000000LL); - EXPECT_EQ(TemporalUtils::CreateTime({.hour = 0, .minute = 0, .second = 1}), 1000000LL); - EXPECT_EQ(TemporalUtils::CreateTime({.hour = 22, .minute = 31, .second = 8}), - 81068000000LL); - EXPECT_EQ(TemporalUtils::CreateTime({.hour = 23, .minute = 59, .second = 59}), - 86399000000LL); - EXPECT_EQ( - TemporalUtils::CreateTime({.hour = 0, .minute = 0, .second = 0, .microsecond = 1}), - 1LL); - EXPECT_EQ(TemporalUtils::CreateTime( - {.hour = 0, .minute = 0, .second = 0, .microsecond = 999999}), - 999999LL); - EXPECT_EQ( - TemporalUtils::CreateTime({.hour = 0, .minute = 0, .second = 1, .microsecond = 1}), - 1000001LL); - EXPECT_EQ(TemporalUtils::CreateTime( - {.hour = 23, .minute = 59, .second = 59, .microsecond = 999999}), - 86399999999LL); -} - -TEST(TemporalUtilTest, CreateTimestamp) { - EXPECT_EQ( - TemporalUtils::CreateTimestamp( - {.year = 1970, .month = 1, .day = 1, .hour = 0, .minute = 0, .second = 0}), - 0LL); - EXPECT_EQ( - TemporalUtils::CreateTimestamp( - {.year = 1970, .month = 1, .day = 2, .hour = 0, .minute = 0, .second = 0}), - 86400000000LL); - EXPECT_EQ( - TemporalUtils::CreateTimestamp( - {.year = 1969, .month = 12, .day = 31, .hour = 23, .minute = 59, .second = 59}), - -1000000LL); - EXPECT_EQ( - TemporalUtils::CreateTimestamp( - {.year = 2000, .month = 1, .day = 1, .hour = 0, .minute = 0, .second = 0}), - 946684800000000LL); - EXPECT_EQ( - TemporalUtils::CreateTimestamp( - {.year = 2017, .month = 11, .day = 16, .hour = 22, .minute = 31, .second = 8}), - 1510871468000000LL); - EXPECT_EQ(TemporalUtils::CreateTimestamp({.year = 2017, - .month = 11, - .day = 16, - .hour = 22, - .minute = 31, - .second = 8, - .microsecond = 1}), - 1510871468000001LL); - EXPECT_EQ(TemporalUtils::CreateTimestamp({.year = 2023, - .month = 10, - .day = 5, - .hour = 15, - .minute = 45, - .second = 30, - .microsecond = 123456}), - 1696520730123456LL); -} - -TEST(TemporalUtilTest, CreateTimestampTz) { - EXPECT_EQ(TemporalUtils::CreateTimestampTz({.year = 2017, - .month = 11, - .day = 16, - .hour = 14, - .minute = 31, - .second = 8, - .tz_offset_minutes = -480}), - 1510871468000000LL); - EXPECT_EQ(TemporalUtils::CreateTimestampTz({.year = 2017, - .month = 11, - .day = 16, - .hour = 14, - .minute = 31, - .second = 8, - .microsecond = 1, - .tz_offset_minutes = -480}), - 1510871468000001LL); - EXPECT_EQ(TemporalUtils::CreateTimestampTz({.year = 2023, - .month = 10, - .day = 5, - .hour = 15, - .minute = 45, - .second = 30, - .microsecond = 123456, - .tz_offset_minutes = 60}), - 1696517130123456LL); - EXPECT_EQ(TemporalUtils::CreateTimestampTz({.year = 2023, - .month = 10, - .day = 5, - .hour = 15, - .minute = 45, - .second = 30, - .microsecond = 123456, - .tz_offset_minutes = -60}), - 1696524330123456LL); -} - -TEST(TemporalUtilTest, CreateTimestampNanos) { - EXPECT_EQ( - TemporalUtils::CreateTimestampNanos( - {.year = 2017, .month = 11, .day = 16, .hour = 22, .minute = 31, .second = 8}), - 1510871468000000000LL); - EXPECT_EQ(TemporalUtils::CreateTimestampNanos({.year = 2017, - .month = 11, - .day = 16, - .hour = 22, - .minute = 31, - .second = 8, - .nanosecond = 1}), - 1510871468000000001LL); -} - -TEST(TemporalUtilTest, CreateTimestampTzNanos) { - EXPECT_EQ(TemporalUtils::CreateTimestampTzNanos({.year = 2017, - .month = 11, - .day = 16, - .hour = 14, - .minute = 31, - .second = 8, - .tz_offset_minutes = -480}), - 1510871468000000000LL); - EXPECT_EQ(TemporalUtils::CreateTimestampTzNanos({.year = 2017, - .month = 11, - .day = 16, - .hour = 14, - .minute = 31, - .second = 8, - .nanosecond = 1, - .tz_offset_minutes = -480}), - 1510871468000000001LL); -} - -} // namespace iceberg diff --git a/src/iceberg/test/transform_test.cc b/src/iceberg/test/transform_test.cc index fdc915cf1..6d72bdce9 100644 --- a/src/iceberg/test/transform_test.cc +++ b/src/iceberg/test/transform_test.cc @@ -29,8 +29,8 @@ #include "iceberg/expression/literal.h" #include "iceberg/type.h" #include "iceberg/util/formatter.h" // IWYU pragma: keep -#include "iceberg/util/temporal_util.h" #include "matchers.h" +#include "temporal_test_helper.h" namespace iceberg { @@ -316,38 +316,39 @@ INSTANTIATE_TEST_SUITE_P( .expected = Literal::Int(3)}, TransformParam{.str = "Date", .source_type = iceberg::date(), - .source = Literal::Date(TemporalUtils::CreateDate( + .source = Literal::Date(TemporalTestHelper::CreateDate( {.year = 2017, .month = 11, .day = 16})), .expected = Literal::Int(2)}, TransformParam{.str = "Time", .source_type = iceberg::time(), - .source = Literal::Time(TemporalUtils::CreateTime( + .source = Literal::Time(TemporalTestHelper::CreateTime( {.hour = 22, .minute = 31, .second = 8})), .expected = Literal::Int(3)}, TransformParam{.str = "Timestamp", // 2017-11-16T22:31:08 in microseconds .source_type = iceberg::timestamp(), .source = Literal::Timestamp( - TemporalUtils::CreateTimestamp({.year = 2017, - .month = 11, - .day = 16, - .hour = 22, - .minute = 31, - .second = 8})), + TemporalTestHelper::CreateTimestamp({.year = 2017, + .month = 11, + .day = 16, + .hour = 22, + .minute = 31, + .second = 8})), .expected = Literal::Int(3)}, - TransformParam{.str = "TimestampTz", - // 2017-11-16T14:31:08.000001-08:00 in microseconds - .source_type = iceberg::timestamp_tz(), - .source = Literal::TimestampTz( - TemporalUtils::CreateTimestampTz({.year = 2017, - .month = 11, - .day = 16, - .hour = 14, - .minute = 31, - .second = 8, - .microsecond = 1, - .tz_offset_minutes = -480})), - .expected = Literal::Int(2)}, + TransformParam{ + .str = "TimestampTz", + // 2017-11-16T14:31:08.000001-08:00 in microseconds + .source_type = iceberg::timestamp_tz(), + .source = Literal::TimestampTz( + TemporalTestHelper::CreateTimestampTz({.year = 2017, + .month = 11, + .day = 16, + .hour = 14, + .minute = 31, + .second = 8, + .microsecond = 1, + .tz_offset_minutes = -480})), + .expected = Literal::Int(2)}, TransformParam{.str = "String", .source_type = iceberg::string(), .source = Literal::String("iceberg"), @@ -443,32 +444,33 @@ TEST_P(YearTransformTest, YearTransform) { INSTANTIATE_TEST_SUITE_P( YearTransformTests, YearTransformTest, ::testing::Values( + TransformParam{.str = "Timestamp", + // 2021-06-01T11:43:20Z + .source_type = iceberg::timestamp(), + .source = Literal::Timestamp( + TemporalTestHelper::CreateTimestamp({.year = 2021, + .month = 6, + .day = 1, + .hour = 11, + .minute = 43, + .second = 20})), + .expected = Literal::Int(2021)}, TransformParam{ - .str = "Timestamp", - // 2021-06-01T11:43:20Z - .source_type = iceberg::timestamp(), - .source = Literal::Timestamp(TemporalUtils::CreateTimestamp({.year = 2021, - .month = 6, - .day = 1, - .hour = 11, - .minute = 43, - .second = 20})), - .expected = Literal::Int(2021)}, - TransformParam{.str = "TimestampTz", - // 2021-01-01T07:43:20+08:00, which is 2020-12-31T23:43:20Z - .source_type = iceberg::timestamp_tz(), - .source = Literal::TimestampTz( - TemporalUtils::CreateTimestampTz({.year = 2021, - .month = 1, - .day = 1, - .hour = 7, - .minute = 43, - .second = 20, - .tz_offset_minutes = 480})), - .expected = Literal::Int(2020)}, + .str = "TimestampTz", + // 2021-01-01T07:43:20+08:00, which is 2020-12-31T23:43:20Z + .source_type = iceberg::timestamp_tz(), + .source = Literal::TimestampTz( + TemporalTestHelper::CreateTimestampTz({.year = 2021, + .month = 1, + .day = 1, + .hour = 7, + .minute = 43, + .second = 20, + .tz_offset_minutes = 480})), + .expected = Literal::Int(2020)}, TransformParam{.str = "Date", .source_type = iceberg::date(), - .source = Literal::Date(TemporalUtils::CreateDate( + .source = Literal::Date(TemporalTestHelper::CreateDate( {.year = 2052, .month = 2, .day = 20})), .expected = Literal::Int(2052)}), [](const ::testing::TestParamInfo& info) { return info.param.str; }); @@ -526,29 +528,30 @@ TEST_P(DayTransformTest, DayTransform) { INSTANTIATE_TEST_SUITE_P( DayTransformTests, DayTransformTest, ::testing::Values( + TransformParam{.str = "Timestamp", + .source_type = iceberg::timestamp(), + .source = Literal::Timestamp( + TemporalTestHelper::CreateTimestamp({.year = 2021, + .month = 6, + .day = 1, + .hour = 11, + .minute = 43, + .second = 20})), + .expected = Literal::Int(TemporalTestHelper::CreateDate( + {.year = 2021, .month = 6, .day = 1}))}, TransformParam{ - .str = "Timestamp", - .source_type = iceberg::timestamp(), - .source = Literal::Timestamp(TemporalUtils::CreateTimestamp({.year = 2021, - .month = 6, - .day = 1, - .hour = 11, - .minute = 43, - .second = 20})), + .str = "TimestampTz", + .source_type = iceberg::timestamp_tz(), + .source = Literal::TimestampTz( + TemporalTestHelper::CreateTimestampTz({.year = 2021, + .month = 1, + .day = 1, + .hour = 7, + .minute = 43, + .second = 20, + .tz_offset_minutes = 480})), .expected = Literal::Int( - TemporalUtils::CreateDate({.year = 2021, .month = 6, .day = 1}))}, - TransformParam{.str = "TimestampTz", - .source_type = iceberg::timestamp_tz(), - .source = Literal::TimestampTz( - TemporalUtils::CreateTimestampTz({.year = 2021, - .month = 1, - .day = 1, - .hour = 7, - .minute = 43, - .second = 20, - .tz_offset_minutes = 480})), - .expected = Literal::Int(TemporalUtils::CreateDate( - {.year = 2020, .month = 12, .day = 31}))}, + TemporalTestHelper::CreateDate({.year = 2020, .month = 12, .day = 31}))}, TransformParam{.str = "Date", .source_type = iceberg::date(), .source = Literal::Date(30000), diff --git a/src/iceberg/util/temporal_util.cc b/src/iceberg/util/temporal_util.cc index e3af9343f..0112e4925 100644 --- a/src/iceberg/util/temporal_util.cc +++ b/src/iceberg/util/temporal_util.cc @@ -237,53 +237,4 @@ Result TemporalUtils::ExtractHour(const Literal& literal) { } } -int32_t TemporalUtils::CreateDate(const TemporalParts& parts) { - year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; - auto days_since_epoch = sys_days(ymd) - kEpochDays; - return static_cast(days_since_epoch.count()); -} - -int64_t TemporalUtils::CreateTime(const TemporalParts& parts) { - return duration_cast(hours(parts.hour) + minutes(parts.minute) + - seconds(parts.second) + - microseconds(parts.microsecond)) - .count(); -} - -int64_t TemporalUtils::CreateTimestamp(const TemporalParts& parts) { - year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; - auto tp = - sys_time{(sys_days(ymd) + hours{parts.hour} + minutes{parts.minute} + - seconds{parts.second} + microseconds{parts.microsecond}) - .time_since_epoch()}; - return tp.time_since_epoch().count(); -} - -int64_t TemporalUtils::CreateTimestampTz(const TemporalParts& parts) { - year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; - auto tp = sys_time{ - (sys_days(ymd) + hours{parts.hour} + minutes{parts.minute} + seconds{parts.second} + - microseconds{parts.microsecond} - minutes{parts.tz_offset_minutes}) - .time_since_epoch()}; - return tp.time_since_epoch().count(); -} - -int64_t TemporalUtils::CreateTimestampNanos(const TemporalParts& parts) { - year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; - auto tp = - sys_time{(sys_days(ymd) + hours{parts.hour} + minutes{parts.minute} + - seconds{parts.second} + nanoseconds{parts.nanosecond}) - .time_since_epoch()}; - return tp.time_since_epoch().count(); -} - -int64_t TemporalUtils::CreateTimestampTzNanos(const TemporalParts& parts) { - year_month_day ymd{year{parts.year}, month{parts.month}, day{parts.day}}; - auto tp = sys_time{ - (sys_days(ymd) + hours{parts.hour} + minutes{parts.minute} + seconds{parts.second} + - nanoseconds{parts.nanosecond} - minutes{parts.tz_offset_minutes}) - .time_since_epoch()}; - return tp.time_since_epoch().count(); -} - } // namespace iceberg diff --git a/src/iceberg/util/temporal_util.h b/src/iceberg/util/temporal_util.h index 37101cd00..750c3d8b7 100644 --- a/src/iceberg/util/temporal_util.h +++ b/src/iceberg/util/temporal_util.h @@ -19,27 +19,12 @@ #pragma once -#include - #include "iceberg/iceberg_export.h" #include "iceberg/result.h" #include "iceberg/type_fwd.h" namespace iceberg { -struct ICEBERG_EXPORT TemporalParts { - int32_t year{0}; - uint16_t month{0}; - uint16_t day{0}; - int32_t hour{0}; - int32_t minute{0}; - int32_t second{0}; - int32_t microsecond{0}; - int32_t nanosecond{0}; - // e.g. -480 for PST (UTC-8:00), +480 for Asia/Shanghai (UTC+8:00) - int32_t tz_offset_minutes; -}; - class ICEBERG_EXPORT TemporalUtils { public: /// \brief Extract a date or timestamp year, as years from 1970 @@ -53,24 +38,6 @@ class ICEBERG_EXPORT TemporalUtils { /// \brief Extract a timestamp hour, as hours from 1970-01-01 00:00:00 static Result ExtractHour(const Literal& literal); - - /// \brief Construct a Calendar date without timezone or time - static int32_t CreateDate(const TemporalParts& parts); - - /// \brief Construct a time-of-day, microsecond precision, without date, timezone - static int64_t CreateTime(const TemporalParts& parts); - - /// \brief Construct a timestamp, microsecond precision, without timezone - static int64_t CreateTimestamp(const TemporalParts& parts); - - /// \brief Construct a timestamp, microsecond precision, with timezone - static int64_t CreateTimestampTz(const TemporalParts& parts); - - /// \brief Construct a timestamp, nanosecond precision, without timezone - static int64_t CreateTimestampNanos(const TemporalParts& parts); - - /// \brief Construct a timestamp, nanosecond precision, with timezone - static int64_t CreateTimestampTzNanos(const TemporalParts& parts); }; } // namespace iceberg