diff --git a/python/pyarrow/src/arrow/python/arrow_to_pandas.cc b/python/pyarrow/src/arrow/python/arrow_to_pandas.cc index 6e12bb1962db1..f3cee6c65e781 100644 --- a/python/pyarrow/src/arrow/python/arrow_to_pandas.cc +++ b/python/pyarrow/src/arrow/python/arrow_to_pandas.cc @@ -1074,11 +1074,23 @@ struct ObjectWriterVisitor { auto ConvertTimezoneAware = [&](typename Type::c_type value, PyObject** out) { PyObject* naive_datetime; RETURN_NOT_OK(ConvertTimezoneNaive(value, &naive_datetime)); + // convert the timezone naive datetime object to timezone aware - *out = PyObject_CallMethod(tzinfo.obj(), "fromutc", "O", naive_datetime); + // two step conversion of the datetime mimics Python's code: + // dt.replace(tzinfo=datetime.timezone.utc).astimezone(tzinfo) + // first step: replacing timezone with timezone.utc (replace method) + OwnedRef args(PyTuple_New(0)); + OwnedRef keywords(PyDict_New()); + PyDict_SetItemString(keywords.obj(), "tzinfo", PyDateTime_TimeZone_UTC); + OwnedRef naive_datetime_replace(PyObject_GetAttrString(naive_datetime, "replace")); + OwnedRef datetime_utc(PyObject_Call(naive_datetime_replace.obj(), args.obj(), keywords.obj())); + // second step: adjust the datetime to tzinfo timezone (astimezone method) + *out = PyObject_CallMethod(datetime_utc.obj(), "astimezone", "O", tzinfo.obj()); + // the timezone naive object is no longer required Py_DECREF(naive_datetime); RETURN_IF_PYERROR(); + return Status::OK(); }; diff --git a/python/pyarrow/tests/test_pandas.py b/python/pyarrow/tests/test_pandas.py index f843904f12646..22deb63306bce 100644 --- a/python/pyarrow/tests/test_pandas.py +++ b/python/pyarrow/tests/test_pandas.py @@ -4472,6 +4472,18 @@ def test_timestamp_as_object_non_nanosecond(resolution, tz, dt): assert result[0] == expected +def test_timestamp_as_object_fixed_offset(): + # ARROW-16547 to_pandas with timestamp_as_object=True and FixedOffset + pytz = pytest.importorskip("pytz") + import datetime + timezone = pytz.FixedOffset(120) + dt = timezone.localize(datetime.datetime(2022, 5, 12, 16, 57)) + + table = pa.table({"timestamp_col": pa.array([dt])}) + result = table.to_pandas(timestamp_as_object=True) + assert pa.table(result) == table + + def test_threaded_pandas_import(): invoke_script("pandas_threaded_import.py")