rok commented on a change in pull request #10960:
URL: https://github.com/apache/arrow/pull/10960#discussion_r709552600



##########
File path: cpp/src/arrow/compute/kernels/scalar_temporal.cc
##########
@@ -830,6 +897,167 @@ struct ISOCalendar {
   }
 };
 
+// ----------------------------------------------------------------------
+// Compute boundary crossings between two timestamps
+
+template <typename Duration, typename Localizer>
+struct YearsBetween {
+  YearsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to(floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1)));
+    return static_cast<T>((to.year() - from.year()).count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct QuartersBetween {
+  QuartersBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  static int64_t GetQuarters(const year_month_day& ymd) {
+    return static_cast<int64_t>(static_cast<int32_t>(ymd.year())) * 4 +
+           (static_cast<uint32_t>(ymd.month()) - 1) / 3;
+  }
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from_ymd(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to_ymd(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg1)));
+    int64_t from_quarters = GetQuarters(from_ymd);
+    int64_t to_quarters = GetQuarters(to_ymd);
+    return static_cast<T>(to_quarters - from_quarters);
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct MonthsBetween {
+  MonthsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to(floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1)));
+    return static_cast<T>(
+        (year_month(to.year(), to.month()) - year_month(from.year(), 
from.month()))
+            .count());

Review comment:
       ```suggestion
       return static_cast<T>(
           ((to.year()/to.month() - from.year()/from.month()).count());
   ```

##########
File path: cpp/src/arrow/compute/kernels/scalar_temporal.cc
##########
@@ -1104,6 +1375,90 @@ const FunctionDoc assume_timezone_doc{
     {"timestamps"},
     "AssumeTimezoneOptions"};
 
+const FunctionDoc years_between_doc{
+    "Compute the number of year boundaries between two timestamps",

Review comment:
       I don't have a better suggestion, but boundaries sounds very 
implementation-like.

##########
File path: cpp/src/arrow/compute/kernels/scalar_temporal.cc
##########
@@ -830,6 +897,167 @@ struct ISOCalendar {
   }
 };
 
+// ----------------------------------------------------------------------
+// Compute boundary crossings between two timestamps
+
+template <typename Duration, typename Localizer>
+struct YearsBetween {
+  YearsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to(floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1)));
+    return static_cast<T>((to.year() - from.year()).count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct QuartersBetween {
+  QuartersBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  static int64_t GetQuarters(const year_month_day& ymd) {
+    return static_cast<int64_t>(static_cast<int32_t>(ymd.year())) * 4 +
+           (static_cast<uint32_t>(ymd.month()) - 1) / 3;
+  }
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from_ymd(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to_ymd(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg1)));
+    int64_t from_quarters = GetQuarters(from_ymd);
+    int64_t to_quarters = GetQuarters(to_ymd);
+    return static_cast<T>(to_quarters - from_quarters);
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct MonthsBetween {
+  MonthsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to(floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1)));
+    return static_cast<T>(
+        (year_month(to.year(), to.month()) - year_month(from.year(), 
from.month()))
+            .count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct WeeksBetween {
+  WeeksBetween(const DayOfWeekOptions* options, Localizer&& localizer)
+      : week_start_(options->week_start), localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    auto from = floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg0));
+    auto to = floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1));
+    const T days_between = static_cast<T>((to - from).count());
+    const T whole_weeks = days_between / 7;
+    const T days_remainder = days_between % 7;
+    // We are counting week boundaries, so there may be one extra week
+    if (days_remainder > 0) {
+      const weekday from_dow(from);
+      const weekday start_of_week(week_start_);
+      const int64_t days = (start_of_week - from_dow).count();
+      if (days > 0 && days <= days_remainder) {
+        return whole_weeks + 1;
+      }
+    } else if (days_remainder < 0) {
+      const weekday from_dow(to);
+      const weekday start_of_week(week_start_);
+      const int64_t days = (start_of_week - from_dow).count();
+      if (days > 0 && days <= -days_remainder) {
+        return whole_weeks - 1;
+      }
+    }
+    return whole_weeks;
+  }
+
+  // Monday = 1, Sunday = 7
+  uint32_t week_start_;
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct DayTimeBetween {
+  DayTimeBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    static_assert(std::is_same<T, 
DayTimeIntervalType::DayMilliseconds>::value, "");
+    auto from = localizer_.template ConvertTimePoint<Duration>(arg0);
+    auto to = localizer_.template ConvertTimePoint<Duration>(arg1);
+    const int32_t num_days =
+        static_cast<int32_t>((floor<days>(to) - floor<days>(from)).count());
+    auto from_time = static_cast<int32_t>(
+        std::chrono::duration_cast<std::chrono::milliseconds>(from - 
floor<days>(from))
+            .count());
+    auto to_time = static_cast<int32_t>(
+        std::chrono::duration_cast<std::chrono::milliseconds>(to - 
floor<days>(to))
+            .count());
+    const int32_t num_millis = to_time - from_time;
+    return DayTimeIntervalType::DayMilliseconds{num_days, num_millis};

Review comment:
       Is there a reason why this is only in ms?

##########
File path: cpp/src/arrow/compute/kernels/scalar_temporal.cc
##########
@@ -192,6 +195,60 @@ struct TemporalComponentExtract
   }
 };
 
