diff --git a/cpp/src/arrow/pretty_print_test.cc b/cpp/src/arrow/pretty_print_test.cc index 547dfd8217ae7..42995de32e0a9 100644 --- a/cpp/src/arrow/pretty_print_test.cc +++ b/cpp/src/arrow/pretty_print_test.cc @@ -344,6 +344,16 @@ TEST_F(TestPrettyPrint, DateTimeTypesWithOutOfRangeValues) { const int32_t max_date32 = 11248737; const int64_t min_date64 = 86400000LL * min_date32; const int64_t max_date64 = 86400000LL * (max_date32 + 1) - 1; + + const int32_t min_time32_seconds = 0; + const int32_t max_time32_seconds = 86399; + const int32_t min_time32_millis = 0; + const int32_t max_time32_millis = 86399999; + const int64_t min_time64_micros = 0; + const int64_t max_time64_micros = 86399999999LL; + const int64_t min_time64_nanos = 0; + const int64_t max_time64_nanos = 86399999999999LL; + const int64_t min_timestamp_seconds = -1096193779200LL; const int64_t max_timestamp_seconds = 971890963199LL; const int64_t min_timestamp_millis = min_timestamp_seconds * 1000; @@ -354,6 +364,7 @@ TEST_F(TestPrettyPrint, DateTimeTypesWithOutOfRangeValues) { std::vector is_valid = {false, false, false, false, true, true, true, true, true, true}; + // Dates { std::vector values = {min_int32, max_int32, min_date32 - 1, max_date32 + 1, min_int32, max_int32, min_date32 - 1, max_date32 + 1, @@ -372,7 +383,6 @@ TEST_F(TestPrettyPrint, DateTimeTypesWithOutOfRangeValues) { ])expected"; CheckPrimitive({0, 10}, is_valid, values, expected); } - { std::vector values = {min_int64, max_int64, min_date64 - 1, max_date64 + 1, min_int64, max_int64, min_date64 - 1, max_date64 + 1, @@ -392,8 +402,95 @@ TEST_F(TestPrettyPrint, DateTimeTypesWithOutOfRangeValues) { CheckPrimitive({0, 10}, is_valid, values, expected); } - // TODO time32, time64 + // Times + { + std::vector values = {min_int32, + max_int32, + min_time32_seconds - 1, + max_time32_seconds + 1, + min_int32, + max_int32, + min_time32_seconds - 1, + max_time32_seconds + 1, + min_time32_seconds, + max_time32_seconds}; + static const char* expected = R"expected([ + null, + null, + null, + null, + , + , + , + , + 00:00:00, + 23:59:59 +])expected"; + CheckPrimitive(time32(TimeUnit::SECOND), {0, 10}, is_valid, + values, expected); + } + { + std::vector values = { + min_int32, max_int32, min_time32_millis - 1, max_time32_millis + 1, + min_int32, max_int32, min_time32_millis - 1, max_time32_millis + 1, + min_time32_millis, max_time32_millis}; + static const char* expected = R"expected([ + null, + null, + null, + null, + , + , + , + , + 00:00:00.000, + 23:59:59.999 +])expected"; + CheckPrimitive(time32(TimeUnit::MILLI), {0, 10}, is_valid, + values, expected); + } + { + std::vector values = { + min_int64, max_int64, min_time64_micros - 1, max_time64_micros + 1, + min_int64, max_int64, min_time64_micros - 1, max_time64_micros + 1, + min_time64_micros, max_time64_micros}; + static const char* expected = R"expected([ + null, + null, + null, + null, + , + , + , + , + 00:00:00.000000, + 23:59:59.999999 +])expected"; + CheckPrimitive(time64(TimeUnit::MICRO), {0, 10}, is_valid, + values, expected); + } + { + std::vector values = { + min_int64, max_int64, min_time64_nanos - 1, max_time64_nanos + 1, + min_int64, max_int64, min_time64_nanos - 1, max_time64_nanos + 1, + min_time64_nanos, max_time64_nanos}; + static const char* expected = R"expected([ + null, + null, + null, + null, + , + , + , + , + 00:00:00.000000000, + 23:59:59.999999999 +])expected"; + CheckPrimitive(time64(TimeUnit::NANO), {0, 10}, is_valid, values, + expected); + } + // Timestamps { std::vector values = {min_int64, max_int64, diff --git a/cpp/src/arrow/util/decimal.cc b/cpp/src/arrow/util/decimal.cc index 5e3e5e4ab43fd..3118db994084c 100644 --- a/cpp/src/arrow/util/decimal.cc +++ b/cpp/src/arrow/util/decimal.cc @@ -386,6 +386,9 @@ static void AdjustIntegerStringWithScale(int32_t scale, std::string* str) { } std::string Decimal128::ToString(int32_t scale) const { + if (ARROW_PREDICT_FALSE(scale < -kMaxScale || scale > kMaxScale)) { + return ""; + } std::string str(ToIntegerString()); AdjustIntegerStringWithScale(scale, &str); return str; @@ -695,6 +698,9 @@ std::string Decimal256::ToIntegerString() const { } std::string Decimal256::ToString(int32_t scale) const { + if (ARROW_PREDICT_FALSE(scale < -kMaxScale || scale > kMaxScale)) { + return ""; + } std::string str(ToIntegerString()); AdjustIntegerStringWithScale(scale, &str); return str; diff --git a/cpp/src/arrow/util/formatting.h b/cpp/src/arrow/util/formatting.h index 32b3d55855c31..09eb748e4a9c6 100644 --- a/cpp/src/arrow/util/formatting.h +++ b/cpp/src/arrow/util/formatting.h @@ -349,6 +349,13 @@ bool IsDateTimeInRange(Unit duration) { // range, and the {kMinIncl, kMaxExcl} constants above would overflow. constexpr bool IsDateTimeInRange(std::chrono::nanoseconds duration) { return true; } +template +bool IsTimeInRange(Unit duration) { + constexpr Unit kMinIncl = std::chrono::duration_cast(std::chrono::seconds{0}); + constexpr Unit kMaxExcl = std::chrono::duration_cast(std::chrono::seconds{86400}); + return duration >= kMinIncl && duration < kMaxExcl; +} + template Return FormatOutOfRange(RawValue&& raw_value, Appender&& append) { // XXX locale-sensitive but good enough for now @@ -484,7 +491,10 @@ class StringFormatter> { template Return operator()(Duration, value_type count, Appender&& append) { - Duration since_midnight{count}; + const Duration since_midnight{count}; + if (!ARROW_PREDICT_TRUE(detail::IsTimeInRange(since_midnight))) { + return detail::FormatOutOfRange(count, append); + } constexpr size_t buffer_size = detail::BufferSizeHH_MM_SS(); diff --git a/testing b/testing index 2c29a733ac2c8..a60b715263d9b 160000 --- a/testing +++ b/testing @@ -1 +1 @@ -Subproject commit 2c29a733ac2c8492d5df3b74ea5ab1a32f892f60 +Subproject commit a60b715263d9bbf7e744527fb0c084b693f58043