diff --git a/cpp/src/arrow/pretty_print_test.cc b/cpp/src/arrow/pretty_print_test.cc index 0db6ae48672..5d2256e8c5d 100644 --- a/cpp/src/arrow/pretty_print_test.cc +++ b/cpp/src/arrow/pretty_print_test.cc @@ -350,10 +350,10 @@ TEST_F(TestPrettyPrint, DateTimeTypes) { std::vector values = { 0, 1, 2, 678 + 1000000 * (5 + 60 * (4 + 60 * (3 + 24 * int64_t(1)))), 4}; static const char* expected = R"expected([ - 1970-01-01 00:00:00.000000, - 1970-01-01 00:00:00.000001, + 1970-01-01 00:00:00.000000Z, + 1970-01-01 00:00:00.000001Z, null, - 1970-01-02 03:04:05.000678, + 1970-01-02 03:04:05.000678Z, null ])expected"; CheckPrimitive(timestamp(TimeUnit::MICRO, "Transylvania"), diff --git a/cpp/src/arrow/util/formatting.h b/cpp/src/arrow/util/formatting.h index 9dcc6463fb7..71bae74629e 100644 --- a/cpp/src/arrow/util/formatting.h +++ b/cpp/src/arrow/util/formatting.h @@ -470,7 +470,8 @@ class StringFormatter { using value_type = int64_t; explicit StringFormatter(const DataType* type) - : unit_(checked_cast(*type).unit()) {} + : unit_(checked_cast(*type).unit()), + timezone_(checked_cast(*type).timezone()) {} template Return operator()(Duration, value_type value, Appender&& append) { @@ -503,6 +504,9 @@ class StringFormatter { std::array buffer; char* cursor = buffer.data() + buffer_size; + if (timezone_.size() > 0) { + detail::FormatOneChar('Z', &cursor); + } detail::FormatHH_MM_SS(arrow_vendored::date::make_time(since_midnight), &cursor); detail::FormatOneChar(' ', &cursor); detail::FormatYYYY_MM_DD(timepoint_days, &cursor); @@ -516,6 +520,7 @@ class StringFormatter { private: TimeUnit::type unit_; + std::string timezone_; }; template diff --git a/cpp/src/arrow/util/formatting_util_test.cc b/cpp/src/arrow/util/formatting_util_test.cc index 9afbc91063a..13f57a495d6 100644 --- a/cpp/src/arrow/util/formatting_util_test.cc +++ b/cpp/src/arrow/util/formatting_util_test.cc @@ -522,6 +522,34 @@ TEST(Formatting, Timestamp) { AssertFormatting(formatter, -2203932304LL * 1000000000LL + 8, "1900-02-28 12:34:56.000000008"); } + + { + auto timestamp_types = {timestamp(TimeUnit::SECOND, "US/Eastern"), + timestamp(TimeUnit::SECOND, "+01:00")}; + for (auto ty : timestamp_types) { + StringFormatter formatter(ty.get()); + + AssertFormatting(formatter, 0, "1970-01-01 00:00:00Z"); + } + } + + { + auto ty = timestamp(TimeUnit::MILLI, "Pacific/Maruesas"); + StringFormatter formatter(ty.get()); + AssertFormatting(formatter, 0, "1970-01-01 00:00:00.000Z"); + } + + { + auto ty = timestamp(TimeUnit::MICRO, "-42:00"); + StringFormatter formatter(ty.get()); + AssertFormatting(formatter, 0, "1970-01-01 00:00:00.000000Z"); + } + + { + auto ty = timestamp(TimeUnit::NANO, "Mars/Mariner_Valley"); + StringFormatter formatter(ty.get()); + AssertFormatting(formatter, 0, "1970-01-01 00:00:00.000000000Z"); + } } TEST(Formatting, Interval) { diff --git a/python/pyarrow/tests/test_types.py b/python/pyarrow/tests/test_types.py index 7600f1dd332..c8a52c6b626 100644 --- a/python/pyarrow/tests/test_types.py +++ b/python/pyarrow/tests/test_types.py @@ -487,6 +487,17 @@ def test_timestamp(): pa.timestamp(invalid_unit) +def test_timestamp_print(): + for unit in ('s', 'ms', 'us', 'ns'): + for tz in ('UTC', 'Europe/Paris', 'Pacific/Marquesas', + 'Mars/Mariner_Valley', '-00:42', '+42:00'): + ty = pa.timestamp(unit, tz=tz) + arr = pa.array([0], ty) + assert "Z" in str(arr) + arr = pa.array([0], pa.timestamp(unit)) + assert "Z" not in str(arr) + + def test_time32_units(): for valid_unit in ('s', 'ms'): ty = pa.time32(valid_unit)