+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, "'");
+    }

Review comment:
       Do we really care if they are in different timezones (as long as neither 
is timezone-naive)?

##########
File path: cpp/src/arrow/compute/kernels/scalar_temporal.cc
##########
@@ -1104,6 +1375,90 @@ const FunctionDoc assume_timezone_doc{
     {"timestamps"},
     "AssumeTimezoneOptions"};
 
+const FunctionDoc years_between_doc{
+    "Compute the number of year boundaries between two timestamps",
+    ("Returns the number of year boundaries crossed from the first timestamp 
to the "
+     "second.\n"
+     "Null values return null."),
+    {"start", "end"}};
+
+const FunctionDoc quarters_between_doc{
+    "Compute the number of quarter start boundaries between two timestamps",
+    ("Returns the number of quarter start boundaries crossed from the first 
timestamp "
+     "to the second. The first quarter starts in January.\n"
+     "Null values return null."),
+    {"start", "end"}};
+
+const FunctionDoc months_between_doc{
+    "Compute the number of month boundaries between two timestamps",
+    ("Returns the number of month boundaries crossed from the first timestamp 
to the "
+     "second.\n"
+     "Null values return null."),
+    {"start", "end"}};
+
+const FunctionDoc weeks_between_doc{
+    "Compute the number of week boundaries between two timestamps",
+    ("Returns the number of week boundaries crossed from the first timestamp 
to the "
+     "second, using DayOfWeekOptions to determine which day starts the week.\n"
+     "Null values return null."),
+    {"start", "end"},
+    "DayOfWeekOptions"};
+
+const FunctionDoc day_time_between_doc{
+    "Compute the number of days and milliseconds between two timestamps",
+    ("Returns the number of days and milliseconds from the first timestamp to 
the "
+     "second.\n"
+     "Null values return null."),
+    {"start", "end"}};
+
+const FunctionDoc days_between_doc{
+    "Compute the number of day boundaries between two timestamps",
+    ("Returns the number of day boundaries crossed from the first timestamp to 
the "
+     "second.\n"
+     "Null values return null."),
+    {"start", "end"}};
+
+const FunctionDoc hours_between_doc{
+    "Compute the number of hour boundaries between two timestamps",
+    ("Returns the number of hour boundaries crossed from the first timestamp 
to the "
+     "second.\n"
+     "Null values return 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 the first timestamp 
to the "
+     "second.\n"
+     "Null values return null."),
+    {"start", "end"}};
+
+const FunctionDoc seconds_between_doc{
+    "Compute the number of second boundaries between two timestamps",
+    ("Returns the number of second boundaries crossed from the first timestamp 
to the "
+     "second.\n"
+     "Null values return null."),
+    {"start", "end"}};
+
+const FunctionDoc millis_between_doc{
+    "Compute the number of millisecond boundaries between two timestamps",
+    ("Returns the number of millisecond boundaries crossed from the first "
+     "timestamp to the second.\n"
+     "Null values return null."),
+    {"start", "end"}};
+
+const FunctionDoc micros_between_doc{
+    "Compute the number of microsecond boundaries between two timestamps",
+    ("Returns the number of microsecond boundaries crossed from the first "
+     "timestamp to the second.\n"
+     "Null values return null."),
+    {"start", "end"}};
+
+const FunctionDoc nanos_between_doc{

Review comment:
       ```suggestion
   const FunctionDoc milliseconds_between_doc{
       "Compute the number of millisecond boundaries between two timestamps",
       ("Returns the number of millisecond boundaries crossed from the first "
        "timestamp to the second.\n"
        "Null values return null."),
       {"start", "end"}};
   
   const FunctionDoc microseconds_between_doc{
       "Compute the number of microsecond boundaries between two timestamps",
       ("Returns the number of microsecond boundaries crossed from the first "
        "timestamp to the second.\n"
        "Null values return null."),
       {"start", "end"}};
   
   const FunctionDoc nanoseconds_between_doc{
   ```

##########
File path: cpp/src/arrow/compute/kernels/scalar_temporal.cc
##########
@@ -830,6 +897,167 @@ struct ISOCalendar {
   }
 };
 
