This is an automated email from the ASF dual-hosted git repository.

gangwu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new 40834ddd fix: YearTransform return years since 1970 (#495)
40834ddd is described below

commit 40834ddd7c33d8b9a67da3b62d1d132a9c42c129
Author: wzhuo <[email protected]>
AuthorDate: Thu Jan 8 21:52:44 2026 +0800

    fix: YearTransform return years since 1970 (#495)
---
 src/iceberg/test/eval_expr_test.cc |  2 +-
 src/iceberg/test/transform_test.cc | 18 +++++++++++-------
 src/iceberg/transform.h            |  8 ++++----
 src/iceberg/transform_function.h   | 12 ++++++++----
 src/iceberg/util/temporal_util.cc  |  4 ++--
 5 files changed, 26 insertions(+), 18 deletions(-)

diff --git a/src/iceberg/test/eval_expr_test.cc 
b/src/iceberg/test/eval_expr_test.cc
index 880f1ffb..39cb2a97 100644
--- a/src/iceberg/test/eval_expr_test.cc
+++ b/src/iceberg/test/eval_expr_test.cc
@@ -161,7 +161,7 @@ TEST_F(BoundExpressionTest, YearTransform) {
   // Evaluate (2021)
   ICEBERG_UNWRAP_OR_FAIL(auto result, bound_transform->Evaluate(*struct_like));
   EXPECT_FALSE(result.IsNull());
-  EXPECT_EQ(std::get<int32_t>(result.value()), 2021);  // Year value
+  EXPECT_EQ(std::get<int32_t>(result.value()), 2021 - 1970);  // Year value
 }
 
 TEST_F(BoundExpressionTest, MonthTransform) {
diff --git a/src/iceberg/test/transform_test.cc 
b/src/iceberg/test/transform_test.cc
index 7f0514df..47a1e87e 100644
--- a/src/iceberg/test/transform_test.cc
+++ b/src/iceberg/test/transform_test.cc
@@ -459,7 +459,7 @@ INSTANTIATE_TEST_SUITE_P(
                                                                 .hour = 11,
                                                                 .minute = 43,
                                                                 .second = 
20})),
-                       .expected = Literal::Int(2021)},
+                       .expected = Literal::Int(2021 - 1970)},
         TransformParam{
             .str = "TimestampTz",
             // 2021-01-01T07:43:20+08:00, which is 2020-12-31T23:43:20Z
@@ -472,12 +472,12 @@ INSTANTIATE_TEST_SUITE_P(
                                                        .minute = 43,
                                                        .second = 20,
                                                        .tz_offset_minutes = 
480})),
-            .expected = Literal::Int(2020)},
+            .expected = Literal::Int(2020 - 1970)},
         TransformParam{.str = "Date",
                        .source_type = iceberg::date(),
                        .source = Literal::Date(TemporalTestHelper::CreateDate(
                            {.year = 2052, .month = 2, .day = 20})),
-                       .expected = Literal::Int(2052)}),
+                       .expected = Literal::Int(2052 - 1970)}),
     [](const ::testing::TestParamInfo<TransformParam>& info) { return 
info.param.str; });
 
 class MonthTransformTest : public ::testing::TestWithParam<TransformParam> {};
@@ -2061,7 +2061,8 @@ TEST_F(TransformProjectStrictTest, YearStrictLessThan) {
           std::move(projected));
   EXPECT_EQ(unbound_projected->op(), Expression::Operation::kLt);
   EXPECT_EQ(unbound_projected->literals().size(), 1);
