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

Gabriel39 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 2c697190ccd [fix](be) Correct Arrow timestamps before Unix epoch in 
DATETIMEV2 deserialization (#65689)
2c697190ccd is described below

commit 2c697190ccd5861af8680d07debf94a3eea509ae
Author: zhangstar333 <[email protected]>
AuthorDate: Mon Jul 20 14:21:35 2026 +0800

    [fix](be) Correct Arrow timestamps before Unix epoch in DATETIMEV2 
deserialization (#65689)
    
    ### What problem does this PR solve?
    Problem Summary:
    
    brefore: auto utc_epoch = static_cast<**UInt64**>(date_value);
    
    But Arrow timestamps are signed integers. For example, 1969-12-31
    23:59:59.123456 UTC is represented as -876544 in timestamp[us]. After
    conversion to UInt64, it becomes a very large positive value, causing an
    invalid DATETIMEV2 result.
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [ ] Regression test
        - [x] Unit Test
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason <!-- Add your reason?  -->
    
    - Behavior changed:
        - [x] No.
        - [ ] Yes. <!-- Explain the behavior change -->
    
    - Does this need documentation?
        - [x] No.
    - [ ] Yes. <!-- Add document PR link here. eg:
    https://github.com/apache/doris-website/pull/1214 -->
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label <!-- Add branch pick label that this PR
    should merge into -->
---
 .../data_type_serde/data_type_datetimev2_serde.cpp | 20 ++++++++++-----
 .../data_type_serde_datetime_v2_test.cpp           | 29 ++++++++++++++++++++++
 .../spark_connector/spark_connector_arrow.out      | 15 ++++++-----
 3 files changed, 50 insertions(+), 14 deletions(-)

diff --git a/be/src/core/data_type_serde/data_type_datetimev2_serde.cpp 
b/be/src/core/data_type_serde/data_type_datetimev2_serde.cpp
index 44402bb5b53..e91e596ab1c 100644
--- a/be/src/core/data_type_serde/data_type_datetimev2_serde.cpp
+++ b/be/src/core/data_type_serde/data_type_datetimev2_serde.cpp
@@ -525,16 +525,24 @@ Status 
DataTypeDateTimeV2SerDe::read_column_from_arrow(IColumn& column,
         for (auto value_i = start; value_i < end; ++value_i) {
             const uint8_t* raw_byte_ptr = base_ptr + value_i * element_size;
             auto date_value = unaligned_load<int64_t>(raw_byte_ptr);
-            auto utc_epoch = static_cast<UInt64>(date_value);
 
             DateV2Value<DateTimeV2ValueType> v;
-            // convert second
-            v.from_unixtime(utc_epoch / divisor, ctz);
-            // get rest time
+            // C++ integer division truncates toward zero. Normalize the 
remainder so a negative
+            // timestamp still has a non-negative fractional part, e.g. 
-876544us becomes
+            // -1 second and 123456us.
+            int64_t seconds = date_value / divisor;
+            int64_t remainder = date_value % divisor;
+            if (remainder < 0) {
+                --seconds;
+                remainder += divisor;
+            }
+            v.from_unixtime(seconds, ctz);
+            // Get the fractional part.
             // add 0 on the right to make it 6 digits. DateTimeV2Value 
microsecond is 6 digits,
             // the scale decides to keep the first few digits, so the valid 
digits should be kept at the front.
-            // "2022-01-01 11:11:11.111", utc_epoch = 1641035471111, divisor = 
1000, set_microsecond(111000)
-            v.set_microsecond((utc_epoch % divisor) * DIVISOR_FOR_MICRO / 
divisor);
+            // "2022-01-01 11:11:11.111", timestamp = 1641035471111, divisor = 
1000,
+            // set_microsecond(111000)
+            v.set_microsecond(remainder * DIVISOR_FOR_MICRO / divisor);
             col_data.emplace_back(v);
         }
     } else {
diff --git a/be/test/core/data_type_serde/data_type_serde_datetime_v2_test.cpp 
b/be/test/core/data_type_serde/data_type_serde_datetime_v2_test.cpp
index 3769e5f6946..dbe9c875834 100644
--- a/be/test/core/data_type_serde/data_type_serde_datetime_v2_test.cpp
+++ b/be/test/core/data_type_serde/data_type_serde_datetime_v2_test.cpp
@@ -366,4 +366,33 @@ TEST_F(DataTypeDateTimeV2SerDeTest, 
ArrowMemNotAlignedDateTime) {
     EXPECT_TRUE(st.ok());
 }
 
+TEST_F(DataTypeDateTimeV2SerDeTest, ReadArrowTimestampBeforeEpoch) {
+    auto source_column = ColumnDateTimeV2::create();
+    DateV2Value<DateTimeV2ValueType> source_value;
+    source_value.unchecked_set_time(1969, 12, 31, 23, 59, 59, 123456);
+    source_column->insert_value(source_value);
+    std::string insert_value = "1969-12-31 23:59:59.123456";
+    ASSERT_EQ(insert_value, source_value.to_string(6));
+
+    arrow::TimestampBuilder builder(arrow::timestamp(arrow::TimeUnit::MICRO),
+                                    arrow::default_memory_pool());
+    ASSERT_TRUE(serde_datetime_v2_6
+                        ->write_column_to_arrow(*source_column, nullptr, 
&builder, 0,
+                                                source_column->size(), 
cctz::utc_time_zone())
+                        .ok());
+
+    std::shared_ptr<arrow::Array> array;
+    ASSERT_TRUE(builder.Finish(&array).ok());
+    const auto* timestamp_array = assert_cast<const 
arrow::TimestampArray*>(array.get());
+    ASSERT_EQ(-876544, timestamp_array->Value(0));
+
+    auto dest_column = ColumnDateTimeV2::create();
+    ASSERT_TRUE(serde_datetime_v2_6
+                        ->read_column_from_arrow(*dest_column, array.get(), 0, 
array->length(),
+                                                 cctz::utc_time_zone())
+                        .ok());
+    ASSERT_EQ(1, dest_column->size());
+    EXPECT_EQ(insert_value, dest_column->get_element(0).to_string(6));
+}
+
 } // namespace doris
diff --git 
a/regression-test/data/connector_p0/spark_connector/spark_connector_arrow.out 
b/regression-test/data/connector_p0/spark_connector/spark_connector_arrow.out
index 69d0c6629f0..0c7f078f812 100644
--- 
a/regression-test/data/connector_p0/spark_connector/spark_connector_arrow.out
+++ 
b/regression-test/data/connector_p0/spark_connector/spark_connector_arrow.out
@@ -18,13 +18,13 @@
 1      [1, 0, 0, 1, 1] [1, 2, 3]       [2, 12, 32]     [3, 4, 5, 6]    [4, 5, 
6]       [123456789, 987654321, 123789456]       [6.6, 6.7, 7.8] [7.7, 
8.800000000000001, 8.899999618530273]     [3.12000, 1.12345]      
["2023-09-08", "2027-10-28"]    ["2023-09-08 17:12:34.123456", "2024-09-08 
18:12:34.123456"]    ["char", "char2"]       ["varchar", "varchar2"] ["string", 
"string2"]
 
 -- !q03 --
-1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"":"2023-09-08 
17:12:34.123456", "3023-09-08 17:12:34.123456":"4023-09-08 17:12:34.123456"}    
{"char":"char2", "char2":"char3"}       {"varchar":"varchar2", 
"varchar3":"varchar4"}   {"string":"string2", "string3":"string4"}
-1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"":"2023-09-08 
17:12:34.123456", "3023-09-08 17:12:34.123456":"4023-09-08 17:12:34.123456"}    
{"char":"char2", "char2":"char3"}       {"varchar":"varchar2", 
"varchar3":"varchar4"}   {"string":"string2", "string3":"string4"}
-1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"":"2023-09-08 
17:12:34.123456", "3023-09-08 17:12:34.123456":"4023-09-08 17:12:34.123456"}    
{"char":"char2", "char2":"char3"}       {"varchar":"varchar2", 
"varchar3":"varchar4"}   {"string":"string2", "string3":"string4"}
-1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"":"2023-09-08 
17:12:34.123456", "3023-09-08 17:12:34.123456":"4023-09-08 17:12:34.123456"}    
{"char":"char2", "char2":"char3"}       {"varchar":"varchar2", 
"varchar3":"varchar4"}   {"string":"string2", "string3":"string4"}
-1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"":"2023-09-08 
17:12:34.123456", "3023-09-08 17:12:34.123456":"4023-09-08 17:12:34.123456"}    
{"char":"char2", "char2":"char3"}       {"varchar":"varchar2", 
"varchar3":"varchar4"}   {"string":"string2", "string3":"string4"}
-1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"":"2023-09-08 
17:12:34.123456", "3023-09-08 17:12:34.123456":"4023-09-08 17:12:34.123456"}    
{"char":"char2", "char2":"char3"}       {"varchar":"varchar2", 
"varchar3":"varchar4"}   {"string":"string2", "string3":"string4"}
-1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"":"2023-09-08 
17:12:34.123456", "3023-09-08 17:12:34.123456":"4023-09-08 17:12:34.123456"}    
{"char":"char2", "char2":"char3"}       {"varchar":"varchar2", 
"varchar3":"varchar4"}   {"string":"string2", "string3":"string4"}
+1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"1023-09-08 
17:06:51.123456":"2023-09-08 17:12:34.123456", "3023-09-08 
17:12:34.123456":"4023-09-08 17:12:34.123456"}  {"char":"char2", 
"char2":"char3"}       {"varchar":"varchar2", "varchar3":"varchar4"}   
{"string":"string2", "string3":"string4"}
+1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"1023-09-08 
17:06:51.123456":"2023-09-08 17:12:34.123456", "3023-09-08 
17:12:34.123456":"4023-09-08 17:12:34.123456"}  {"char":"char2", 
"char2":"char3"}       {"varchar":"varchar2", "varchar3":"varchar4"}   
{"string":"string2", "string3":"string4"}
+1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"1023-09-08 
17:06:51.123456":"2023-09-08 17:12:34.123456", "3023-09-08 
17:12:34.123456":"4023-09-08 17:12:34.123456"}  {"char":"char2", 
"char2":"char3"}       {"varchar":"varchar2", "varchar3":"varchar4"}   
{"string":"string2", "string3":"string4"}
+1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"1023-09-08 
17:06:51.123456":"2023-09-08 17:12:34.123456", "3023-09-08 
17:12:34.123456":"4023-09-08 17:12:34.123456"}  {"char":"char2", 
"char2":"char3"}       {"varchar":"varchar2", "varchar3":"varchar4"}   
{"string":"string2", "string3":"string4"}
+1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"1023-09-08 
17:06:51.123456":"2023-09-08 17:12:34.123456", "3023-09-08 
17:12:34.123456":"4023-09-08 17:12:34.123456"}  {"char":"char2", 
"char2":"char3"}       {"varchar":"varchar2", "varchar3":"varchar4"}   
{"string":"string2", "string3":"string4"}
+1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"1023-09-08 
17:06:51.123456":"2023-09-08 17:12:34.123456", "3023-09-08 
17:12:34.123456":"4023-09-08 17:12:34.123456"}  {"char":"char2", 
"char2":"char3"}       {"varchar":"varchar2", "varchar3":"varchar4"}   
{"string":"string2", "string3":"string4"}
+1      {1:1, 0:1}      {1:2, 3:4}      {2:4, 5:6}      {3:4, 7:8}      {4:5, 
1:2}      {123456789:987654321, 789456123:456789123}      {6.6:8.8, 9.9:10.1}   
  {7.7:1.1, 2.2:3.3}      {3.12000:1.23000, 2.34000:5.67000}      
{"2023-09-08":"2024-09-08", "1023-09-08":"2023-09-08"}  {"1023-09-08 
17:06:51.123456":"2023-09-08 17:12:34.123456", "3023-09-08 
17:12:34.123456":"4023-09-08 17:12:34.123456"}  {"char":"char2", 
"char2":"char3"}       {"varchar":"varchar2", "varchar3":"varchar4"}   
{"string":"string2", "string3":"string4"}
 
 -- !q04 --
 1      {"c_bool":1, "c_tinyint":1, "c_smallint":2, "c_int":3, "c_bigint":4, 
"c_largeint":123456789, "c_float":6.6, "c_double":7.7, "c_decimal":3.12000, 
"c_date":"2023-09-08", "c_datetime":"2023-09-08 17:12:34.123456", 
"c_char":"char", "c_varchar":"varchar", "c_string":"string"}
@@ -34,4 +34,3 @@
 1      {"c_bool":1, "c_tinyint":1, "c_smallint":2, "c_int":3, "c_bigint":4, 
"c_largeint":123456789, "c_float":6.6, "c_double":7.7, "c_decimal":3.12000, 
"c_date":"2023-09-08", "c_datetime":"2023-09-08 17:12:34.123456", 
"c_char":"char", "c_varchar":"varchar", "c_string":"string"}
 1      {"c_bool":1, "c_tinyint":1, "c_smallint":2, "c_int":3, "c_bigint":4, 
"c_largeint":123456789, "c_float":6.6, "c_double":7.7, "c_decimal":3.12000, 
"c_date":"2023-09-08", "c_datetime":"2023-09-08 17:12:34.123456", 
"c_char":"char", "c_varchar":"varchar", "c_string":"string"}
 1      {"c_bool":1, "c_tinyint":1, "c_smallint":2, "c_int":3, "c_bigint":4, 
"c_largeint":123456789, "c_float":6.6, "c_double":7.7, "c_decimal":3.12000, 
"c_date":"2023-09-08", "c_datetime":"2023-09-08 17:12:34.123456", 
"c_char":"char", "c_varchar":"varchar", "c_string":"string"}
-


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to