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

raulcd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new 6287272f39 GH-50464: [C++][Python] Simplify arrow_to_pandas DateOffset 
handling for nanoseconds/milliseconds (#50465)
6287272f39 is described below

commit 6287272f39b8ebef02ca3ec8cf51650e4b240231
Author: Raúl Cumplido <[email protected]>
AuthorDate: Thu Jul 23 10:28:47 2026 +0200

    GH-50464: [C++][Python] Simplify arrow_to_pandas DateOffset handling for 
nanoseconds/milliseconds (#50465)
    
    ### Rationale for this change
    
    Pandas 1.4 fixed the following:
    - https://github.com/pandas-dev/pandas/issues/43892
    
    Which made us special case microseconds extracting those from our 
`MonthDayNano` and manually building `microseconds`.
    
    `DateOffset` can be created with the following:
    ```python
    DateOffset(years=1, months=1,
                       days=1, seconds=1, microseconds=1,
                       minutes=1, hours=1, weeks=1, nanoseconds=1)
    ```
    We currently only roundtrip Month, Day, Microseconds and Nano due to our 
MonthDayNano data type. We special cased microseconds but we were not special 
casing years, seconds, minutos, hours or weeks.
    
    As the initial issue that made us special case has been solved upstream we 
can stop roundtripping microseconds and just return nanoseconds simplifying our 
codebase.
    
    ### What changes are included in this PR?
    
    Remove the unnecessary code to retrieve microseconds from nanoseconds.
    Update test as microseconds won't be returned, only nanoseconds.
    
    ### Are these changes tested?
    
    Yes via CI
    
    ### Are there any user-facing changes?
    
    Yes, this is a change of the UX.
    
    * GitHub Issue: #50464
    
    Authored-by: Raúl Cumplido <[email protected]>
    Signed-off-by: Raúl Cumplido <[email protected]>
---
 python/pyarrow/src/arrow/python/arrow_to_pandas.cc | 25 +++++++---------------
 python/pyarrow/tests/test_array.py                 | 12 +++++------
 python/pyarrow/tests/test_pandas.py                |  3 +--
 3 files changed, 14 insertions(+), 26 deletions(-)

diff --git a/python/pyarrow/src/arrow/python/arrow_to_pandas.cc 
b/python/pyarrow/src/arrow/python/arrow_to_pandas.cc
index 348d352a04..3cf6da31a8 100644
--- a/python/pyarrow/src/arrow/python/arrow_to_pandas.cc
+++ b/python/pyarrow/src/arrow/python/arrow_to_pandas.cc
@@ -1292,24 +1292,15 @@ struct ObjectWriterVisitor {
     auto to_date_offset = [&](const MonthDayNanoIntervalType::MonthDayNanos& 
interval,
                               PyObject** out) {
       ARROW_DCHECK(internal::BorrowPandasDataOffsetType() != nullptr);
-      // DateOffset objects do not add nanoseconds component to pd.Timestamp.
-      // as of Pandas 1.3.3
-      // (https://github.com/pandas-dev/pandas/issues/43892).
-      // So convert microseconds and remainder to preserve data
-      // but give users more expected results.
-      int64_t microseconds = interval.nanoseconds / 1000;
-      int64_t nanoseconds;
-      if (interval.nanoseconds >= 0) {
-        nanoseconds = interval.nanoseconds % 1000;
-      } else {
-        nanoseconds = -((-interval.nanoseconds) % 1000);
-      }
 
-      PyDict_SetItemString(kwargs.obj(), "months", 
PyLong_FromLong(interval.months));
-      PyDict_SetItemString(kwargs.obj(), "days", 
PyLong_FromLong(interval.days));
-      PyDict_SetItemString(kwargs.obj(), "microseconds",
-                           PyLong_FromLongLong(microseconds));
-      PyDict_SetItemString(kwargs.obj(), "nanoseconds", 
PyLong_FromLongLong(nanoseconds));
+      OwnedRef months(PyLong_FromLong(interval.months));
+      OwnedRef days(PyLong_FromLong(interval.days));
+      OwnedRef nanoseconds(PyLong_FromLongLong(interval.nanoseconds));
+      RETURN_IF_PYERROR();
+      PyDict_SetItemString(kwargs.obj(), "months", months.obj());
+      PyDict_SetItemString(kwargs.obj(), "days", days.obj());
+      PyDict_SetItemString(kwargs.obj(), "nanoseconds", nanoseconds.obj());
+      RETURN_IF_PYERROR();
       *out =
           PyObject_Call(internal::BorrowPandasDataOffsetType(), args.obj(), 
kwargs.obj());
       RETURN_IF_PYERROR();
diff --git a/python/pyarrow/tests/test_array.py 
b/python/pyarrow/tests/test_array.py
index c1e3f0128b..a26481c575 100644
--- a/python/pyarrow/tests/test_array.py
+++ b/python/pyarrow/tests/test_array.py
@@ -2820,11 +2820,10 @@ def test_interval_array_from_relativedelta():
     assert arr.equals(expected)
     assert arr.to_pandas().tolist() == [
         None, DateOffset(months=13, days=8,
-                         microseconds=(
+                         nanoseconds=(
                              datetime.timedelta(seconds=1, microseconds=1,
                                                 minutes=1, hours=1) //
-                             datetime.timedelta(microseconds=1)),
-                         nanoseconds=0)]
+                             datetime.timedelta(microseconds=1)) * 1000)]
     with pytest.raises(ValueError):
         pa.array([DateOffset(years=((1 << 32) // 12), months=100)])
     with pytest.raises(ValueError):
@@ -2872,12 +2871,11 @@ def test_interval_array_from_dateoffset():
     assert arr.equals(expected)
     expected_from_pandas = [
         None, DateOffset(months=13, days=8,
-                         microseconds=(
+                         nanoseconds=(
                              datetime.timedelta(seconds=1, microseconds=1,
                                                 minutes=1, hours=1) //
-                             datetime.timedelta(microseconds=1)),
-                         nanoseconds=1),
-        DateOffset(months=0, days=0, microseconds=0, nanoseconds=0)]
+                             datetime.timedelta(microseconds=1) * 1000) + 1),
+        DateOffset(months=0, days=0, nanoseconds=0)]
 
     assert arr.to_pandas().tolist() == expected_from_pandas
 
diff --git a/python/pyarrow/tests/test_pandas.py 
b/python/pyarrow/tests/test_pandas.py
index c3153e82b7..4b144448ce 100644
--- a/python/pyarrow/tests/test_pandas.py
+++ b/python/pyarrow/tests/test_pandas.py
@@ -1733,8 +1733,7 @@ class TestConvertDateTimeLikeTypes:
         from pandas.tseries.offsets import DateOffset
         df = pd.DataFrame({
             'date_offset': [None,
-                            DateOffset(days=3600, months=3600, microseconds=3,
-                                       nanoseconds=600)]
+                            DateOffset(days=3600, months=3600, 
nanoseconds=3600)]
         })
         schema = pa.schema([('date_offset', pa.month_day_nano_interval())])
         _check_pandas_roundtrip(

Reply via email to