-  EXPECT_EQ(std::get<int32_t>(unbound_projected->literals().front().value()), 
2021);
+  EXPECT_EQ(std::get<int32_t>(unbound_projected->literals().front().value()),
+            2021 - 1970);
 }
 
 TEST_F(TransformProjectStrictTest, YearStrictGreaterThanOrEqual) {
@@ -2085,7 +2086,8 @@ TEST_F(TransformProjectStrictTest, 
YearStrictGreaterThanOrEqual) {
           std::move(projected));
   EXPECT_EQ(unbound_projected->op(), Expression::Operation::kGt);
   EXPECT_EQ(unbound_projected->literals().size(), 1);
-  EXPECT_EQ(std::get<int32_t>(unbound_projected->literals().front().value()), 
2020);
+  EXPECT_EQ(std::get<int32_t>(unbound_projected->literals().front().value()),
+            2020 - 1970);
 }
 
 TEST_F(TransformProjectStrictTest, YearStrictNotEqual) {
@@ -2109,7 +2111,8 @@ TEST_F(TransformProjectStrictTest, YearStrictNotEqual) {
           std::move(projected));
   EXPECT_EQ(unbound_projected->op(), Expression::Operation::kNotEq);
   EXPECT_EQ(unbound_projected->literals().size(), 1);
-  EXPECT_EQ(std::get<int32_t>(unbound_projected->literals().front().value()), 
2021);
+  EXPECT_EQ(std::get<int32_t>(unbound_projected->literals().front().value()),
+            2021 - 1970);
 }
 
 TEST_F(TransformProjectStrictTest, MonthStrictLessThan) {
@@ -2218,7 +2221,8 @@ TEST_F(TransformProjectStrictTest, YearStrictUpperBound) {
           std::move(projected));
   EXPECT_EQ(unbound_projected->op(), Expression::Operation::kLt);
   EXPECT_EQ(unbound_projected->literals().size(), 1);
-  EXPECT_EQ(std::get<int32_t>(unbound_projected->literals().front().value()), 
2018);
+  EXPECT_EQ(std::get<int32_t>(unbound_projected->literals().front().value()),
+            2018 - 1970);
 }
 
 TEST_F(TransformProjectStrictTest, VoidStrictReturnsNull) {
diff --git a/src/iceberg/transform.h b/src/iceberg/transform.h
index 1044e264..36da46d9 100644
--- a/src/iceberg/transform.h
+++ b/src/iceberg/transform.h
@@ -111,25 +111,25 @@ class ICEBERG_EXPORT Transform : public util::Formattable 
{
 
   /// \brief Creates a shared singleton instance of the Year transform.
   ///
-  /// Extracts the year portion from a date or timestamp.
+  /// Extracts the number of years from a date or timestamp since the epoch.
   /// \return A shared pointer to the Year transform.
   static std::shared_ptr<Transform> Year();
 
   /// \brief Creates a shared singleton instance of the Month transform.
   ///
-  /// Extracts the month portion from a date or timestamp.
+  /// Extracts the number of months from a date or timestamp since the epoch.
   /// \return A shared pointer to the Month transform.
   static std::shared_ptr<Transform> Month();
 
   /// \brief Creates a shared singleton instance of the Day transform.
   ///
-  /// Extracts the day portion from a date or timestamp.
+  /// Extracts the number of days from a date or timestamp since the epoch.
   /// \return A shared pointer to the Day transform.
   static std::shared_ptr<Transform> Day();
 
   /// \brief Creates a shared singleton instance of the Hour transform.
   ///
-  /// Extracts the hour portion from a timestamp.
+  /// Extracts the number of hours from a timestamp since the epoch.
   /// \return A shared pointer to the Hour transform.
   static std::shared_ptr<Transform> Hour();
 
diff --git a/src/iceberg/transform_function.h b/src/iceberg/transform_function.h
index c8670824..a44e6c7a 100644
--- a/src/iceberg/transform_function.h
+++ b/src/iceberg/transform_function.h
@@ -100,7 +100,8 @@ class ICEBERG_EXPORT TruncateTransform : public 
TransformFunction {
   int32_t width_;
 };
 
-/// \brief Year transform that extracts the year component from timestamp 
inputs.
+/// \brief Year transform that extracts the number of years from timestamp 
inputs since
+/// the epoch.
 class ICEBERG_EXPORT YearTransform : public TransformFunction {
  public:
   /// \param source_type Must be a timestamp type.
@@ -119,7 +120,8 @@ class ICEBERG_EXPORT YearTransform : public 
TransformFunction {
       std::shared_ptr<Type> const& source_type);
 };
 
-/// \brief Month transform that extracts the month component from timestamp 
inputs.
+/// \brief Month transform that extracts the number of months from timestamp 
inputs since
+/// the epoch.
 class ICEBERG_EXPORT MonthTransform : public TransformFunction {
  public:
   /// \param source_type Must be a timestamp type.
@@ -138,7 +140,8 @@ class ICEBERG_EXPORT MonthTransform : public 
TransformFunction {
       std::shared_ptr<Type> const& source_type);
 };
 
-/// \brief Day transform that extracts the day of the month from timestamp 
inputs.
+/// \brief Day transform that extracts the number of days from timestamp 
inputs since the
+/// epoch.
 class ICEBERG_EXPORT DayTransform : public TransformFunction {
  public:
   /// \param source_type Must be a timestamp type.
@@ -161,7 +164,8 @@ class ICEBERG_EXPORT DayTransform : public 
TransformFunction {
       std::shared_ptr<Type> const& source_type);
 };
 
-/// \brief Hour transform that extracts the hour component from timestamp 
inputs.
+/// \brief Hour transform that extracts the number of hours from timestamp 
inputs since
+/// the epoch.
 class ICEBERG_EXPORT HourTransform : public TransformFunction {
  public:
   /// \param source_type Must be a timestamp type.
diff --git a/src/iceberg/util/temporal_util.cc 
b/src/iceberg/util/temporal_util.cc
index 0112e492..05aafb96 100644
--- a/src/iceberg/util/temporal_util.cc
+++ b/src/iceberg/util/temporal_util.cc
@@ -68,14 +68,14 @@ template <>
 Result<Literal> ExtractYearImpl<TypeId::kDate>(const Literal& literal) {
   auto value = std::get<int32_t>(literal.value());
   auto ymd = DateToYmd(value);
-  return Literal::Int(static_cast<int32_t>(ymd.year()));
+  return Literal::Int((ymd.year() - kEpochYmd.year()).count());
 }
 
 template <>
 Result<Literal> ExtractYearImpl<TypeId::kTimestamp>(const Literal& literal) {
   auto value = std::get<int64_t>(literal.value());
   auto ymd = TimestampToYmd(value);
-  return Literal::Int(static_cast<int32_t>(ymd.year()));
+  return Literal::Int((ymd.year() - kEpochYmd.year()).count());
 }
 
 template <>

Reply via email to