From 7124872e99673d1087e7c547babf0fb3dd2634d7 Mon Sep 17 00:00:00 2001
From: David Li
Date: Tue, 17 Aug 2021 16:10:59 -0400
Subject: [PATCH 1/4] ARROW-13548: [C++] Implement temporal difference kernels
Co-authored-by: Rok Mihevc
---
cpp/src/arrow/array/diff.cc | 2 +-
cpp/src/arrow/array/diff_test.cc | 2 +-
.../arrow/compute/kernels/codegen_internal.h | 4 +
.../arrow/compute/kernels/scalar_temporal.cc | 473 +++++++++++++++++-
.../compute/kernels/scalar_temporal_test.cc | 271 ++++++++++
.../arrow/compute/kernels/temporal_internal.h | 4 +
cpp/src/arrow/type.cc | 12 +
cpp/src/arrow/type.h | 7 +
cpp/src/arrow/util/formatting.h | 84 ++++
cpp/src/arrow/util/formatting_util_test.cc | 38 ++
docs/source/cpp/compute.rst | 42 ++
docs/source/python/api/compute.rst | 19 +
12 files changed, 949 insertions(+), 9 deletions(-)
diff --git a/cpp/src/arrow/array/diff.cc b/cpp/src/arrow/array/diff.cc
index 6d9619bb8aa..0a50de0f1f1 100644
--- a/cpp/src/arrow/array/diff.cc
+++ b/cpp/src/arrow/array/diff.cc
@@ -464,7 +464,7 @@ class MakeFormatterImpl {
impl_ = [](const Array& array, int64_t index, std::ostream* os) {
auto month_day_nanos =
checked_cast(array).Value(index);
- *os << month_day_nanos.months << "m" << month_day_nanos.days << "d"
+ *os << month_day_nanos.months << "M" << month_day_nanos.days << "d"
<< month_day_nanos.nanoseconds << "ns";
};
return Status::OK();
diff --git a/cpp/src/arrow/array/diff_test.cc b/cpp/src/arrow/array/diff_test.cc
index c5cf94fc069..d802a52cdc2 100644
--- a/cpp/src/arrow/array/diff_test.cc
+++ b/cpp/src/arrow/array/diff_test.cc
@@ -507,7 +507,7 @@ TEST_F(DiffTest, UnifiedDiffFormatter) {
target_ = ArrayFromJSON(month_day_nano_interval(), R"([])");
AssertDiffAndFormat(R"(
@@ -0, +0 @@
--2m3d1ns
+-2M3d1ns
)");
// lists
diff --git a/cpp/src/arrow/compute/kernels/codegen_internal.h b/cpp/src/arrow/compute/kernels/codegen_internal.h
index f9ce34b06e0..847522bb865 100644
--- a/cpp/src/arrow/compute/kernels/codegen_internal.h
+++ b/cpp/src/arrow/compute/kernels/codegen_internal.h
@@ -923,6 +923,10 @@ using ScalarBinaryEqualTypes = ScalarBinary;
template
using ScalarBinaryNotNullEqualTypes = ScalarBinaryNotNull;
+template
+using ScalarBinaryNotNullStatefulEqualTypes =
+ ScalarBinaryNotNullStateful;
+
} // namespace applicator
// ----------------------------------------------------------------------
diff --git a/cpp/src/arrow/compute/kernels/scalar_temporal.cc b/cpp/src/arrow/compute/kernels/scalar_temporal.cc
index 674be60d42b..21793de55b7 100644
--- a/cpp/src/arrow/compute/kernels/scalar_temporal.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_temporal.cc
@@ -50,6 +50,7 @@ using arrow_vendored::date::trunc;
using arrow_vendored::date::weekday;
using arrow_vendored::date::weeks;
using arrow_vendored::date::year_month_day;
+using arrow_vendored::date::year_month_weekday;
using arrow_vendored::date::years;
using arrow_vendored::date::zoned_time;
using arrow_vendored::date::literals::dec;
@@ -59,7 +60,7 @@ using arrow_vendored::date::literals::mon;
using arrow_vendored::date::literals::sun;
using arrow_vendored::date::literals::thu;
using arrow_vendored::date::literals::wed;
-using internal::applicator::ScalarUnaryNotNull;
+using internal::applicator::ScalarBinaryNotNullStatefulEqualTypes;
using internal::applicator::SimpleUnary;
using DayOfWeekState = OptionsWrapper;
@@ -81,6 +82,60 @@ Result GetLocale(const std::string& locale) {
}
}
+Status CheckTimezones(const ExecBatch& batch) {
+ const auto& timezone = GetInputTimezone(batch.values[0]);
+ for (int i = 1; i < batch.num_values(); i++) {
+ const auto& other_timezone = GetInputTimezone(batch.values[i]);
+ if (other_timezone != timezone) {
+ return Status::TypeError("Got differing time zone '", other_timezone,
+ "' for argument ", i + 1, "; expected '", timezone, "'");
+ }
+ }
+ return Status::OK();
+}
+
+Status ValidateDayOfWeekOptions(const DayOfWeekOptions& options) {
+ if (options.week_start < 1 || 7 < options.week_start) {
+ return Status::Invalid(
+ "week_start must follow ISO convention (Monday=1, Sunday=7). Got week_start=",
+ options.week_start);
+ }
+ return Status::OK();
+}
+
+template class Op, typename Duration, typename InType,
+ typename OutType>
+struct TemporalBinary {
+ template
+ static Status ExecWithOptions(KernelContext* ctx, const OptionsType* options,
+ const ExecBatch& batch, Datum* out) {
+ RETURN_NOT_OK(CheckTimezones(batch));
+
+ const auto& timezone = GetInputTimezone(batch.values[0]);
+ if (timezone.empty()) {
+ using ExecTemplate = Op;
+ auto op = ExecTemplate(options, NonZonedLocalizer());
+ applicator::ScalarBinaryNotNullStatefulEqualTypes
+ kernel{op};
+ return kernel.Exec(ctx, batch, out);
+ } else {
+ ARROW_ASSIGN_OR_RAISE(auto tz, LocateZone(timezone));
+ using ExecTemplate = Op;
+ auto op = ExecTemplate(options, ZonedLocalizer{tz});
+ applicator::ScalarBinaryNotNullStatefulEqualTypes
+ kernel{op};
+ return kernel.Exec(ctx, batch, out);
+ }
+ }
+
+ static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) {
+ const FunctionOptions* options = nullptr;
+ return ExecWithOptions(ctx, options, batch, out);
+ }
+};
+
template class Op, typename Duration, typename InType,
typename OutType>
struct TemporalComponentExtractDayOfWeek
@@ -89,11 +144,20 @@ struct TemporalComponentExtractDayOfWeek
static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) {
const DayOfWeekOptions& options = DayOfWeekState::Get(ctx);
- if (options.week_start < 1 || 7 < options.week_start) {
- return Status::Invalid(
- "week_start must follow ISO convention (Monday=1, Sunday=7). Got week_start=",
- options.week_start);
- }
+ RETURN_NOT_OK(ValidateDayOfWeekOptions(options));
+ return Base::ExecWithOptions(ctx, &options, batch, out);
+ }
+};
+
+template class Op, typename Duration, typename InType,
+ typename OutType>
+struct TemporalDayOfWeekBinary : public TemporalBinary {
+ using Base = TemporalBinary;
+
+ static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) {
+ const DayOfWeekOptions& options = DayOfWeekState::Get(ctx);
+ RETURN_NOT_OK(CheckTimezones(batch));
+ RETURN_NOT_OK(ValidateDayOfWeekOptions(options));
return Base::ExecWithOptions(ctx, &options, batch, out);
}
};
@@ -209,7 +273,7 @@ struct DayOfWeek {
template
T Call(KernelContext*, Arg0 arg, Status*) const {
- const auto wd = arrow_vendored::date::year_month_weekday(
+ const auto wd = year_month_weekday(
floor(localizer_.template ConvertTimePoint(arg)))
.weekday()
.iso_encoding();
@@ -776,6 +840,188 @@ struct ISOCalendar {
}
};
+// ----------------------------------------------------------------------
+// Compute boundary crossings between two timestamps
+
+template
+struct YearsBetween {
+ YearsBetween(const FunctionOptions* options, Localizer&& localizer)
+ : localizer_(std::move(localizer)) {}
+
+ template
+ T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+ year_month_day from(
+ floor(localizer_.template ConvertTimePoint(arg0)));
+ year_month_day to(floor(localizer_.template ConvertTimePoint(arg1)));
+ return static_cast((to.year() - from.year()).count());
+ }
+
+ Localizer localizer_;
+};
+
+template
+struct QuartersBetween {
+ QuartersBetween(const FunctionOptions* options, Localizer&& localizer)
+ : localizer_(std::move(localizer)) {}
+
+ static int64_t GetQuarters(const year_month_day& ymd) {
+ return static_cast(static_cast(ymd.year())) * 4 +
+ (static_cast(ymd.month()) - 1) / 3;
+ }
+
+ template
+ T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+ year_month_day from_ymd(
+ floor(localizer_.template ConvertTimePoint(arg0)));
+ year_month_day to_ymd(
+ floor(localizer_.template ConvertTimePoint(arg1)));
+ int64_t from_quarters = GetQuarters(from_ymd);
+ int64_t to_quarters = GetQuarters(to_ymd);
+ return static_cast(to_quarters - from_quarters);
+ }
+
+ Localizer localizer_;
+};
+
+template
+struct MonthsBetween {
+ MonthsBetween(const FunctionOptions* options, Localizer&& localizer)
+ : localizer_(std::move(localizer)) {}
+
+ template
+ T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+ year_month_day from(
+ floor(localizer_.template ConvertTimePoint(arg0)));
+ year_month_day to(floor(localizer_.template ConvertTimePoint(arg1)));
+ return static_cast((to.year() / to.month() - from.year() / from.month()).count());
+ }
+
+ Localizer localizer_;
+};
+
+template
+struct WeeksBetween {
+ using days_t = typename Localizer::days_t;
+
+ WeeksBetween(const DayOfWeekOptions* options, Localizer&& localizer)
+ : week_start_(options->week_start), localizer_(std::move(localizer)) {}
+
+ /// Adjust the day backwards to land on the start of the week.
+ days_t ToWeekStart(days_t point) const {
+ const weekday dow(point);
+ const weekday start_of_week(week_start_);
+ if (dow == start_of_week) return point;
+ const days delta = start_of_week - dow;
+ // delta is always positive and in [0, 6]
+ return point - days(7 - delta.count());
+ }
+
+ template
+ T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+ auto from =
+ ToWeekStart(floor(localizer_.template ConvertTimePoint(arg0)));
+ auto to =
+ ToWeekStart(floor(localizer_.template ConvertTimePoint(arg1)));
+ return (to - from).count() / 7;
+ }
+
+ uint32_t week_start_;
+ Localizer localizer_;
+};
+
+template
+struct MonthDayNanoBetween {
+ MonthDayNanoBetween(const FunctionOptions* options, Localizer&& localizer)
+ : localizer_(std::move(localizer)) {}
+
+ template
+ T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+ static_assert(std::is_same::value, "");
+ auto from = localizer_.template ConvertTimePoint(arg0);
+ auto to = localizer_.template ConvertTimePoint(arg1);
+ year_month_day from_ymd(floor(from));
+ year_month_day to_ymd(floor(to));
+ const int32_t num_months = static_cast(
+ (to_ymd.year() / to_ymd.month() - from_ymd.year() / from_ymd.month()).count());
+ const int32_t num_days = static_cast(static_cast(to_ymd.day())) -
+ static_cast(static_cast(from_ymd.day()));
+ auto from_time = static_cast(
+ std::chrono::duration_cast(from - floor(from))
+ .count());
+ auto to_time = static_cast(
+ std::chrono::duration_cast(to - floor(to))
+ .count());
+ const int64_t num_nanos = to_time - from_time;
+ return T{num_months, num_days, num_nanos};
+ }
+
+ Localizer localizer_;
+};
+
+template
+struct DayTimeBetween {
+ DayTimeBetween(const FunctionOptions* options, Localizer&& localizer)
+ : localizer_(std::move(localizer)) {}
+
+ template
+ T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+ static_assert(std::is_same::value, "");
+ auto from = localizer_.template ConvertTimePoint(arg0);
+ auto to = localizer_.template ConvertTimePoint(arg1);
+ const int32_t num_days =
+ static_cast((floor(to) - floor(from)).count());
+ auto from_time = static_cast(
+ std::chrono::duration_cast(from - floor(from))
+ .count());
+ auto to_time = static_cast(
+ std::chrono::duration_cast(to - floor(to))
+ .count());
+ const int32_t num_millis = to_time - from_time;
+ return DayTimeIntervalType::DayMilliseconds{num_days, num_millis};
+ }
+
+ Localizer localizer_;
+};
+
+template
+struct UnitsBetween {
+ UnitsBetween(const FunctionOptions* options, Localizer&& localizer)
+ : localizer_(std::move(localizer)) {}
+
+ template
+ T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+ auto from = floor(localizer_.template ConvertTimePoint(arg0));
+ auto to = floor(localizer_.template ConvertTimePoint(arg1));
+ return static_cast((to - from).count());
+ }
+
+ Localizer localizer_;
+};
+
+template
+using DaysBetween = UnitsBetween;
+
+template
+using HoursBetween = UnitsBetween;
+
+template
+using MinutesBetween = UnitsBetween;
+
+template
+using SecondsBetween = UnitsBetween;
+
+template
+using MillisecondsBetween = UnitsBetween;
+
+template
+using MicrosecondsBetween = UnitsBetween;
+
+template
+using NanosecondsBetween = UnitsBetween;
+
+// ----------------------------------------------------------------------
+// Registration helpers
+
// Which types to generate a kernel for
enum EnabledTypes : uint8_t { WithDates, WithTimes, WithTimestamps };
@@ -929,6 +1175,49 @@ std::shared_ptr MakeSimpleUnaryTemporal(
return func;
}
+template class Op,
+ template class OpExec, typename Duration,
+ typename InType, typename OutType>
+ class ExecTemplate,
+ typename OutType>
+std::shared_ptr MakeTemporalBinary(
+ std::string name, const std::shared_ptr out_type,
+ const FunctionDoc* doc, const FunctionOptions* default_options = NULLPTR,
+ KernelInit init = NULLPTR) {
+ auto func =
+ std::make_shared(name, Arity::Binary(), doc, default_options);
+
+ for (auto unit : TimeUnit::values()) {
+ InputType in_type{match::TimestampTypeUnit(unit)};
+ switch (unit) {
+ case TimeUnit::SECOND: {
+ auto exec = ExecTemplate::Exec;
+ DCHECK_OK(func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
+ break;
+ }
+ case TimeUnit::MILLI: {
+ auto exec =
+ ExecTemplate::Exec;
+ DCHECK_OK(func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
+ break;
+ }
+ case TimeUnit::MICRO: {
+ auto exec =
+ ExecTemplate::Exec;
+ DCHECK_OK(func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
+ break;
+ }
+ case TimeUnit::NANO: {
+ auto exec =
+ ExecTemplate::Exec;
+ DCHECK_OK(func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
+ break;
+ }
+ }
+ }
+ return func;
+}
+
const FunctionDoc year_doc{
"Extract year number",
("Null values emit null.\n"
@@ -1107,6 +1396,114 @@ const FunctionDoc assume_timezone_doc{
{"timestamps"},
"AssumeTimezoneOptions"};
+const FunctionDoc years_between_doc{
+ "Compute the number of years between two timestamps",
+ ("Returns the number of year boundaries crossed from `start` to `end`.\n"
+ "That is, the difference is calculated as if the timestamps were\n"
+ "truncated to the year.\n"
+ "Null values emit null."),
+ {"start", "end"}};
+
+const FunctionDoc quarters_between_doc{
+ "Compute the number of quarters between two timestamps",
+ ("Returns the number of quarter start boundaries crossed from `start` to `end`.\n"
+ "That is, the difference is calculated as if the timestamps were\n"
+ "truncated to the quarter.\n"
+ "Null values emit null."),
+ {"start", "end"}};
+
+const FunctionDoc months_between_doc{
+ "Compute the number of months between two timestamps",
+ ("Returns the number of month boundaries crossed from `start` to `end`.\n"
+ "That is, the difference is calculated as if the timestamps were\n"
+ "truncated to the month.\n"
+ "Null values emit null."),
+ {"start", "end"}};
+
+const FunctionDoc month_day_nano_interval_between_doc{
+ "Compute the number of months, days and nanoseconds between two timestamps",
+ ("Returns the number of months, days, and nanoseconds from `start` to `end`.\n"
+ "That is, first the difference in months is computed as if both timestamps\n"
+ "were truncated to the months, then the difference between the days\n"
+ "is computed, and finally the difference between the times of the two\n"
+ "timestamps is computed as if both times were truncated to the nanosecond.\n"
+ "Null values return null."),
+ {"start", "end"}};
+
+const FunctionDoc weeks_between_doc{
+ "Compute the number of weeks between two timestamps",
+ ("Returns the number of week boundaries crossed from `start` to `end`.\n"
+ "That is, the difference is calculated as if the timestamps were\n"
+ "truncated to the week.\n"
+ "Null values emit null."),
+ {"start", "end"},
+ "DayOfWeekOptions"};
+
+const FunctionDoc day_time_interval_between_doc{
+ "Compute the number of days and milliseconds between two timestamps",
+ ("Returns the number of days and milliseconds from `start` to `end`.\n"
+ "That is, first the difference in days is computed as if both\n"
+ "timestamps were truncated to the day, then the difference between time times\n"
+ "of the two timestamps is computed as if both times were truncated to the\n"
+ "millisecond.\n"
+ "Null values return null."),
+ {"start", "end"}};
+
+const FunctionDoc days_between_doc{
+ "Compute the number of days between two timestamps",
+ ("Returns the number of day boundaries crossed from `start` to `end`.\n"
+ "That is, the difference is calculated as if the timestamps were\n"
+ "truncated to the day.\n"
+ "Null values emit null."),
+ {"start", "end"}};
+
+const FunctionDoc hours_between_doc{
+ "Compute the number of hours between two timestamps",
+ ("Returns the number of hour boundaries crossed from `start` to `end`.\n"
+ "That is, the difference is calculated as if the timestamps were\n"
+ "truncated to the hour.\n"
+ "Null values emit null."),
+ {"start", "end"}};
+
+const FunctionDoc minutes_between_doc{
+ "Compute the number of minute boundaries between two timestamps",
+ ("Returns the number of minute boundaries crossed from `start` to `end`.\n"
+ "That is, the difference is calculated as if the timestamps were\n"
+ "truncated to the minute.\n"
+ "Null values emit null."),
+ {"start", "end"}};
+
+const FunctionDoc seconds_between_doc{
+ "Compute the number of seconds between two timestamps",
+ ("Returns the number of second boundaries crossed from `start` to `end`.\n"
+ "That is, the difference is calculated as if the timestamps were\n"
+ "truncated to the second.\n"
+ "Null values emit null."),
+ {"start", "end"}};
+
+const FunctionDoc milliseconds_between_doc{
+ "Compute the number of millisecond boundaries between two timestamps",
+ ("Returns the number of millisecond boundaries crossed from `start` to `end`.\n"
+ "That is, the difference is calculated as if the timestamps were\n"
+ "truncated to the millisecond.\n"
+ "Null values emit null."),
+ {"start", "end"}};
+
+const FunctionDoc microseconds_between_doc{
+ "Compute the number of microseconds between two timestamps",
+ ("Returns the number of microsecond boundaries crossed from `start` to `end`.\n"
+ "That is, the difference is calculated as if the timestamps were\n"
+ "truncated to the microsecond.\n"
+ "Null values emit null."),
+ {"start", "end"}};
+
+const FunctionDoc nanoseconds_between_doc{
+ "Compute the number of nanoseconds between two timestamps",
+ ("Returns the number of nanosecond boundaries crossed from `start` to `end`.\n"
+ "That is, the difference is calculated as if the timestamps were\n"
+ "truncated to the nanosecond.\n"
+ "Null values emit null."),
+ {"start", "end"}};
} // namespace
void RegisterScalarTemporal(FunctionRegistry* registry) {
@@ -1209,6 +1606,68 @@ void RegisterScalarTemporal(FunctionRegistry* registry) {
OutputType::Resolver(ResolveAssumeTimezoneOutput), &assume_timezone_doc,
nullptr, AssumeTimezoneState::Init);
DCHECK_OK(registry->AddFunction(std::move(assume_timezone)));
+
+ auto years_between = MakeTemporalBinary(
+ "years_between", int64(), &years_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(years_between)));
+
+ auto quarters_between = MakeTemporalBinary(
+ "quarters_between", int64(), &quarters_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(quarters_between)));
+
+ auto month_interval_between =
+ MakeTemporalBinary(
+ "month_interval_between", month_interval(), &months_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(month_interval_between)));
+
+ auto month_day_nano_between =
+ MakeTemporalBinary(
+ "month_day_nano_interval_between", month_day_nano_interval(),
+ &month_day_nano_interval_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(month_day_nano_between)));
+
+ auto weeks_between =
+ MakeTemporalBinary(
+ "weeks_between", int64(), &weeks_between_doc, &default_day_of_week_options,
+ DayOfWeekState::Init);
+ DCHECK_OK(registry->AddFunction(std::move(weeks_between)));
+
+ auto day_time_between =
+ MakeTemporalBinary(
+ "day_time_interval_between", day_time_interval(),
+ &day_time_interval_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(day_time_between)));
+
+ auto days_between = MakeTemporalBinary(
+ "days_between", int64(), &days_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(days_between)));
+
+ auto hours_between = MakeTemporalBinary(
+ "hours_between", int64(), &hours_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(hours_between)));
+
+ auto minutes_between = MakeTemporalBinary(
+ "minutes_between", int64(), &minutes_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(minutes_between)));
+
+ auto seconds_between = MakeTemporalBinary(
+ "seconds_between", int64(), &seconds_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(seconds_between)));
+
+ auto milliseconds_between =
+ MakeTemporalBinary(
+ "milliseconds_between", int64(), &milliseconds_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(milliseconds_between)));
+
+ auto microseconds_between =
+ MakeTemporalBinary(
+ "microseconds_between", int64(), µseconds_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(microseconds_between)));
+
+ auto nanoseconds_between =
+ MakeTemporalBinary(
+ "nanoseconds_between", int64(), &nanoseconds_between_doc);
+ DCHECK_OK(registry->AddFunction(std::move(nanoseconds_between)));
}
} // namespace internal
diff --git a/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc b/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc
index fcc54e418ee..b624ebcd58d 100644
--- a/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc
@@ -20,6 +20,7 @@
#include "arrow/compute/api_scalar.h"
#include "arrow/compute/kernels/test_util.h"
#include "arrow/testing/gtest_util.h"
+#include "arrow/testing/matchers.h"
#include "arrow/type.h"
#include "arrow/util/checked_cast.h"
#include "arrow/util/formatting.h"
@@ -73,6 +74,15 @@ class ScalarTemporalTest : public ::testing::Test {
"2010-01-04T07:35:35", "2006-01-01T08:40:40",
"2005-12-31T09:45:45", "2008-12-28", "2008-12-29",
"2012-01-01 01:02:03", null])";
+ const char* times_seconds_precision2 =
+ R"(["1971-01-01T00:00:59","1999-02-28T23:23:23",
+ "1899-02-01T00:59:20","2033-04-18T03:33:20",
+ "2020-01-02T01:05:05", "2019-12-29T02:10:10",
+ "2019-12-30T04:15:15", "2009-12-31T03:20:20",
+ "2010-01-01T05:26:25", "2010-01-03T06:29:30",
+ "2010-01-04T07:35:36", "2006-01-01T08:40:39",
+ "2005-12-31T09:45:45", "2008-12-27T23:59:59",
+ "2008-02-28", "2012-03-01", null])";
std::shared_ptr iso_calendar_type =
struct_({field("iso_year", int64()), field("iso_week", int64()),
field("iso_day_of_week", int64())});
@@ -130,6 +140,75 @@ class ScalarTemporalTest : public ::testing::Test {
"[0.123456, 0.999999, 0.001001, 0, 0.001, 0.002, 0.003, 0.004132, 0.005321, "
"0.006163, 0, 0, 0, 0, 0, 0, null]";
std::string zeros = "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null]";
+ std::string years_between = "[1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null]";
+ std::string years_between_tz =
+ "[1, -1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, null]";
+ std::string quarters_between =
+ "[4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, null]";
+ std::string quarters_between_tz =
+ "[4, -4, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 1, null]";
+ std::string months_between =
+ "[12, -12, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10, 2, null]";
+ std::string months_between_tz =
+ "[12, -12, 1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10, 2, null]";
+ std::string month_day_nano_interval_between_zeros =
+ "[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], "
+ "[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], "
+ "[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], null]";
+ std::string month_day_nano_interval_between =
+ "[[12, 0, 0], [-12, -1, 0], [1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -2, 0], "
+ "[0, 0, 3600000000000], [0, 0, -3600000000000], "
+ "[0, 0, 60000000000], [0, 0, -60000000000], "
+ "[0, 0, 1000000000], [0, 0, -1000000000], "
+ "[0, 0, 0], [0, -1, 86399000000000], [-10, -1, 0], [2, 0, -3723000000000], null]";
+ std::string month_day_nano_interval_between_tz =
+ "[[12, 0, 0], [-12, -1, 0], [1, 0, 0], [-1, 0, 0], [1, -30, 0], [0, -2, 0], "
+ "[0, 0, 3600000000000], [0, 0, -3600000000000], "
+ "[0, 0, 60000000000], [0, 0, -60000000000], "
+ "[0, 0, 1000000000], [0, 0, -1000000000], "
+ "[0, 0, 0], [0, 0, -1000000000], [-10, -1, 0], [2, -2, -3723000000000], null]";
+ std::string day_time_interval_between_zeros =
+ "[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], "
+ "[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], null]";
+ std::string day_time_interval_between =
+ "[[365, 0], [-366, 0], [31, 0], [-30, 0], [1, 0], [-2, 0], [0, 3600000], "
+ "[0, -3600000], [0, 60000], [0, -60000], [0, 1000], [0, -1000], [0, 0], "
+ "[-1, 86399000], [-305, 0], [60, -3723000], null]";
+ std::string day_time_interval_between_tz =
+ "[[365, 0], [-366, 0], [31, 0], [-30, 0], [1, 0], [-2, 0], [0, 3600000], "
+ "[0, -3600000], [0, 60000], [0, -60000], [0, 1000], [0, -1000], [0, 0], "
+ "[0, -1000], [-305, 0], [60, -3723000], null]";
+ std::string weeks_between =
+ "[52, -53, 5, -4, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, -44, 9, null]";
+ std::string weeks_between_tz =
+ "[52, -53, 5, -5, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, -43, 9, null]";
+ std::string days_between =
+ "[365, -366, 31, -30, 1, -2, 0, 0, 0, 0, 0, 0, 0, -1, -305, 60, null]";
+ std::string days_between_tz =
+ "[365, -366, 31, -30, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, -305, 60, null]";
+ std::string hours_between =
+ "[8760, -8784, 744, -720, 24, -48, 1, -1, 0, 0, 0, 0, 0, -1, -7320, 1439, null]";
+ std::string hours_between_tz =
+ "[8760, -8784, 744, -720, 24, -48, 1, -1, 0, -1, 0, 0, 0, 0, -7320, 1439, null]";
+ std::string minutes_between =
+ "[525600, -527040, 44640, -43200, 1440, -2880, 60, -60, 1, -1, 0, 0, 0, -1, "
+ "-439200, 86338, null]";
+ std::string seconds_between =
+ "[31536000, -31622400, 2678400, -2592000, 86400, -172800, 3600, -3600, 60, -60, 1, "
+ "-1, 0, -1, -26352000, 5180277, null]";
+ std::string milliseconds_between =
+ "[31536000000, -31622400000, 2678400000, -2592000000, 86400000, -172800000, "
+ "3600000, -3600000, 60000, -60000, 1000, -1000, 0, -1000, -26352000000, "
+ "5180277000, null]";
+ std::string microseconds_between =
+ "[31536000000000, -31622400000000, 2678400000000, -2592000000000, 86400000000, "
+ "-172800000000, 3600000000, -3600000000, 60000000, -60000000, 1000000, -1000000, "
+ "0, -1000000, -26352000000000, 5180277000000, null]";
+ std::string nanoseconds_between =
+ "[31536000000000000, -31622400000000000, 2678400000000000, -2592000000000000, "
+ "86400000000000, -172800000000000, 3600000000000, -3600000000000, 60000000000, "
+ "-60000000000, 1000000000, -1000000000, 0, -1000000000, -26352000000000000, "
+ "5180277000000000, null]";
};
TEST_F(ScalarTemporalTest, TestTemporalComponentExtractionAllTemporalTypes) {
@@ -505,6 +584,144 @@ TEST_F(ScalarTemporalTest, DayOfWeek) {
/*week_start=*/8)));
}
+TEST_F(ScalarTemporalTest, TestTemporalDifference) {
+ for (auto u : TimeUnit::values()) {
+ auto unit = timestamp(u);
+ auto arr1 = ArrayFromJSON(unit, times_seconds_precision);
+ auto arr2 = ArrayFromJSON(unit, times_seconds_precision2);
+ CheckScalarBinary("years_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("years_between", arr1, arr2, ArrayFromJSON(int64(), years_between));
+ CheckScalarBinary("quarters_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("quarters_between", arr1, arr2,
+ ArrayFromJSON(int64(), quarters_between));
+ CheckScalarBinary("month_interval_between", arr1, arr1,
+ ArrayFromJSON(month_interval(), zeros));
+ CheckScalarBinary("month_interval_between", arr1, arr1,
+ ArrayFromJSON(month_interval(), zeros));
+ CheckScalarBinary("month_interval_between", arr1, arr2,
+ ArrayFromJSON(month_interval(), months_between));
+ CheckScalarBinary(
+ "month_day_nano_interval_between", arr1, arr1,
+ ArrayFromJSON(month_day_nano_interval(), month_day_nano_interval_between_zeros));
+ CheckScalarBinary(
+ "month_day_nano_interval_between", arr1, arr2,
+ ArrayFromJSON(month_day_nano_interval(), month_day_nano_interval_between));
+ CheckScalarBinary(
+ "day_time_interval_between", arr1, arr1,
+ ArrayFromJSON(day_time_interval(), day_time_interval_between_zeros));
+ CheckScalarBinary("day_time_interval_between", arr1, arr2,
+ ArrayFromJSON(day_time_interval(), day_time_interval_between));
+ CheckScalarBinary("weeks_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("weeks_between", arr1, arr2, ArrayFromJSON(int64(), weeks_between));
+ CheckScalarBinary("days_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("days_between", arr1, arr2, ArrayFromJSON(int64(), days_between));
+ CheckScalarBinary("hours_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("hours_between", arr1, arr2, ArrayFromJSON(int64(), hours_between));
+ CheckScalarBinary("minutes_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("minutes_between", arr1, arr2,
+ ArrayFromJSON(int64(), minutes_between));
+ CheckScalarBinary("seconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("seconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), seconds_between));
+ CheckScalarBinary("milliseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("milliseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), milliseconds_between));
+ CheckScalarBinary("microseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("microseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), microseconds_between));
+ CheckScalarBinary("nanoseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("nanoseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), nanoseconds_between));
+ }
+}
+
+TEST_F(ScalarTemporalTest, TestTemporalDifferenceWeeks) {
+ auto ty = timestamp(TimeUnit::SECOND);
+ auto days = ArrayFromJSON(ty, R"([
+ "2021-08-09", "2021-08-10", "2021-08-11", "2021-08-12", "2021-08-13", "2021-08-14", "2021-08-15",
+ "2021-08-16", "2021-08-17", "2021-08-18", "2021-08-19", "2021-08-20", "2021-08-21", "2021-08-22",
+ "2021-08-23", "2021-08-24", "2021-08-25", "2021-08-26", "2021-08-27", "2021-08-28", "2021-08-29"
+ ])");
+
+ DayOfWeekOptions options(/*one_based_numbering=*/false, /*week_start=Monday*/ 1);
+ EXPECT_THAT(CallFunction("weeks_between", {ScalarFromJSON(ty, R"("2021-08-16")"), days},
+ &options),
+ ResultWith(Datum(ArrayFromJSON(int64(), R"([
+-1, -1, -1, -1, -1, -1, -1,
+0, 0, 0, 0, 0, 0, 0,
+1, 1, 1, 1, 1, 1, 1
+])"))));
+ EXPECT_THAT(CallFunction("weeks_between", {ScalarFromJSON(ty, R"("2021-08-17")"), days},
+ &options),
+ ResultWith(Datum(ArrayFromJSON(int64(), R"([
+-1, -1, -1, -1, -1, -1, -1,
+0, 0, 0, 0, 0, 0, 0,
+1, 1, 1, 1, 1, 1, 1
+])"))));
+
+ options.week_start = 3; // Wednesday
+ EXPECT_THAT(CallFunction("weeks_between", {ScalarFromJSON(ty, R"("2021-08-16")"), days},
+ &options),
+ ResultWith(Datum(ArrayFromJSON(int64(), R"([
+-1, -1, 0, 0, 0, 0, 0,
+0, 0, 1, 1, 1, 1, 1,
+1, 1, 2, 2, 2, 2, 2
+])"))));
+ EXPECT_THAT(CallFunction("weeks_between", {ScalarFromJSON(ty, R"("2021-08-17")"), days},
+ &options),
+ ResultWith(Datum(ArrayFromJSON(int64(), R"([
+-1, -1, 0, 0, 0, 0, 0,
+0, 0, 1, 1, 1, 1, 1,
+1, 1, 2, 2, 2, 2, 2
+])"))));
+ EXPECT_THAT(CallFunction("weeks_between", {ScalarFromJSON(ty, R"("2021-08-18")"), days},
+ &options),
+ ResultWith(Datum(ArrayFromJSON(int64(), R"([
+-2, -2, -1, -1, -1, -1, -1,
+-1, -1, 0, 0, 0, 0, 0,
+0, 0, 1, 1, 1, 1, 1
+])"))));
+}
+
+TEST_F(ScalarTemporalTest, TestTemporalDifferenceErrors) {
+ Datum arr1 = ArrayFromJSON(timestamp(TimeUnit::SECOND, "America/New_York"),
+ R"(["1970-01-01T00:00:59"])");
+ Datum arr2 = ArrayFromJSON(timestamp(TimeUnit::SECOND, "America/Phoenix"),
+ R"(["1970-01-01T00:00:59"])");
+ Datum arr3 = ArrayFromJSON(timestamp(TimeUnit::SECOND), R"(["1970-01-01T00:00:59"])");
+ Datum arr4 =
+ ArrayFromJSON(timestamp(TimeUnit::SECOND, "UTC"), R"(["1970-01-01T00:00:59"])");
+ for (auto fn :
+ {"years_between", "month_interval_between", "month_day_nano_interval_between",
+ "day_time_interval_between", "weeks_between", "days_between", "hours_between",
+ "minutes_between", "seconds_between", "milliseconds_between",
+ "microseconds_between", "nanoseconds_between"}) {
+ SCOPED_TRACE(fn);
+ EXPECT_RAISES_WITH_MESSAGE_THAT(
+ TypeError,
+ ::testing::HasSubstr("Got differing time zone 'America/Phoenix' for argument 2; "
+ "expected 'America/New_York'"),
+ CallFunction(fn, {arr1, arr2}));
+ EXPECT_RAISES_WITH_MESSAGE_THAT(
+ TypeError,
+ ::testing::HasSubstr(
+ "Got differing time zone 'America/Phoenix' for argument 2; expected ''"),
+ CallFunction(fn, {arr3, arr2}));
+ EXPECT_RAISES_WITH_MESSAGE_THAT(
+ TypeError,
+ ::testing::HasSubstr("Got differing time zone 'UTC' for argument 2; expected ''"),
+ CallFunction(fn, {arr3, arr4}));
+ }
+
+ DayOfWeekOptions options;
+ options.week_start = 20;
+ EXPECT_RAISES_WITH_MESSAGE_THAT(
+ Invalid,
+ ::testing::HasSubstr("week_start must follow ISO convention (Monday=1, Sunday=7). "
+ "Got week_start=20"),
+ CallFunction("weeks_between", {arr1, arr1}, &options));
+}
+
// TODO: We should test on windows once ARROW-13168 is resolved.
#ifndef _WIN32
TEST_F(ScalarTemporalTest, TestAssumeTimezone) {
@@ -795,6 +1012,60 @@ TEST_F(ScalarTemporalTest, StrftimeInvalidLocale) {
testing::HasSubstr("Cannot find locale 'non-existent'"),
Strftime(arr, options));
}
+
+TEST_F(ScalarTemporalTest, TestTemporalDifferenceZoned) {
+ for (auto u : TimeUnit::values()) {
+ auto unit = timestamp(u, "Pacific/Marquesas");
+ auto arr1 = ArrayFromJSON(unit, times_seconds_precision);
+ auto arr2 = ArrayFromJSON(unit, times_seconds_precision2);
+ CheckScalarBinary("years_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("years_between", arr1, arr2,
+ ArrayFromJSON(int64(), years_between_tz));
+ CheckScalarBinary("quarters_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("quarters_between", arr1, arr2,
+ ArrayFromJSON(int64(), quarters_between_tz));
+ CheckScalarBinary(
+ "month_day_nano_interval_between", arr1, arr1,
+ ArrayFromJSON(month_day_nano_interval(), month_day_nano_interval_between_zeros));
+ CheckScalarBinary(
+ "month_day_nano_interval_between", arr1, arr2,
+ ArrayFromJSON(month_day_nano_interval(), month_day_nano_interval_between_tz));
+ CheckScalarBinary("month_interval_between", arr1, arr1,
+ ArrayFromJSON(month_interval(), zeros));
+ CheckScalarBinary("month_interval_between", arr1, arr2,
+ ArrayFromJSON(month_interval(), months_between_tz));
+ CheckScalarBinary(
+ "day_time_interval_between", arr1, arr1,
+ ArrayFromJSON(day_time_interval(), day_time_interval_between_zeros));
+ CheckScalarBinary("day_time_interval_between", arr1, arr2,
+ ArrayFromJSON(day_time_interval(), day_time_interval_between_tz));
+ CheckScalarBinary("weeks_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("weeks_between", arr1, arr2,
+ ArrayFromJSON(int64(), weeks_between_tz));
+ CheckScalarBinary("days_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("days_between", arr1, arr2,
+ ArrayFromJSON(int64(), days_between_tz));
+ CheckScalarBinary("hours_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("hours_between", arr1, arr2,
+ ArrayFromJSON(int64(), hours_between_tz));
+ CheckScalarBinary("minutes_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("minutes_between", arr1, arr2,
+ ArrayFromJSON(int64(), minutes_between));
+ CheckScalarBinary("seconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("seconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), seconds_between));
+ CheckScalarBinary("milliseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("milliseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), milliseconds_between));
+ CheckScalarBinary("microseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("microseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), microseconds_between));
+ CheckScalarBinary("nanoseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("nanoseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), nanoseconds_between));
+ }
+}
+
#endif // !_WIN32
} // namespace compute
diff --git a/cpp/src/arrow/compute/kernels/temporal_internal.h b/cpp/src/arrow/compute/kernels/temporal_internal.h
index 06dce454818..f6469d0f508 100644
--- a/cpp/src/arrow/compute/kernels/temporal_internal.h
+++ b/cpp/src/arrow/compute/kernels/temporal_internal.h
@@ -68,6 +68,8 @@ static inline const std::string& GetInputTimezone(const ArrayData& array) {
}
struct NonZonedLocalizer {
+ using days_t = sys_days;
+
// No-op conversions: UTC -> UTC
template
sys_time ConvertTimePoint(int64_t t) const {
@@ -78,6 +80,8 @@ struct NonZonedLocalizer {
};
struct ZonedLocalizer {
+ using days_t = local_days;
+
// Timezone-localizing conversions: UTC -> local time
const time_zone* tz;
diff --git a/cpp/src/arrow/type.cc b/cpp/src/arrow/type.cc
index ff6a7508cf5..ab5e15ed76d 100644
--- a/cpp/src/arrow/type.cc
+++ b/cpp/src/arrow/type.cc
@@ -403,6 +403,18 @@ FloatingPointType::Precision DoubleType::precision() const {
return FloatingPointType::DOUBLE;
}
+std::ostream& operator<<(std::ostream& os,
+ DayTimeIntervalType::DayMilliseconds interval) {
+ os << interval.days << "d" << interval.milliseconds << "ms";
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os,
+ MonthDayNanoIntervalType::MonthDayNanos interval) {
+ os << interval.months << "M" << interval.days << "d" << interval.nanoseconds << "ns";
+ return os;
+}
+
std::string ListType::ToString() const {
std::stringstream s;
s << "list<" << value_field()->ToString() << ">";
diff --git a/cpp/src/arrow/type.h b/cpp/src/arrow/type.h
index c94d629b61e..23e6c7e9e2d 100644
--- a/cpp/src/arrow/type.h
+++ b/cpp/src/arrow/type.h
@@ -1361,6 +1361,9 @@ class ARROW_EXPORT DayTimeIntervalType : public IntervalType {
std::string name() const override { return "day_time_interval"; }
};
+ARROW_EXPORT
+std::ostream& operator<<(std::ostream& os, DayTimeIntervalType::DayMilliseconds interval);
+
/// \brief Represents a number of months, days and nanoseconds between
/// two dates.
///
@@ -1398,6 +1401,10 @@ class ARROW_EXPORT MonthDayNanoIntervalType : public IntervalType {
std::string name() const override { return "month_day_nano_interval"; }
};
+ARROW_EXPORT
+std::ostream& operator<<(std::ostream& os,
+ MonthDayNanoIntervalType::MonthDayNanos interval);
+
/// \brief Represents an elapsed time without any relation to a calendar artifact.
class ARROW_EXPORT DurationType : public TemporalType, public ParametricType {
public:
diff --git a/cpp/src/arrow/util/formatting.h b/cpp/src/arrow/util/formatting.h
index 566c9795f83..a6a88f98291 100644
--- a/cpp/src/arrow/util/formatting.h
+++ b/cpp/src/arrow/util/formatting.h
@@ -422,5 +422,89 @@ class StringFormatter {
TimeUnit::type unit_;
};
+template <>
+class StringFormatter {
+ public:
+ using value_type = MonthIntervalType::c_type;
+
+ explicit StringFormatter(const std::shared_ptr&) {}
+
+ template
+ Return operator()(value_type interval, Appender&& append) {
+ constexpr size_t buffer_size =
+ /*'m'*/ 3 + /*negative signs*/ 1 +
+ /*months*/ detail::Digits10(std::numeric_limits::max());
+ std::array buffer;
+ char* cursor = buffer.data() + buffer_size;
+
+ detail::FormatOneChar('M', &cursor);
+ detail::FormatAllDigits(detail::Abs(interval), &cursor);
+ if (interval < 0) detail::FormatOneChar('-', &cursor);
+
+ return append(detail::ViewDigitBuffer(buffer, cursor));
+ }
+};
+
+template <>
+class StringFormatter {
+ public:
+ using value_type = DayTimeIntervalType::DayMilliseconds;
+
+ explicit StringFormatter(const std::shared_ptr&) {}
+
+ template
+ Return operator()(value_type interval, Appender&& append) {
+ constexpr size_t buffer_size =
+ /*d, ms*/ 3 + /*negative signs*/ 2 +
+ /*days/milliseconds*/ 2 * detail::Digits10(std::numeric_limits::max());
+ std::array buffer;
+ char* cursor = buffer.data() + buffer_size;
+
+ detail::FormatOneChar('s', &cursor);
+ detail::FormatOneChar('m', &cursor);
+ detail::FormatAllDigits(detail::Abs(interval.milliseconds), &cursor);
+ if (interval.milliseconds < 0) detail::FormatOneChar('-', &cursor);
+
+ detail::FormatOneChar('d', &cursor);
+ detail::FormatAllDigits(detail::Abs(interval.days), &cursor);
+ if (interval.days < 0) detail::FormatOneChar('-', &cursor);
+
+ return append(detail::ViewDigitBuffer(buffer, cursor));
+ }
+};
+
+template <>
+class StringFormatter {
+ public:
+ using value_type = MonthDayNanoIntervalType::MonthDayNanos;
+
+ explicit StringFormatter(const std::shared_ptr&) {}
+
+ template
+ Return operator()(value_type interval, Appender&& append) {
+ constexpr size_t buffer_size =
+ /*m, d, ns*/ 4 + /*negative signs*/ 3 +
+ /*months/days*/ 2 * detail::Digits10(std::numeric_limits::max()) +
+ /*nanoseconds*/ detail::Digits10(std::numeric_limits::max());
+ std::array buffer;
+ char* cursor = buffer.data() + buffer_size;
+
+ detail::FormatOneChar('s', &cursor);
+ detail::FormatOneChar('n', &cursor);
+ detail::FormatAllDigits(detail::Abs(interval.nanoseconds), &cursor);
+ if (interval.nanoseconds < 0) detail::FormatOneChar('-', &cursor);
+
+ detail::FormatOneChar('d', &cursor);
+ detail::FormatAllDigits(detail::Abs(interval.days), &cursor);
+ if (interval.days < 0) detail::FormatOneChar('-', &cursor);
+
+ detail::FormatOneChar('M', &cursor);
+ detail::FormatAllDigits(detail::Abs(interval.months), &cursor);
+ if (interval.months < 0) detail::FormatOneChar('-', &cursor);
+
+ return append(detail::ViewDigitBuffer(buffer, cursor));
+ }
+};
+
} // namespace internal
} // namespace arrow
diff --git a/cpp/src/arrow/util/formatting_util_test.cc b/cpp/src/arrow/util/formatting_util_test.cc
index 35985c04305..3e785518773 100644
--- a/cpp/src/arrow/util/formatting_util_test.cc
+++ b/cpp/src/arrow/util/formatting_util_test.cc
@@ -427,4 +427,42 @@ TEST(Formatting, Timestamp) {
}
}
+TEST(Formatting, Interval) {
+ using DayMilliseconds = DayTimeIntervalType::DayMilliseconds;
+ using MonthDayNanos = MonthDayNanoIntervalType::MonthDayNanos;
+
+ const int32_t max_int32 = std::numeric_limits::max();
+ const int32_t min_int32 = std::numeric_limits::min();
+ const int64_t max_int64 = std::numeric_limits::max();
+ const int64_t min_int64 = std::numeric_limits::min();
+ {
+ StringFormatter formatter(month_interval());
+
+ AssertFormatting(formatter, 0, "0M");
+ AssertFormatting(formatter, -1, "-1M");
+ AssertFormatting(formatter, min_int32, "-2147483648M");
+ AssertFormatting(formatter, max_int32, "2147483647M");
+ }
+ {
+ StringFormatter formatter(day_time_interval());
+
+ AssertFormatting(formatter, DayMilliseconds{0, 0}, "0d0ms");
+ AssertFormatting(formatter, DayMilliseconds{-1, -1}, "-1d-1ms");
+ AssertFormatting(formatter, DayMilliseconds{min_int32, min_int32},
+ "-2147483648d-2147483648ms");
+ AssertFormatting(formatter, DayMilliseconds{max_int32, max_int32},
+ "2147483647d2147483647ms");
+ }
+ {
+ StringFormatter formatter(month_day_nano_interval());
+
+ AssertFormatting(formatter, MonthDayNanos{0, 0, 0}, "0M0d0ns");
+ AssertFormatting(formatter, MonthDayNanos{-1, -1, -1}, "-1M-1d-1ns");
+ AssertFormatting(formatter, MonthDayNanos{min_int32, min_int32, min_int64},
+ "-2147483648M-2147483648d-9223372036854775808ns");
+ AssertFormatting(formatter, MonthDayNanos{max_int32, max_int32, max_int64},
+ "2147483647M2147483647d9223372036854775807ns");
+ }
+}
+
} // namespace arrow
diff --git a/docs/source/cpp/compute.rst b/docs/source/cpp/compute.rst
index 9063e8cf241..630525a23f5 100644
--- a/docs/source/cpp/compute.rst
+++ b/docs/source/cpp/compute.rst
@@ -1385,6 +1385,48 @@ For timestamps inputs with non-empty timezone, localized timestamp components wi
.. _ISO 8601 week date definition: https://en.wikipedia.org/wiki/ISO_week_date#First_week
+Temporal difference
+~~~~~~~~~~~~~~~~~~~
+
+These functions compute the difference between two timestamps in the
+specified unit. The difference is determined by the number of
+boundaries crossed, not the span of time. For example, the difference
+in days between 23:59:59 on one day and 00:00:01 on the next day is
+one day (since midnight was crossed), not zero days (even though less
+than 24 hours elapsed). Additionally, if the timestamp has a defined
+timezone, the difference is calculated in the local timezone. For
+instance, the difference in years between "2019-12-31 18:00:00-0500"
+and "2019-12-31 23:00:00-0500" is zero years, because the local year
+is the same, even though the UTC years would be different.
+
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| Function name | Arity | Input types | Output type | Options class |
++=================================+============+===================+=======================+============================+
+| day_time_interval_between | Binary | Timestamp | DayTime interval | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| days_between | Binary | Timestamp | Int64 | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| hours_between | Binary | Timestamp | Int64 | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| microseconds_between | Binary | Timestamp | Int64 | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| milliseconds_between | Binary | Timestamp | Int64 | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| minutes_between | Binary | Timestamp | Int64 | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| month_day_nano_interval_between | Binary | Timestamp | MonthDayNano interval | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| month_interval_between | Binary | Timestamp | Month interval | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| nanoseconds_between | Binary | Timestamp | Int64 | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| seconds_between | Binary | Timestamp | Int64 | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| weeks_between | Binary | Timestamp | Int64 | :struct:`DayOfWeekOptions` |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| years_between | Binary | Timestamp | Int64 | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
+
Timezone handling
~~~~~~~~~~~~~~~~~
diff --git a/docs/source/python/api/compute.rst b/docs/source/python/api/compute.rst
index e60fe4c3ff0..575d786a63e 100644
--- a/docs/source/python/api/compute.rst
+++ b/docs/source/python/api/compute.rst
@@ -420,6 +420,25 @@ Temporal Component Extraction
week
year
+Temporal Difference
+-------------------
+
+.. autosummary::
+ :toctree: ../generated/
+
+ day_time_interval_between
+ days_between
+ hours_between
+ microseconds_between
+ milliseconds_between
+ minutes_between
+ month_day_nano_interval_between
+ month_interval_between
+ nanoseconds_between
+ seconds_between
+ weeks_between
+ years_between
+
Timezone Handling
-----------------
From 7ed878b6da956e4a2a88887418edf230c9d6486f Mon Sep 17 00:00:00 2001
From: David Li
Date: Tue, 28 Sep 2021 13:01:50 -0400
Subject: [PATCH 2/4] ARROW-13548: [C++] Implement temporal difference kernels
for dates, times
---
.../arrow/compute/kernels/scalar_temporal.cc | 165 +++++++---
.../compute/kernels/scalar_temporal_test.cc | 296 ++++++++++++++++--
2 files changed, 390 insertions(+), 71 deletions(-)
diff --git a/cpp/src/arrow/compute/kernels/scalar_temporal.cc b/cpp/src/arrow/compute/kernels/scalar_temporal.cc
index 21793de55b7..f4efa863a21 100644
--- a/cpp/src/arrow/compute/kernels/scalar_temporal.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_temporal.cc
@@ -103,33 +103,48 @@ Status ValidateDayOfWeekOptions(const DayOfWeekOptions& options) {
return Status::OK();
}
+int64_t GetQuarter(const year_month_day& ymd) {
+ return static_cast((static_cast(ymd.month()) - 1) / 3);
+}
+
template class Op, typename Duration, typename InType,
typename OutType>
struct TemporalBinary {
- template
- static Status ExecWithOptions(KernelContext* ctx, const OptionsType* options,
- const ExecBatch& batch, Datum* out) {
+ template
+ static enable_if_timestamp ExecWithOptions(KernelContext* ctx,
+ const OptionsType* options,
+ const ExecBatch& batch,
+ Datum* out) {
RETURN_NOT_OK(CheckTimezones(batch));
const auto& timezone = GetInputTimezone(batch.values[0]);
if (timezone.empty()) {
using ExecTemplate = Op;
auto op = ExecTemplate(options, NonZonedLocalizer());
- applicator::ScalarBinaryNotNullStatefulEqualTypes
- kernel{op};
+ applicator::ScalarBinaryNotNullStatefulEqualTypes kernel{
+ op};
return kernel.Exec(ctx, batch, out);
} else {
ARROW_ASSIGN_OR_RAISE(auto tz, LocateZone(timezone));
using ExecTemplate = Op;
auto op = ExecTemplate(options, ZonedLocalizer{tz});
- applicator::ScalarBinaryNotNullStatefulEqualTypes
- kernel{op};
+ applicator::ScalarBinaryNotNullStatefulEqualTypes kernel{
+ op};
return kernel.Exec(ctx, batch, out);
}
}
+ template
+ static enable_if_t::value, Status> ExecWithOptions(
+ KernelContext* ctx, const OptionsType* options, const ExecBatch& batch,
+ Datum* out) {
+ using ExecTemplate = Op;
+ auto op = ExecTemplate(options, NonZonedLocalizer());
+ applicator::ScalarBinaryNotNullStatefulEqualTypes kernel{
+ op};
+ return kernel.Exec(ctx, batch, out);
+ }
+
static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) {
const FunctionOptions* options = nullptr;
return ExecWithOptions(ctx, options, batch, out);
@@ -156,7 +171,6 @@ struct TemporalDayOfWeekBinary : public TemporalBinary(localizer_.template ConvertTimePoint(arg)));
- return static_cast((static_cast(ymd.month()) - 1) / 3 + 1);
+ return static_cast(GetQuarter(ymd) + 1);
}
Localizer localizer_;
@@ -865,8 +879,7 @@ struct QuartersBetween {
: localizer_(std::move(localizer)) {}
static int64_t GetQuarters(const year_month_day& ymd) {
- return static_cast(static_cast(ymd.year())) * 4 +
- (static_cast(ymd.month()) - 1) / 3;
+ return static_cast(static_cast(ymd.year())) * 4 + GetQuarter(ymd);
}
template
@@ -1181,36 +1194,77 @@ template class Op,
class ExecTemplate,
typename OutType>
std::shared_ptr MakeTemporalBinary(
- std::string name, const std::shared_ptr out_type,
- const FunctionDoc* doc, const FunctionOptions* default_options = NULLPTR,
- KernelInit init = NULLPTR) {
+ std::string name, std::initializer_list in_types,
+ const std::shared_ptr out_type, const FunctionDoc* doc,
+ const FunctionOptions* default_options = NULLPTR, KernelInit init = NULLPTR) {
+ DCHECK_GT(in_types.size(), 0);
auto func =
std::make_shared(name, Arity::Binary(), doc, default_options);
- for (auto unit : TimeUnit::values()) {
- InputType in_type{match::TimestampTypeUnit(unit)};
- switch (unit) {
- case TimeUnit::SECOND: {
- auto exec = ExecTemplate::Exec;
- DCHECK_OK(func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
- break;
- }
- case TimeUnit::MILLI: {
- auto exec =
- ExecTemplate::Exec;
- DCHECK_OK(func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
+ for (const auto in_type : in_types) {
+ switch (in_type) {
+ case WithDates: {
+ auto exec32 = ExecTemplate::Exec;
+ DCHECK_OK(
+ func->AddKernel({date32(), date32()}, out_type, std::move(exec32), init));
+ auto exec64 =
+ ExecTemplate::Exec;
+ DCHECK_OK(
+ func->AddKernel({date64(), date64()}, out_type, std::move(exec64), init));
break;
}
- case TimeUnit::MICRO: {
- auto exec =
- ExecTemplate::Exec;
- DCHECK_OK(func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
+ case WithTimes: {
+ auto exec32s = ExecTemplate::Exec;
+ auto ty = time32(TimeUnit::SECOND);
+ DCHECK_OK(func->AddKernel({ty, ty}, out_type, std::move(exec32s), init));
+ auto exec32ms =
+ ExecTemplate::Exec;
+ ty = time32(TimeUnit::MILLI);
+ DCHECK_OK(func->AddKernel({ty, ty}, out_type, std::move(exec32ms), init));
+ auto exec64us =
+ ExecTemplate::Exec;
+ ty = time64(TimeUnit::MICRO);
+ DCHECK_OK(func->AddKernel({ty, ty}, out_type, std::move(exec64us), init));
+ auto exec64ns =
+ ExecTemplate::Exec;
+ ty = time64(TimeUnit::NANO);
+ DCHECK_OK(func->AddKernel({ty, ty}, out_type, std::move(exec64ns), init));
break;
}
- case TimeUnit::NANO: {
- auto exec =
- ExecTemplate::Exec;
- DCHECK_OK(func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
+ case WithTimestamps: {
+ for (auto unit : TimeUnit::values()) {
+ InputType in_type{match::TimestampTypeUnit(unit)};
+ switch (unit) {
+ case TimeUnit::SECOND: {
+ auto exec =
+ ExecTemplate::Exec;
+ DCHECK_OK(
+ func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
+ break;
+ }
+ case TimeUnit::MILLI: {
+ auto exec = ExecTemplate::Exec;
+ DCHECK_OK(
+ func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
+ break;
+ }
+ case TimeUnit::MICRO: {
+ auto exec = ExecTemplate::Exec;
+ DCHECK_OK(
+ func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
+ break;
+ }
+ case TimeUnit::NANO: {
+ auto exec = ExecTemplate::Exec;
+ DCHECK_OK(
+ func->AddKernel({in_type, in_type}, out_type, std::move(exec), init));
+ break;
+ }
+ }
+ }
break;
}
}
@@ -1608,65 +1662,72 @@ void RegisterScalarTemporal(FunctionRegistry* registry) {
DCHECK_OK(registry->AddFunction(std::move(assume_timezone)));
auto years_between = MakeTemporalBinary(
- "years_between", int64(), &years_between_doc);
+ "years_between", {WithDates, WithTimestamps}, int64(), &years_between_doc);
DCHECK_OK(registry->AddFunction(std::move(years_between)));
auto quarters_between = MakeTemporalBinary(
- "quarters_between", int64(), &quarters_between_doc);
+ "quarters_between", {WithDates, WithTimestamps}, int64(), &quarters_between_doc);
DCHECK_OK(registry->AddFunction(std::move(quarters_between)));
auto month_interval_between =
MakeTemporalBinary(
- "month_interval_between", month_interval(), &months_between_doc);
+ "month_interval_between", {WithDates, WithTimestamps}, month_interval(),
+ &months_between_doc);
DCHECK_OK(registry->AddFunction(std::move(month_interval_between)));
auto month_day_nano_between =
MakeTemporalBinary(
- "month_day_nano_interval_between", month_day_nano_interval(),
- &month_day_nano_interval_between_doc);
+ "month_day_nano_interval_between", {WithDates, WithTimes, WithTimestamps},
+ month_day_nano_interval(), &month_day_nano_interval_between_doc);
DCHECK_OK(registry->AddFunction(std::move(month_day_nano_between)));
auto weeks_between =
MakeTemporalBinary(
- "weeks_between", int64(), &weeks_between_doc, &default_day_of_week_options,
- DayOfWeekState::Init);
+ "weeks_between", {WithDates, WithTimestamps}, int64(), &weeks_between_doc,
+ &default_day_of_week_options, DayOfWeekState::Init);
DCHECK_OK(registry->AddFunction(std::move(weeks_between)));
auto day_time_between =
MakeTemporalBinary(
- "day_time_interval_between", day_time_interval(),
- &day_time_interval_between_doc);
+ "day_time_interval_between", {WithDates, WithTimes, WithTimestamps},
+ day_time_interval(), &day_time_interval_between_doc);
DCHECK_OK(registry->AddFunction(std::move(day_time_between)));
auto days_between = MakeTemporalBinary(
- "days_between", int64(), &days_between_doc);
+ "days_between", {WithDates, WithTimestamps}, int64(), &days_between_doc);
DCHECK_OK(registry->AddFunction(std::move(days_between)));
auto hours_between = MakeTemporalBinary(
- "hours_between", int64(), &hours_between_doc);
+ "hours_between", {WithDates, WithTimes, WithTimestamps}, int64(),
+ &hours_between_doc);
DCHECK_OK(registry->AddFunction(std::move(hours_between)));
auto minutes_between = MakeTemporalBinary(
- "minutes_between", int64(), &minutes_between_doc);
+ "minutes_between", {WithDates, WithTimes, WithTimestamps}, int64(),
+ &minutes_between_doc);
DCHECK_OK(registry->AddFunction(std::move(minutes_between)));
auto seconds_between = MakeTemporalBinary(
- "seconds_between", int64(), &seconds_between_doc);
+ "seconds_between", {WithDates, WithTimes, WithTimestamps}, int64(),
+ &seconds_between_doc);
DCHECK_OK(registry->AddFunction(std::move(seconds_between)));
auto milliseconds_between =
MakeTemporalBinary(
- "milliseconds_between", int64(), &milliseconds_between_doc);
+ "milliseconds_between", {WithDates, WithTimes, WithTimestamps}, int64(),
+ &milliseconds_between_doc);
DCHECK_OK(registry->AddFunction(std::move(milliseconds_between)));
auto microseconds_between =
MakeTemporalBinary(
- "microseconds_between", int64(), µseconds_between_doc);
+ "microseconds_between", {WithDates, WithTimes, WithTimestamps}, int64(),
+ µseconds_between_doc);
DCHECK_OK(registry->AddFunction(std::move(microseconds_between)));
auto nanoseconds_between =
MakeTemporalBinary(
- "nanoseconds_between", int64(), &nanoseconds_between_doc);
+ "nanoseconds_between", {WithDates, WithTimes, WithTimestamps}, int64(),
+ &nanoseconds_between_doc);
DCHECK_OK(registry->AddFunction(std::move(nanoseconds_between)));
}
diff --git a/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc b/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc
index b624ebcd58d..92133136b62 100644
--- a/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc
@@ -15,9 +15,12 @@
// specific language governing permissions and limitations
// under the License.
+#include
+
#include
#include "arrow/compute/api_scalar.h"
+#include "arrow/compute/cast.h"
#include "arrow/compute/kernels/test_util.h"
#include "arrow/testing/gtest_util.h"
#include "arrow/testing/matchers.h"
@@ -37,26 +40,48 @@ class ScalarTemporalTest : public ::testing::Test {
const char* date32s =
R"([0, 11016, -25932, 23148, 18262, 18261, 18260, 14609, 14610, 14612,
14613, 13149, 13148, 14241, 14242, 15340, null])";
+ const char* date32s2 =
+ R"([365, 10650, -25901, 23118, 18263, 18259, 18260, 14609, 14610, 14612,
+ 14613, 13149, 13148, 14240, 13937, 15400, null])";
const char* date64s =
R"([0, 951782400000, -2240524800000, 1999987200000, 1577836800000,
1577750400000, 1577664000000, 1262217600000, 1262304000000, 1262476800000,
1262563200000, 1136073600000, 1135987200000, 1230422400000, 1230508800000,
1325376000000, null])";
+ const char* date64s2 =
+ R"([31536000000, 920160000000, -2237846400000, 1997395200000,
+ 1577923200000, 1577577600000, 1577664000000, 1262217600000, 1262304000000,
+ 1262476800000, 1262563200000, 1136073600000, 1135987200000, 1230336000000,
+ 1204156800000, 1330560000000, null])";
const char* times_s =
R"([59, 84203, 3560, 12800, 3905, 7810, 11715, 15620, 19525, 23430, 27335,
31240, 35145, 0, 0, 3723, null])";
+ const char* times_s2 =
+ R"([59, 84203, 12642, 7182, 68705, 7390, 915, 16820, 19525, 5430, 84959,
+ 31207, 35145, 0, 0, 3723, null])";
const char* times_ms =
R"([59123, 84203999, 3560001, 12800000, 3905001, 7810002, 11715003, 15620004,
19525005, 23430006, 27335000, 31240000, 35145000, 0, 0, 3723000, null])";
+ const char* times_ms2 =
+ R"([59103, 84203999, 12642001, 7182000, 68705005, 7390000, 915003, 16820004,
+ 19525005, 5430006, 84959000, 31207000, 35145000, 0, 0, 3723000, null])";
const char* times_us =
R"([59123456, 84203999999, 3560001001, 12800000000, 3905001000, 7810002000,
11715003000, 15620004132, 19525005321, 23430006163, 27335000000,
31240000000, 35145000000, 0, 0, 3723000000, null])";
+ const char* times_us2 =
+ R"([59103476, 84203999999, 12642001001, 7182000000, 68705005000, 7390000000,
+ 915003000, 16820004432, 19525005021, 5430006163, 84959000000,
+ 31207000000, 35145000000, 0, 0, 3723000000, null])";
const char* times_ns =
R"([59123456789, 84203999999999, 3560001001001, 12800000000000, 3905001000000,
7810002000000, 11715003000000, 15620004132000, 19525005321000,
23430006163000, 27335000000000, 31240000000000, 35145000000000, 0, 0,
3723000000000, null])";
+ const char* times_ns2 =
+ R"([59103476799, 84203999999909, 12642001001001, 7182000000000, 68705005000000,
+ 7390000000000, 915003000000, 16820004432000, 19525005021000, 5430006163000,
+ 84959000000000, 31207000000000, 35145000000000, 0, 0, 3723000000000, null])";
const char* times =
R"(["1970-01-01T00:00:59.123456789","2000-02-29T23:23:23.999999999",
"1899-01-01T00:59:20.001001001","2033-05-18T03:33:20.000000000",
@@ -65,6 +90,15 @@ class ScalarTemporalTest : public ::testing::Test {
"2010-01-01T05:25:25.005321", "2010-01-03T06:30:30.006163",
"2010-01-04T07:35:35", "2006-01-01T08:40:40", "2005-12-31T09:45:45",
"2008-12-28", "2008-12-29", "2012-01-01 01:02:03", null])";
+ const char* times2 =
+ R"(["1970-01-01T00:00:59.103476799","2000-02-29T23:23:23.999999909",
+ "1899-01-01T03:30:42.001001001","2033-05-18T01:59:42.000000000",
+ "2020-01-01T19:05:05.005", "2019-12-31T02:03:10.000",
+ "2019-12-30T00:15:15.003", "2009-12-31T04:40:20.004432",
+ "2010-01-01T05:25:25.005021", "2010-01-03T01:30:30.006163",
+ "2010-01-04T23:35:59", "2006-01-01T08:40:07",
+ "2005-12-31T09:45:45", "2008-12-28", "2008-12-29",
+ "2012-01-01 01:02:03", null])";
const char* times_seconds_precision =
R"(["1970-01-01T00:00:59","2000-02-29T23:23:23",
"1899-01-01T00:59:20","2033-05-18T03:33:20",
@@ -167,6 +201,36 @@ class ScalarTemporalTest : public ::testing::Test {
"[0, 0, 60000000000], [0, 0, -60000000000], "
"[0, 0, 1000000000], [0, 0, -1000000000], "
"[0, 0, 0], [0, 0, -1000000000], [-10, -1, 0], [2, -2, -3723000000000], null]";
+ std::string month_day_nano_interval_between_date =
+ "[[12, 0, 0], [-12, -1, 0], [1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -2, 0], "
+ "[0, 0, 0], [0, 0, 0], "
+ "[0, 0, 0], [0, 0, 0], "
+ "[0, 0, 0], [0, 0, 0], "
+ "[0, 0, 0], [0, -1, 0], [-10, -1, 0], [2, 0, 0], null]";
+ std::string month_day_nano_interval_between_time =
+ "[[0, 0, -19979990], [0, 0, -90], [0, 0, 9082000000000], [0, 0, -5618000000000], "
+ "[0, 0, 64800004000000], [0, 0, -420002000000], [0, 0, -10800000000000], "
+ "[0, 0, 1200000300000], [0, 0, -300000], [0, 0, -18000000000000], [0, 0, "
+ "57624000000000], "
+ "[0, 0, -33000000000], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], null]";
+ std::string month_day_nano_interval_between_time_s =
+ "[[0, 0, 0], [0, 0, 0], [0, 0, 9082000000000], [0, 0, -5618000000000], "
+ "[0, 0, 64800000000000], [0, 0, -420000000000], [0, 0, -10800000000000], "
+ "[0, 0, 1200000000000], [0, 0, 0], [0, 0, -18000000000000], [0, 0, "
+ "57624000000000], "
+ "[0, 0, -33000000000], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], null]";
+ std::string month_day_nano_interval_between_time_ms =
+ "[[0, 0, -20000000], [0, 0, 0], [0, 0, 9082000000000], [0, 0, -5618000000000], "
+ "[0, 0, 64800004000000], [0, 0, -420002000000], [0, 0, -10800000000000], "
+ "[0, 0, 1200000000000], [0, 0, 0], [0, 0, -18000000000000], [0, 0, "
+ "57624000000000], "
+ "[0, 0, -33000000000], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], null]";
+ std::string month_day_nano_interval_between_time_us =
+ "[[0, 0, -19980000], [0, 0, 0], [0, 0, 9082000000000], [0, 0, -5618000000000], "
+ "[0, 0, 64800004000000], [0, 0, -420002000000], [0, 0, -10800000000000], "
+ "[0, 0, 1200000300000], [0, 0, -300000], [0, 0, -18000000000000], [0, 0, "
+ "57624000000000], "
+ "[0, 0, -33000000000], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], null]";
std::string day_time_interval_between_zeros =
"[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], "
"[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], null]";
@@ -174,10 +238,22 @@ class ScalarTemporalTest : public ::testing::Test {
"[[365, 0], [-366, 0], [31, 0], [-30, 0], [1, 0], [-2, 0], [0, 3600000], "
"[0, -3600000], [0, 60000], [0, -60000], [0, 1000], [0, -1000], [0, 0], "
"[-1, 86399000], [-305, 0], [60, -3723000], null]";
+ std::string day_time_interval_between_date =
+ "[[365, 0], [-366, 0], [31, 0], [-30, 0], [1, 0], [-2, 0], [0, 0], "
+ "[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], "
+ "[-1, 0], [-305, 0], [60, 0], null]";
std::string day_time_interval_between_tz =
"[[365, 0], [-366, 0], [31, 0], [-30, 0], [1, 0], [-2, 0], [0, 3600000], "
"[0, -3600000], [0, 60000], [0, -60000], [0, 1000], [0, -1000], [0, 0], "
"[0, -1000], [-305, 0], [60, -3723000], null]";
+ std::string day_time_interval_between_time =
+ "[[0, -20], [0, 0], [0, 9082000], [0, -5618000], [0, 64800004], [0, -420002], "
+ "[0, -10800000], [0, 1200000], [0, 0], [0, -18000000], [0, 57624000], "
+ "[0, -33000], [0, 0], [0, 0], [0, 0], [0, 0], null]";
+ std::string day_time_interval_between_time_s =
+ "[[0, 0], [0, 0], [0, 9082000], [0, -5618000], [0, 64800000], [0, -420000], "
+ "[0, -10800000], [0, 1200000], [0, 0], [0, -18000000], [0, 57624000], "
+ "[0, -33000], [0, 0], [0, 0], [0, 0], [0, 0], null]";
std::string weeks_between =
"[52, -53, 5, -4, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, -44, 9, null]";
std::string weeks_between_tz =
@@ -188,27 +264,84 @@ class ScalarTemporalTest : public ::testing::Test {
"[365, -366, 31, -30, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, -305, 60, null]";
std::string hours_between =
"[8760, -8784, 744, -720, 24, -48, 1, -1, 0, 0, 0, 0, 0, -1, -7320, 1439, null]";
+ std::string hours_between_date =
+ "[8760, -8784, 744, -720, 24, -48, 0, 0, 0, 0, 0, 0, 0, -24, -7320, 1440, null]";
std::string hours_between_tz =
"[8760, -8784, 744, -720, 24, -48, 1, -1, 0, -1, 0, 0, 0, 0, -7320, 1439, null]";
+ std::string hours_between_time =
+ "[0, 0, 3, -2, 18, 0, -3, 0, 0, -5, 16, 0, 0, 0, 0, 0, null]";
std::string minutes_between =
"[525600, -527040, 44640, -43200, 1440, -2880, 60, -60, 1, -1, 0, 0, 0, -1, "
"-439200, 86338, null]";
+ std::string minutes_between_date =
+ "[525600, -527040, 44640, -43200, 1440, -2880, 0, 0, 0, 0, 0, 0, 0, -1440, "
+ "-439200, 86400, null]";
+ std::string minutes_between_time =
+ "[0, 0, 151, -94, 1080, -7, -180, 20, 0, -300, 960, 0, 0, 0, 0, 0, null]";
std::string seconds_between =
"[31536000, -31622400, 2678400, -2592000, 86400, -172800, 3600, -3600, 60, -60, 1, "
"-1, 0, -1, -26352000, 5180277, null]";
+ std::string seconds_between_date =
+ "[31536000, -31622400, 2678400, -2592000, 86400, -172800, 0, 0, 0, 0, 0, "
+ "0, 0, -86400, -26352000, 5184000, null]";
+ std::string seconds_between_time =
+ "[0, 0, 9082, -5618, 64800, -420, -10800, 1200, 0, -18000, 57624, -33, 0, 0, 0, 0, "
+ "null]";
std::string milliseconds_between =
"[31536000000, -31622400000, 2678400000, -2592000000, 86400000, -172800000, "
"3600000, -3600000, 60000, -60000, 1000, -1000, 0, -1000, -26352000000, "
"5180277000, null]";
+ std::string milliseconds_between_date =
+ "[31536000000, -31622400000, 2678400000, -2592000000, 86400000, -172800000, "
+ "0, 0, 0, 0, 0, 0, 0, -86400000, -26352000000, 5184000000, null]";
+ std::string milliseconds_between_time =
+ "[-20, 0, 9082000, -5618000, 64800004, -420002, -10800000, 1200000, 0, "
+ "-18000000, 57624000, -33000, 0, 0, 0, 0, null]";
+ std::string milliseconds_between_time_s =
+ "[0, 0, 9082000, -5618000, 64800000, -420000, -10800000, 1200000, 0, "
+ "-18000000, 57624000, -33000, 0, 0, 0, 0, null]";
std::string microseconds_between =
"[31536000000000, -31622400000000, 2678400000000, -2592000000000, 86400000000, "
"-172800000000, 3600000000, -3600000000, 60000000, -60000000, 1000000, -1000000, "
"0, -1000000, -26352000000000, 5180277000000, null]";
+ std::string microseconds_between_date =
+ "[31536000000000, -31622400000000, 2678400000000, -2592000000000, 86400000000, "
+ "-172800000000, 0, 0, 0, 0, 0, 0, 0, -86400000000, -26352000000000, 5184000000000, "
+ "null]";
+ std::string microseconds_between_time =
+ "[-19980, 0, 9082000000, -5618000000, 64800004000, -420002000, -10800000000, "
+ "1200000300, -300, -18000000000, 57624000000, -33000000, 0, 0, 0, 0, null]";
+ std::string microseconds_between_time_s =
+ "[0, 0, 9082000000, -5618000000, 64800000000, -420000000, -10800000000, "
+ "1200000000, 0, -18000000000, 57624000000, -33000000, 0, 0, 0, 0, null]";
+ std::string microseconds_between_time_ms =
+ "[-20000, 0, 9082000000, -5618000000, 64800004000, -420002000, -10800000000, "
+ "1200000000, 0, -18000000000, 57624000000, -33000000, 0, 0, 0, 0, null]";
std::string nanoseconds_between =
"[31536000000000000, -31622400000000000, 2678400000000000, -2592000000000000, "
"86400000000000, -172800000000000, 3600000000000, -3600000000000, 60000000000, "
"-60000000000, 1000000000, -1000000000, 0, -1000000000, -26352000000000000, "
"5180277000000000, null]";
+ std::string nanoseconds_between_date =
+ "[31536000000000000, -31622400000000000, 2678400000000000, -2592000000000000, "
+ "86400000000000, -172800000000000, 0, 0, 0, 0, 0, 0, 0, -86400000000000, "
+ "-26352000000000000, 5184000000000000, null]";
+ std::string nanoseconds_between_time =
+ "[-19979990, -90, 9082000000000, -5618000000000, 64800004000000, -420002000000, "
+ "-10800000000000, 1200000300000, -300000, -18000000000000, 57624000000000, "
+ "-33000000000, 0, 0, 0, 0, null]";
+ std::string nanoseconds_between_time_s =
+ "[0, 0, 9082000000000, -5618000000000, 64800000000000, -420000000000, "
+ "-10800000000000, 1200000000000, 0, -18000000000000, 57624000000000, "
+ "-33000000000, 0, 0, 0, 0, null]";
+ std::string nanoseconds_between_time_ms =
+ "[-20000000, 0, 9082000000000, -5618000000000, 64800004000000, -420002000000, "
+ "-10800000000000, 1200000000000, 0, -18000000000000, 57624000000000, "
+ "-33000000000, 0, 0, 0, 0, null]";
+ std::string nanoseconds_between_time_us =
+ "[-19980000, 0, 9082000000000, -5618000000000, 64800004000000, -420002000000, "
+ "-10800000000000, 1200000300000, -300000, -18000000000000, 57624000000000, "
+ "-33000000000, 0, 0, 0, 0, null]";
};
TEST_F(ScalarTemporalTest, TestTemporalComponentExtractionAllTemporalTypes) {
@@ -633,54 +766,179 @@ TEST_F(ScalarTemporalTest, TestTemporalDifference) {
CheckScalarBinary("nanoseconds_between", arr1, arr2,
ArrayFromJSON(int64(), nanoseconds_between));
}
+
+ for (auto date_case : {std::make_tuple(date32(), date32s, date32s2),
+ std::make_tuple(date64(), date64s, date64s2)}) {
+ auto ty = std::get<0>(date_case);
+ auto arr1 = ArrayFromJSON(ty, std::get<1>(date_case));
+ auto arr2 = ArrayFromJSON(ty, std::get<2>(date_case));
+ CheckScalarBinary("years_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("years_between", arr1, arr2, ArrayFromJSON(int64(), years_between));
+ CheckScalarBinary("quarters_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("quarters_between", arr1, arr2,
+ ArrayFromJSON(int64(), quarters_between));
+ CheckScalarBinary("month_interval_between", arr1, arr1,
+ ArrayFromJSON(month_interval(), zeros));
+ CheckScalarBinary("month_interval_between", arr1, arr2,
+ ArrayFromJSON(month_interval(), months_between));
+ CheckScalarBinary(
+ "month_day_nano_interval_between", arr1, arr1,
+ ArrayFromJSON(month_day_nano_interval(), month_day_nano_interval_between_zeros));
+ CheckScalarBinary(
+ "month_day_nano_interval_between", arr1, arr2,
+ ArrayFromJSON(month_day_nano_interval(), month_day_nano_interval_between_date));
+ CheckScalarBinary(
+ "day_time_interval_between", arr1, arr1,
+ ArrayFromJSON(day_time_interval(), day_time_interval_between_zeros));
+ CheckScalarBinary("day_time_interval_between", arr1, arr2,
+ ArrayFromJSON(day_time_interval(), day_time_interval_between_date));
+ CheckScalarBinary("weeks_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("weeks_between", arr1, arr2, ArrayFromJSON(int64(), weeks_between));
+ CheckScalarBinary("days_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("days_between", arr1, arr2, ArrayFromJSON(int64(), days_between));
+ CheckScalarBinary("hours_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("hours_between", arr1, arr2,
+ ArrayFromJSON(int64(), hours_between_date));
+ CheckScalarBinary("minutes_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("minutes_between", arr1, arr2,
+ ArrayFromJSON(int64(), minutes_between_date));
+ CheckScalarBinary("seconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("seconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), seconds_between_date));
+ CheckScalarBinary("milliseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("milliseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), milliseconds_between_date));
+ CheckScalarBinary("microseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("microseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), microseconds_between_date));
+ CheckScalarBinary("nanoseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("nanoseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), nanoseconds_between_date));
+ }
+
+ struct TimeCase {
+ std::shared_ptr ty;
+ std::string times1;
+ std::string times2;
+ std::string month_day_nano_interval_between;
+ std::string day_time_interval_between;
+ std::string milliseconds_between;
+ std::string microseconds_between;
+ std::string nanoseconds_between;
+ };
+ std::vector cases = {
+ {time32(TimeUnit::SECOND), times_s, times_s2,
+ month_day_nano_interval_between_time_s, day_time_interval_between_time_s,
+ milliseconds_between_time_s, microseconds_between_time_s,
+ nanoseconds_between_time_s},
+ {time32(TimeUnit::MILLI), times_ms, times_ms2,
+ month_day_nano_interval_between_time_ms, day_time_interval_between_time,
+ milliseconds_between_time, microseconds_between_time_ms,
+ nanoseconds_between_time_ms},
+ {time64(TimeUnit::MICRO), times_us, times_us2,
+ month_day_nano_interval_between_time_us, day_time_interval_between_time,
+ milliseconds_between_time, microseconds_between_time, nanoseconds_between_time_us},
+ {time64(TimeUnit::NANO), times_ns, times_ns2, month_day_nano_interval_between_time,
+ day_time_interval_between_time, milliseconds_between_time,
+ microseconds_between_time, nanoseconds_between_time},
+ };
+ for (auto time_case : cases) {
+ auto arr1 = ArrayFromJSON(time_case.ty, time_case.times1);
+ auto arr2 = ArrayFromJSON(time_case.ty, time_case.times2);
+ CheckScalarBinary(
+ "month_day_nano_interval_between", arr1, arr1,
+ ArrayFromJSON(month_day_nano_interval(), month_day_nano_interval_between_zeros));
+ CheckScalarBinary("month_day_nano_interval_between", arr1, arr2,
+ ArrayFromJSON(month_day_nano_interval(),
+ time_case.month_day_nano_interval_between));
+ CheckScalarBinary("hours_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("hours_between", arr1, arr2,
+ ArrayFromJSON(int64(), hours_between_time));
+ CheckScalarBinary("minutes_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("minutes_between", arr1, arr2,
+ ArrayFromJSON(int64(), minutes_between_time));
+ CheckScalarBinary("seconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("seconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), seconds_between_time));
+ CheckScalarBinary(
+ "day_time_interval_between", arr1, arr1,
+ ArrayFromJSON(day_time_interval(), day_time_interval_between_zeros));
+ CheckScalarBinary(
+ "day_time_interval_between", arr1, arr2,
+ ArrayFromJSON(day_time_interval(), time_case.day_time_interval_between));
+ CheckScalarBinary("milliseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("milliseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), time_case.milliseconds_between));
+ CheckScalarBinary("microseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("microseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), time_case.microseconds_between));
+ CheckScalarBinary("nanoseconds_between", arr1, arr1, ArrayFromJSON(int64(), zeros));
+ CheckScalarBinary("nanoseconds_between", arr1, arr2,
+ ArrayFromJSON(int64(), time_case.nanoseconds_between));
+ }
}
TEST_F(ScalarTemporalTest, TestTemporalDifferenceWeeks) {
- auto ty = timestamp(TimeUnit::SECOND);
- auto days = ArrayFromJSON(ty, R"([
+ auto raw_days = ArrayFromJSON(timestamp(TimeUnit::SECOND), R"([
"2021-08-09", "2021-08-10", "2021-08-11", "2021-08-12", "2021-08-13", "2021-08-14", "2021-08-15",
"2021-08-16", "2021-08-17", "2021-08-18", "2021-08-19", "2021-08-20", "2021-08-21", "2021-08-22",
"2021-08-23", "2021-08-24", "2021-08-25", "2021-08-26", "2021-08-27", "2021-08-28", "2021-08-29"
])");
+ std::vector ts_scalars = {R"("2021-08-16")", R"("2021-08-17")",
+ R"("2021-08-18")"};
+ std::vector date32_scalars = {"18855", "18856", "18857"};
+ std::vector date64_scalars = {"1629072000000", "1629158400000",
+ "1629244800000"};
+
+ for (const auto& test_case : {std::make_pair(timestamp(TimeUnit::SECOND), ts_scalars),
+ std::make_pair(date32(), date32_scalars),
+ std::make_pair(date64(), date64_scalars)}) {
+ auto ty = test_case.first;
+ std::shared_ptr days;
+ if (ty->id() == Type::TIMESTAMP) {
+ days = raw_days;
+ } else {
+ ASSERT_OK_AND_ASSIGN(auto temp, Cast(raw_days, ty));
+ days = temp.make_array();
+ }
+ auto aug16 = ScalarFromJSON(ty, test_case.second[0]);
+ auto aug17 = ScalarFromJSON(ty, test_case.second[1]);
+ auto aug18 = ScalarFromJSON(ty, test_case.second[2]);
- DayOfWeekOptions options(/*one_based_numbering=*/false, /*week_start=Monday*/ 1);
- EXPECT_THAT(CallFunction("weeks_between", {ScalarFromJSON(ty, R"("2021-08-16")"), days},
- &options),
- ResultWith(Datum(ArrayFromJSON(int64(), R"([
+ DayOfWeekOptions options(/*one_based_numbering=*/false, /*week_start=Monday*/ 1);
+ EXPECT_THAT(CallFunction("weeks_between", {aug16, days}, &options),
+ ResultWith(Datum(ArrayFromJSON(int64(), R"([
-1, -1, -1, -1, -1, -1, -1,
0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1
])"))));
- EXPECT_THAT(CallFunction("weeks_between", {ScalarFromJSON(ty, R"("2021-08-17")"), days},
- &options),
- ResultWith(Datum(ArrayFromJSON(int64(), R"([
+ EXPECT_THAT(CallFunction("weeks_between", {aug17, days}, &options),
+ ResultWith(Datum(ArrayFromJSON(int64(), R"([
-1, -1, -1, -1, -1, -1, -1,
0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1
])"))));
- options.week_start = 3; // Wednesday
- EXPECT_THAT(CallFunction("weeks_between", {ScalarFromJSON(ty, R"("2021-08-16")"), days},
- &options),
- ResultWith(Datum(ArrayFromJSON(int64(), R"([
+ options.week_start = 3; // Wednesday
+ EXPECT_THAT(CallFunction("weeks_between", {aug16, days}, &options),
+ ResultWith(Datum(ArrayFromJSON(int64(), R"([
-1, -1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1,
1, 1, 2, 2, 2, 2, 2
])"))));
- EXPECT_THAT(CallFunction("weeks_between", {ScalarFromJSON(ty, R"("2021-08-17")"), days},
- &options),
- ResultWith(Datum(ArrayFromJSON(int64(), R"([
+ EXPECT_THAT(CallFunction("weeks_between", {aug17, days}, &options),
+ ResultWith(Datum(ArrayFromJSON(int64(), R"([
-1, -1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1,
1, 1, 2, 2, 2, 2, 2
])"))));
- EXPECT_THAT(CallFunction("weeks_between", {ScalarFromJSON(ty, R"("2021-08-18")"), days},
- &options),
- ResultWith(Datum(ArrayFromJSON(int64(), R"([
+ EXPECT_THAT(CallFunction("weeks_between", {aug18, days}, &options),
+ ResultWith(Datum(ArrayFromJSON(int64(), R"([
-2, -2, -1, -1, -1, -1, -1,
-1, -1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1
])"))));
+ }
}
TEST_F(ScalarTemporalTest, TestTemporalDifferenceErrors) {
From 9d0a83ac60aeb8678238ceaa8b3e52c72fd90a87 Mon Sep 17 00:00:00 2001
From: David Li
Date: Tue, 28 Sep 2021 14:02:27 -0400
Subject: [PATCH 3/4] ARROW-13548: [C++] Update docs
---
docs/source/cpp/compute.rst | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/docs/source/cpp/compute.rst b/docs/source/cpp/compute.rst
index 630525a23f5..0c076033b15 100644
--- a/docs/source/cpp/compute.rst
+++ b/docs/source/cpp/compute.rst
@@ -1402,29 +1402,29 @@ is the same, even though the UTC years would be different.
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
| Function name | Arity | Input types | Output type | Options class |
+=================================+============+===================+=======================+============================+
-| day_time_interval_between | Binary | Timestamp | DayTime interval | |
+| day_time_interval_between | Binary | Temporal | DayTime interval | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
-| days_between | Binary | Timestamp | Int64 | |
+| days_between | Binary | Timestamp, Date | Int64 | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
-| hours_between | Binary | Timestamp | Int64 | |
+| hours_between | Binary | Temporal | Int64 | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
-| microseconds_between | Binary | Timestamp | Int64 | |
+| microseconds_between | Binary | Temporal | Int64 | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
-| milliseconds_between | Binary | Timestamp | Int64 | |
+| milliseconds_between | Binary | Temporal | Int64 | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
-| minutes_between | Binary | Timestamp | Int64 | |
+| minutes_between | Binary | Temporal | Int64 | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
-| month_day_nano_interval_between | Binary | Timestamp | MonthDayNano interval | |
+| month_day_nano_interval_between | Binary | Temporal | MonthDayNano interval | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
-| month_interval_between | Binary | Timestamp | Month interval | |
+| month_interval_between | Binary | Timestamp, Date | Month interval | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
-| nanoseconds_between | Binary | Timestamp | Int64 | |
+| nanoseconds_between | Binary | Temporal | Int64 | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
-| seconds_between | Binary | Timestamp | Int64 | |
+| seconds_between | Binary | Temporal | Int64 | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
-| weeks_between | Binary | Timestamp | Int64 | :struct:`DayOfWeekOptions` |
+| weeks_between | Binary | Timestamp, Date | Int64 | :struct:`DayOfWeekOptions` |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
-| years_between | Binary | Timestamp | Int64 | |
+| years_between | Binary | Timestamp, Date | Int64 | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
Timezone handling
From e24f39323fbf510a32c629944f4708ec0142025d Mon Sep 17 00:00:00 2001
From: David Li
Date: Tue, 28 Sep 2021 18:20:05 -0400
Subject: [PATCH 4/4] ARROW-13548: [C++] Fix suggestions
---
cpp/src/arrow/compute/kernels/scalar_temporal.cc | 8 ++++----
docs/source/cpp/compute.rst | 2 ++
docs/source/python/api/compute.rst | 1 +
3 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/cpp/src/arrow/compute/kernels/scalar_temporal.cc b/cpp/src/arrow/compute/kernels/scalar_temporal.cc
index f4efa863a21..decf180a252 100644
--- a/cpp/src/arrow/compute/kernels/scalar_temporal.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_temporal.cc
@@ -1675,11 +1675,11 @@ void RegisterScalarTemporal(FunctionRegistry* registry) {
&months_between_doc);
DCHECK_OK(registry->AddFunction(std::move(month_interval_between)));
- auto month_day_nano_between =
+ auto month_day_nano_interval_between =
MakeTemporalBinary(
"month_day_nano_interval_between", {WithDates, WithTimes, WithTimestamps},
month_day_nano_interval(), &month_day_nano_interval_between_doc);
- DCHECK_OK(registry->AddFunction(std::move(month_day_nano_between)));
+ DCHECK_OK(registry->AddFunction(std::move(month_day_nano_interval_between)));
auto weeks_between =
MakeTemporalBinary(
@@ -1687,11 +1687,11 @@ void RegisterScalarTemporal(FunctionRegistry* registry) {
&default_day_of_week_options, DayOfWeekState::Init);
DCHECK_OK(registry->AddFunction(std::move(weeks_between)));
- auto day_time_between =
+ auto day_time_interval_between =
MakeTemporalBinary(
"day_time_interval_between", {WithDates, WithTimes, WithTimestamps},
day_time_interval(), &day_time_interval_between_doc);
- DCHECK_OK(registry->AddFunction(std::move(day_time_between)));
+ DCHECK_OK(registry->AddFunction(std::move(day_time_interval_between)));
auto days_between = MakeTemporalBinary(
"days_between", {WithDates, WithTimestamps}, int64(), &days_between_doc);
diff --git a/docs/source/cpp/compute.rst b/docs/source/cpp/compute.rst
index 0c076033b15..84203da7030 100644
--- a/docs/source/cpp/compute.rst
+++ b/docs/source/cpp/compute.rst
@@ -1420,6 +1420,8 @@ is the same, even though the UTC years would be different.
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
| nanoseconds_between | Binary | Temporal | Int64 | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
+| quarters_between | Binary | Timestamp, Date | Int64 | |
++---------------------------------+------------+-------------------+-----------------------+----------------------------+
| seconds_between | Binary | Temporal | Int64 | |
+---------------------------------+------------+-------------------+-----------------------+----------------------------+
| weeks_between | Binary | Timestamp, Date | Int64 | :struct:`DayOfWeekOptions` |
diff --git a/docs/source/python/api/compute.rst b/docs/source/python/api/compute.rst
index 575d786a63e..c2f83552ef3 100644
--- a/docs/source/python/api/compute.rst
+++ b/docs/source/python/api/compute.rst
@@ -435,6 +435,7 @@ Temporal Difference
month_day_nano_interval_between
month_interval_between
nanoseconds_between
+ quarters_between
seconds_between
weeks_between
years_between