+// ----------------------------------------------------------------------
+// Compute boundary crossings between two timestamps
+
+template <typename Duration, typename Localizer>
+struct YearsBetween {
+  YearsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to(floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1)));
+    return static_cast<T>((to.year() - from.year()).count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct QuartersBetween {
+  QuartersBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  static int64_t GetQuarters(const year_month_day& ymd) {
+    return static_cast<int64_t>(static_cast<int32_t>(ymd.year())) * 4 +
+           (static_cast<uint32_t>(ymd.month()) - 1) / 3;
+  }
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from_ymd(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to_ymd(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg1)));
+    int64_t from_quarters = GetQuarters(from_ymd);
+    int64_t to_quarters = GetQuarters(to_ymd);
+    return static_cast<T>(to_quarters - from_quarters);
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct MonthsBetween {
+  MonthsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to(floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1)));
+    return static_cast<T>(
+        (year_month(to.year(), to.month()) - year_month(from.year(), 
from.month()))
+            .count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct WeeksBetween {
+  WeeksBetween(const DayOfWeekOptions* options, Localizer&& localizer)
+      : week_start_(options->week_start), localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    auto from = floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg0));
+    auto to = floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1));
+    const T days_between = static_cast<T>((to - from).count());
+    const T whole_weeks = days_between / 7;
+    const T days_remainder = days_between % 7;
+    // We are counting week boundaries, so there may be one extra week
+    if (days_remainder > 0) {
+      const weekday from_dow(from);
+      const weekday start_of_week(week_start_);
+      const int64_t days = (start_of_week - from_dow).count();
+      if (days > 0 && days <= days_remainder) {
+        return whole_weeks + 1;
+      }
+    } else if (days_remainder < 0) {
+      const weekday from_dow(to);
+      const weekday start_of_week(week_start_);
+      const int64_t days = (start_of_week - from_dow).count();
+      if (days > 0 && days <= -days_remainder) {
+        return whole_weeks - 1;
+      }
+    }
+    return whole_weeks;
+  }
+
+  // Monday = 1, Sunday = 7
+  uint32_t week_start_;
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct DayTimeBetween {
+  DayTimeBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    static_assert(std::is_same<T, 
DayTimeIntervalType::DayMilliseconds>::value, "");
+    auto from = localizer_.template ConvertTimePoint<Duration>(arg0);
+    auto to = localizer_.template ConvertTimePoint<Duration>(arg1);
+    const int32_t num_days =
+        static_cast<int32_t>((floor<days>(to) - floor<days>(from)).count());
+    auto from_time = static_cast<int32_t>(
+        std::chrono::duration_cast<std::chrono::milliseconds>(from - 
floor<days>(from))
+            .count());
+    auto to_time = static_cast<int32_t>(
+        std::chrono::duration_cast<std::chrono::milliseconds>(to - 
floor<days>(to))
+            .count());
+    const int32_t num_millis = to_time - from_time;
+    return DayTimeIntervalType::DayMilliseconds{num_days, num_millis};
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Unit, typename Duration, typename Localizer>
+struct UnitsBetween {
+  UnitsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    auto from = floor<Unit>(localizer_.template 
ConvertTimePoint<Duration>(arg0));
+    auto to = floor<Unit>(localizer_.template 
ConvertTimePoint<Duration>(arg1));
+    return static_cast<T>((to - from).count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+using DaysBetween = UnitsBetween<days, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using HoursBetween = UnitsBetween<std::chrono::hours, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using MinutesBetween = UnitsBetween<std::chrono::minutes, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using SecondsBetween = UnitsBetween<std::chrono::seconds, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using MillisBetween = UnitsBetween<std::chrono::milliseconds, Duration, 
Localizer>;
+
+template <typename Duration, typename Localizer>
+using MicrosBetween = UnitsBetween<std::chrono::microseconds, Duration, 
Localizer>;
+
+template <typename Duration, typename Localizer>
+using NanosBetween = UnitsBetween<std::chrono::nanoseconds, Duration, 
Localizer>;

Review comment:
       ```suggestion
   using NanosecondsBetween = UnitsBetween<std::chrono::nanoseconds, Duration, 
Localizer>;
   ```

##########
File path: cpp/src/arrow/compute/kernels/scalar_temporal.cc
##########
@@ -830,6 +897,167 @@ struct ISOCalendar {
   }
 };
 
+// ----------------------------------------------------------------------
+// Compute boundary crossings between two timestamps
+
+template <typename Duration, typename Localizer>
+struct YearsBetween {
+  YearsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to(floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1)));
+    return static_cast<T>((to.year() - from.year()).count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct QuartersBetween {
+  QuartersBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  static int64_t GetQuarters(const year_month_day& ymd) {
+    return static_cast<int64_t>(static_cast<int32_t>(ymd.year())) * 4 +
+           (static_cast<uint32_t>(ymd.month()) - 1) / 3;
+  }
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from_ymd(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to_ymd(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg1)));
+    int64_t from_quarters = GetQuarters(from_ymd);
+    int64_t to_quarters = GetQuarters(to_ymd);
+    return static_cast<T>(to_quarters - from_quarters);
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct MonthsBetween {
+  MonthsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to(floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1)));
+    return static_cast<T>(
+        (year_month(to.year(), to.month()) - year_month(from.year(), 
from.month()))
+            .count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct WeeksBetween {
+  WeeksBetween(const DayOfWeekOptions* options, Localizer&& localizer)
+      : week_start_(options->week_start), localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    auto from = floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg0));
+    auto to = floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1));
+    const T days_between = static_cast<T>((to - from).count());
+    const T whole_weeks = days_between / 7;
+    const T days_remainder = days_between % 7;
+    // We are counting week boundaries, so there may be one extra week
+    if (days_remainder > 0) {
+      const weekday from_dow(from);
+      const weekday start_of_week(week_start_);
+      const int64_t days = (start_of_week - from_dow).count();
+      if (days > 0 && days <= days_remainder) {
+        return whole_weeks + 1;
+      }
+    } else if (days_remainder < 0) {
+      const weekday from_dow(to);
+      const weekday start_of_week(week_start_);
+      const int64_t days = (start_of_week - from_dow).count();
+      if (days > 0 && days <= -days_remainder) {
+        return whole_weeks - 1;
+      }
+    }
+    return whole_weeks;
+  }
+
+  // Monday = 1, Sunday = 7
+  uint32_t week_start_;
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct DayTimeBetween {
+  DayTimeBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    static_assert(std::is_same<T, 
DayTimeIntervalType::DayMilliseconds>::value, "");
+    auto from = localizer_.template ConvertTimePoint<Duration>(arg0);
+    auto to = localizer_.template ConvertTimePoint<Duration>(arg1);
+    const int32_t num_days =
+        static_cast<int32_t>((floor<days>(to) - floor<days>(from)).count());
+    auto from_time = static_cast<int32_t>(
+        std::chrono::duration_cast<std::chrono::milliseconds>(from - 
floor<days>(from))
+            .count());
+    auto to_time = static_cast<int32_t>(
+        std::chrono::duration_cast<std::chrono::milliseconds>(to - 
floor<days>(to))
+            .count());
+    const int32_t num_millis = to_time - from_time;
+    return DayTimeIntervalType::DayMilliseconds{num_days, num_millis};
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Unit, typename Duration, typename Localizer>
+struct UnitsBetween {
+  UnitsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    auto from = floor<Unit>(localizer_.template 
ConvertTimePoint<Duration>(arg0));
+    auto to = floor<Unit>(localizer_.template 
ConvertTimePoint<Duration>(arg1));
+    return static_cast<T>((to - from).count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+using DaysBetween = UnitsBetween<days, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using HoursBetween = UnitsBetween<std::chrono::hours, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using MinutesBetween = UnitsBetween<std::chrono::minutes, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using SecondsBetween = UnitsBetween<std::chrono::seconds, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using MillisBetween = UnitsBetween<std::chrono::milliseconds, Duration, 
Localizer>;
+
+template <typename Duration, typename Localizer>
+using MicrosBetween = UnitsBetween<std::chrono::microseconds, Duration, 
Localizer>;

Review comment:
       ```suggestion
   using MicrosecondsBetween = UnitsBetween<std::chrono::microseconds, 
Duration, Localizer>;
   ```

##########
File path: cpp/src/arrow/compute/kernels/scalar_temporal.cc
##########
@@ -830,6 +897,167 @@ struct ISOCalendar {
   }
 };
 
+// ----------------------------------------------------------------------
+// Compute boundary crossings between two timestamps
+
+template <typename Duration, typename Localizer>
+struct YearsBetween {
+  YearsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to(floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1)));
+    return static_cast<T>((to.year() - from.year()).count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct QuartersBetween {
+  QuartersBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  static int64_t GetQuarters(const year_month_day& ymd) {
+    return static_cast<int64_t>(static_cast<int32_t>(ymd.year())) * 4 +
+           (static_cast<uint32_t>(ymd.month()) - 1) / 3;
+  }
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from_ymd(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to_ymd(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg1)));
+    int64_t from_quarters = GetQuarters(from_ymd);
+    int64_t to_quarters = GetQuarters(to_ymd);
+    return static_cast<T>(to_quarters - from_quarters);
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct MonthsBetween {
+  MonthsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    year_month_day from(
+        floor<days>(localizer_.template ConvertTimePoint<Duration>(arg0)));
+    year_month_day to(floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1)));
+    return static_cast<T>(
+        (year_month(to.year(), to.month()) - year_month(from.year(), 
from.month()))
+            .count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct WeeksBetween {
+  WeeksBetween(const DayOfWeekOptions* options, Localizer&& localizer)
+      : week_start_(options->week_start), localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    auto from = floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg0));
+    auto to = floor<days>(localizer_.template 
ConvertTimePoint<Duration>(arg1));
+    const T days_between = static_cast<T>((to - from).count());
+    const T whole_weeks = days_between / 7;
+    const T days_remainder = days_between % 7;
+    // We are counting week boundaries, so there may be one extra week
+    if (days_remainder > 0) {
+      const weekday from_dow(from);
+      const weekday start_of_week(week_start_);
+      const int64_t days = (start_of_week - from_dow).count();
+      if (days > 0 && days <= days_remainder) {
+        return whole_weeks + 1;
+      }
+    } else if (days_remainder < 0) {
+      const weekday from_dow(to);
+      const weekday start_of_week(week_start_);
+      const int64_t days = (start_of_week - from_dow).count();
+      if (days > 0 && days <= -days_remainder) {
+        return whole_weeks - 1;
+      }
+    }
+    return whole_weeks;
+  }
+
+  // Monday = 1, Sunday = 7
+  uint32_t week_start_;
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+struct DayTimeBetween {
+  DayTimeBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    static_assert(std::is_same<T, 
DayTimeIntervalType::DayMilliseconds>::value, "");
+    auto from = localizer_.template ConvertTimePoint<Duration>(arg0);
+    auto to = localizer_.template ConvertTimePoint<Duration>(arg1);
+    const int32_t num_days =
+        static_cast<int32_t>((floor<days>(to) - floor<days>(from)).count());
+    auto from_time = static_cast<int32_t>(
+        std::chrono::duration_cast<std::chrono::milliseconds>(from - 
floor<days>(from))
+            .count());
+    auto to_time = static_cast<int32_t>(
+        std::chrono::duration_cast<std::chrono::milliseconds>(to - 
floor<days>(to))
+            .count());
+    const int32_t num_millis = to_time - from_time;
+    return DayTimeIntervalType::DayMilliseconds{num_days, num_millis};
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Unit, typename Duration, typename Localizer>
+struct UnitsBetween {
+  UnitsBetween(const FunctionOptions* options, Localizer&& localizer)
+      : localizer_(std::move(localizer)) {}
+
+  template <typename T, typename Arg0, typename Arg1>
+  T Call(KernelContext*, Arg0 arg0, Arg1 arg1, Status*) const {
+    auto from = floor<Unit>(localizer_.template 
ConvertTimePoint<Duration>(arg0));
+    auto to = floor<Unit>(localizer_.template 
ConvertTimePoint<Duration>(arg1));
+    return static_cast<T>((to - from).count());
+  }
+
+  Localizer localizer_;
+};
+
+template <typename Duration, typename Localizer>
+using DaysBetween = UnitsBetween<days, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using HoursBetween = UnitsBetween<std::chrono::hours, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using MinutesBetween = UnitsBetween<std::chrono::minutes, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using SecondsBetween = UnitsBetween<std::chrono::seconds, Duration, Localizer>;
+
+template <typename Duration, typename Localizer>
+using MillisBetween = UnitsBetween<std::chrono::milliseconds, Duration, 
Localizer>;

Review comment:
       ```suggestion
   using MillisecondsBetween = UnitsBetween<std::chrono::milliseconds, 
Duration, Localizer>;
   ```

##########
File path: cpp/src/arrow/compute/kernels/scalar_temporal.cc
##########
@@ -1192,6 +1547,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<YearsBetween, TemporalComponentExtractBinary, 
Int64Type>(
+          "years_between", int64(), &years_between_doc);
+  DCHECK_OK(registry->AddFunction(std::move(years_between)));
+
+  auto quarters_between =
+      MakeTemporalBinary<QuartersBetween, TemporalComponentExtractBinary, 
Int64Type>(
+          "quarters_between", int64(), &quarters_between_doc);
+  DCHECK_OK(registry->AddFunction(std::move(quarters_between)));
+
+  auto months_between =
+      MakeTemporalBinary<MonthsBetween, TemporalComponentExtractBinary, 
Int64Type>(
+          "months_between", int64(), &months_between_doc);
+  DCHECK_OK(registry->AddFunction(std::move(months_between)));
+
+  auto weeks_between =
+      MakeTemporalBinary<WeeksBetween, TemporalComponentExtractDayOfWeekBinary,
+                         Int64Type>("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<DayTimeBetween, TemporalComponentExtractBinary,
+                         DayTimeIntervalType>("day_time_between", 
day_time_interval(),
+                                              &day_time_between_doc);
+  DCHECK_OK(registry->AddFunction(std::move(day_time_between)));
+
+  auto days_between =
+      MakeTemporalBinary<DaysBetween, TemporalComponentExtractBinary, 
Int64Type>(
+          "days_between", int64(), &days_between_doc);
+  DCHECK_OK(registry->AddFunction(std::move(days_between)));
+
+  auto hours_between =
+      MakeTemporalBinary<HoursBetween, TemporalComponentExtractBinary, 
Int64Type>(
+          "hours_between", int64(), &hours_between_doc);
+  DCHECK_OK(registry->AddFunction(std::move(hours_between)));
+
+  auto minutes_between =
+      MakeTemporalBinary<MinutesBetween, TemporalComponentExtractBinary, 
Int64Type>(
+          "minutes_between", int64(), &minutes_between_doc);
+  DCHECK_OK(registry->AddFunction(std::move(minutes_between)));
+
+  auto seconds_between =
+      MakeTemporalBinary<SecondsBetween, TemporalComponentExtractBinary, 
Int64Type>(
+          "seconds_between", int64(), &seconds_between_doc);
+  DCHECK_OK(registry->AddFunction(std::move(seconds_between)));
+
+  auto millis_between =
+      MakeTemporalBinary<MillisBetween, TemporalComponentExtractBinary, 
Int64Type>(
+          "millis_between", int64(), &millis_between_doc);
+  DCHECK_OK(registry->AddFunction(std::move(millis_between)));
+
+  auto micros_between =
+      MakeTemporalBinary<MicrosBetween, TemporalComponentExtractBinary, 
Int64Type>(
+          "micros_between", int64(), &micros_between_doc);
+  DCHECK_OK(registry->AddFunction(std::move(micros_between)));
+
+  auto nanos_between =
+      MakeTemporalBinary<NanosBetween, TemporalComponentExtractBinary, 
Int64Type>(
+          "nanos_between", int64(), &nanos_between_doc);
+  DCHECK_OK(registry->AddFunction(std::move(nanos_between)));

Review comment:
       ```suggestion
     auto seconds_between =
         MakeTemporalBinary<SecondsBetween, TemporalComponentExtractBinary, 
Int64Type>(
             "seconds_between", int64(), &seconds_between_doc);
     DCHECK_OK(registry->AddFunction(std::move(seconds_between)));
   
     auto milliseconds_between =
         MakeTemporalBinary<MillisecondsBetween, 
TemporalComponentExtractBinary, Int64Type>(
             "milliseconds_between", int64(), &milliseconds_between_doc);
     DCHECK_OK(registry->AddFunction(std::move(milliseconds_between)));
   
     auto microseconds_between =
         MakeTemporalBinary<MicrosecondsBetween, 
TemporalComponentExtractBinary, Int64Type>(
             "microseconds_between", int64(), &microseconds_between_doc);
     DCHECK_OK(registry->AddFunction(std::move(microseconds_between)));
   
     auto nanoseconds_between =
         MakeTemporalBinary<NanosecondsBetween, TemporalComponentExtractBinary, 
Int64Type>(
             "nanoseconds_between", int64(), &nanoseconds_between_doc);
     DCHECK_OK(registry->AddFunction(std::move(nanoseconds_between)));
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to