You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by ap...@apache.org on 2022/07/05 16:40:06 UTC

[arrow] branch master updated: ARROW-16932: [C++] Rounding RoundTemporalOptions.calendar_based_origin doesn't correctly offset non-UTC results (#13462)

This is an automated email from the ASF dual-hosted git repository.

apitrou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new c1a1f47b8a ARROW-16932: [C++] Rounding RoundTemporalOptions.calendar_based_origin doesn't correctly offset non-UTC results (#13462)
c1a1f47b8a is described below

commit c1a1f47b8a2772fc270832902e7d788ee467ea08
Author: Rok Mihevc <ro...@mihevc.org>
AuthorDate: Tue Jul 5 18:40:00 2022 +0200

    ARROW-16932: [C++] Rounding RoundTemporalOptions.calendar_based_origin doesn't correctly offset non-UTC results (#13462)
    
    When rounding to some calendar units the result will be offset in UTC instead of the local timezone. [See discussion](https://github.com/apache/arrow/pull/12154#issuecomment-1169578666).
    This adds a fix and C++ test cases.
    
    Lead-authored-by: Rok <ro...@mihevc.org>
    Co-authored-by: Rok Mihevc <ro...@mihevc.org>
    Signed-off-by: Antoine Pitrou <an...@python.org>
---
 .../arrow/compute/kernels/scalar_temporal_test.cc  | 619 +++++++++++++++------
 .../arrow/compute/kernels/scalar_temporal_unary.cc |  52 +-
 2 files changed, 484 insertions(+), 187 deletions(-)

diff --git a/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc b/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc
index 45bd7819c4..ab1f349c16 100644
--- a/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_temporal_test.cc
@@ -418,6 +418,122 @@ class ScalarTemporalTest : public ::testing::Test {
   }
 };
 
+class ScalarTemporalTestStrictCeil : public ScalarTemporalTest {
+ public:
+  RoundTemporalOptions round_to_1_nanoseconds = RoundTemporalOptions(
+      1, CalendarUnit::NANOSECOND, /*week_starts_monday=*/true,
+      /*ceil_is_strictly_greater=*/true, /*calendar_based_origin=*/false);
+  RoundTemporalOptions round_to_1_microseconds =
+      RoundTemporalOptions(1, CalendarUnit::MICROSECOND, true, true, false);
+  RoundTemporalOptions round_to_1_milliseconds =
+      RoundTemporalOptions(1, CalendarUnit::MILLISECOND, true, true, false);
+  RoundTemporalOptions round_to_1_seconds =
+      RoundTemporalOptions(1, CalendarUnit::SECOND, true, true, false);
+  RoundTemporalOptions round_to_1_minutes =
+      RoundTemporalOptions(1, CalendarUnit::MINUTE, true, true, false);
+  RoundTemporalOptions round_to_1_hours =
+      RoundTemporalOptions(1, CalendarUnit::HOUR, true, true, false);
+  RoundTemporalOptions round_to_1_days =
+      RoundTemporalOptions(1, CalendarUnit::DAY, true, true, false);
+  RoundTemporalOptions round_to_1_weeks =
+      RoundTemporalOptions(1, CalendarUnit::WEEK, true, true, false);
+  RoundTemporalOptions round_to_1_weeks_sunday =
+      RoundTemporalOptions(1, CalendarUnit::WEEK, false, true, false);
+  RoundTemporalOptions round_to_1_months =
+      RoundTemporalOptions(1, CalendarUnit::MONTH, true, true, false);
+  RoundTemporalOptions round_to_1_quarters =
+      RoundTemporalOptions(1, CalendarUnit::QUARTER, true, true, false);
+  RoundTemporalOptions round_to_1_years =
+      RoundTemporalOptions(1, CalendarUnit::YEAR, true, true, false);
+
+  RoundTemporalOptions round_to_15_nanoseconds =
+      RoundTemporalOptions(15, CalendarUnit::NANOSECOND, true, true, false);
+  RoundTemporalOptions round_to_15_microseconds =
+      RoundTemporalOptions(15, CalendarUnit::MICROSECOND, true, true, false);
+  RoundTemporalOptions round_to_15_milliseconds =
+      RoundTemporalOptions(15, CalendarUnit::MILLISECOND, true, true, false);
+  RoundTemporalOptions round_to_13_seconds =
+      RoundTemporalOptions(13, CalendarUnit::SECOND, true, true, false);
+  RoundTemporalOptions round_to_13_minutes =
+      RoundTemporalOptions(13, CalendarUnit::MINUTE, true, true, false);
+  RoundTemporalOptions round_to_15_hours =
+      RoundTemporalOptions(15, CalendarUnit::HOUR, true, true, false);
+  RoundTemporalOptions round_to_15_days =
+      RoundTemporalOptions(15, CalendarUnit::DAY, true, true, false);
+  RoundTemporalOptions round_to_3_weeks =
+      RoundTemporalOptions(3, CalendarUnit::WEEK, true, true, false);
+  RoundTemporalOptions round_to_3_weeks_sunday =
+      RoundTemporalOptions(3, CalendarUnit::WEEK, false, true, false);
+  RoundTemporalOptions round_to_5_months =
+      RoundTemporalOptions(5, CalendarUnit::MONTH, true, true, false);
+  RoundTemporalOptions round_to_15_months =
+      RoundTemporalOptions(15, CalendarUnit::MONTH, true, true, false);
+  RoundTemporalOptions round_to_3_quarters =
+      RoundTemporalOptions(3, CalendarUnit::QUARTER, true, true, false);
+  RoundTemporalOptions round_to_15_quarters =
+      RoundTemporalOptions(15, CalendarUnit::QUARTER, true, true, false);
+  RoundTemporalOptions round_to_15_years =
+      RoundTemporalOptions(15, CalendarUnit::YEAR, true, true, false);
+};
+
+class ScalarTemporalTestMultipleSinceGreaterUnit : public ScalarTemporalTest {
+ public:
+  RoundTemporalOptions round_to_1_nanoseconds = RoundTemporalOptions(
+      1, CalendarUnit::NANOSECOND, /*week_starts_monday=*/true,
+      /*ceil_is_strictly_greater=*/true, /*calendar_based_origin=*/true);
+  RoundTemporalOptions round_to_1_microseconds =
+      RoundTemporalOptions(1, CalendarUnit::MICROSECOND, true, true, true);
+  RoundTemporalOptions round_to_1_milliseconds =
+      RoundTemporalOptions(1, CalendarUnit::MILLISECOND, true, true, true);
+  RoundTemporalOptions round_to_1_seconds =
+      RoundTemporalOptions(1, CalendarUnit::SECOND, true, true, true);
+  RoundTemporalOptions round_to_1_minutes =
+      RoundTemporalOptions(1, CalendarUnit::MINUTE, true, true, true);
+  RoundTemporalOptions round_to_1_hours =
+      RoundTemporalOptions(1, CalendarUnit::HOUR, true, true, true);
+  RoundTemporalOptions round_to_1_days =
+      RoundTemporalOptions(1, CalendarUnit::DAY, true, true, true);
+  RoundTemporalOptions round_to_1_weeks =
+      RoundTemporalOptions(1, CalendarUnit::WEEK, true, true, true);
+  RoundTemporalOptions round_to_1_weeks_sunday =
+      RoundTemporalOptions(1, CalendarUnit::WEEK, false, true, true);
+  RoundTemporalOptions round_to_1_months =
+      RoundTemporalOptions(1, CalendarUnit::MONTH, true, true, true);
+  RoundTemporalOptions round_to_1_quarters =
+      RoundTemporalOptions(1, CalendarUnit::QUARTER, true, true, true);
+  RoundTemporalOptions round_to_1_years =
+      RoundTemporalOptions(1, CalendarUnit::YEAR, true, true, true);
+
+  RoundTemporalOptions round_to_15_nanoseconds =
+      RoundTemporalOptions(15, CalendarUnit::NANOSECOND, true, true, true);
+  RoundTemporalOptions round_to_15_microseconds =
+      RoundTemporalOptions(15, CalendarUnit::MICROSECOND, true, true, true);
+  RoundTemporalOptions round_to_15_milliseconds =
+      RoundTemporalOptions(15, CalendarUnit::MILLISECOND, true, true, true);
+  RoundTemporalOptions round_to_13_seconds =
+      RoundTemporalOptions(13, CalendarUnit::SECOND, true, true, true);
+  RoundTemporalOptions round_to_13_minutes =
+      RoundTemporalOptions(13, CalendarUnit::MINUTE, true, true, true);
+  RoundTemporalOptions round_to_15_hours =
+      RoundTemporalOptions(15, CalendarUnit::HOUR, true, true, true);
+  RoundTemporalOptions round_to_15_days =
+      RoundTemporalOptions(15, CalendarUnit::DAY, true, true, true);
+  RoundTemporalOptions round_to_3_weeks =
+      RoundTemporalOptions(3, CalendarUnit::WEEK, true, true, true);
+  RoundTemporalOptions round_to_3_weeks_sunday =
+      RoundTemporalOptions(3, CalendarUnit::WEEK, false, true, true);
+  RoundTemporalOptions round_to_5_months =
+      RoundTemporalOptions(5, CalendarUnit::MONTH, true, true, true);
+  RoundTemporalOptions round_to_15_months =
+      RoundTemporalOptions(15, CalendarUnit::MONTH, true, true, true);
+  RoundTemporalOptions round_to_3_quarters =
+      RoundTemporalOptions(3, CalendarUnit::QUARTER, true, true, true);
+  RoundTemporalOptions round_to_15_quarters =
+      RoundTemporalOptions(15, CalendarUnit::QUARTER, true, true, true);
+  RoundTemporalOptions round_to_15_years =
+      RoundTemporalOptions(15, CalendarUnit::YEAR, true, true, true);
+};
+
 TEST_F(ScalarTemporalTest, TestTemporalComponentExtractionAllTemporalTypes) {
   std::vector<std::shared_ptr<DataType>> units = {date32(), date64(),
                                                   timestamp(TimeUnit::NANO)};
@@ -2229,57 +2345,8 @@ TEST_F(ScalarTemporalTest, TestCeilTemporal) {
   CheckScalarUnary(op, unit, times, unit, ceil_15_years, &round_to_15_years);
 }
 
-TEST_F(ScalarTemporalTest, TestCeilTemporalStrictCeil) {
+TEST_F(ScalarTemporalTestStrictCeil, TestCeilTemporalStrictCeil) {
   std::string op = "ceil_temporal";
-  RoundTemporalOptions round_to_1_nanoseconds =
-      RoundTemporalOptions(1, CalendarUnit::NANOSECOND, true, true, false);
-  RoundTemporalOptions round_to_1_microseconds =
-      RoundTemporalOptions(1, CalendarUnit::MICROSECOND, true, true, false);
-  RoundTemporalOptions round_to_1_milliseconds =
-      RoundTemporalOptions(1, CalendarUnit::MILLISECOND, true, true, false);
-  RoundTemporalOptions round_to_1_seconds =
-      RoundTemporalOptions(1, CalendarUnit::SECOND, true, true, false);
-  RoundTemporalOptions round_to_1_minutes =
-      RoundTemporalOptions(1, CalendarUnit::MINUTE, true, true, false);
-  RoundTemporalOptions round_to_1_hours =
-      RoundTemporalOptions(1, CalendarUnit::HOUR, true, true, false);
-  RoundTemporalOptions round_to_1_days =
-      RoundTemporalOptions(1, CalendarUnit::DAY, true, true, false);
-  RoundTemporalOptions round_to_1_weeks =
-      RoundTemporalOptions(1, CalendarUnit::WEEK, true, true, false);
-  RoundTemporalOptions round_to_1_weeks_sunday =
-      RoundTemporalOptions(1, CalendarUnit::WEEK, false, true, false);
-  RoundTemporalOptions round_to_1_months =
-      RoundTemporalOptions(1, CalendarUnit::MONTH, true, true, false);
-  RoundTemporalOptions round_to_1_quarters =
-      RoundTemporalOptions(1, CalendarUnit::QUARTER, true, true, false);
-  RoundTemporalOptions round_to_1_years =
-      RoundTemporalOptions(1, CalendarUnit::YEAR, true, true, false);
-
-  RoundTemporalOptions round_to_15_nanoseconds =
-      RoundTemporalOptions(15, CalendarUnit::NANOSECOND, true, true, false);
-  RoundTemporalOptions round_to_15_microseconds =
-      RoundTemporalOptions(15, CalendarUnit::MICROSECOND, true, true, false);
-  RoundTemporalOptions round_to_15_milliseconds =
-      RoundTemporalOptions(15, CalendarUnit::MILLISECOND, true, true, false);
-  RoundTemporalOptions round_to_13_seconds =
-      RoundTemporalOptions(13, CalendarUnit::SECOND, true, true, false);
-  RoundTemporalOptions round_to_13_minutes =
-      RoundTemporalOptions(13, CalendarUnit::MINUTE, true, true, false);
-  RoundTemporalOptions round_to_15_hours =
-      RoundTemporalOptions(15, CalendarUnit::HOUR, true, true, false);
-  RoundTemporalOptions round_to_15_days =
-      RoundTemporalOptions(15, CalendarUnit::DAY, true, true, false);
-  RoundTemporalOptions round_to_15_weeks =
-      RoundTemporalOptions(15, CalendarUnit::WEEK, true, true, false);
-  RoundTemporalOptions round_to_15_weeks_sunday =
-      RoundTemporalOptions(15, CalendarUnit::WEEK, false, true, false);
-  RoundTemporalOptions round_to_15_months =
-      RoundTemporalOptions(15, CalendarUnit::MONTH, true, true, false);
-  RoundTemporalOptions round_to_15_quarters =
-      RoundTemporalOptions(15, CalendarUnit::QUARTER, true, true, false);
-  RoundTemporalOptions round_to_15_years =
-      RoundTemporalOptions(15, CalendarUnit::YEAR, true, true, false);
 
   const char* ceil_1_nanosecond =
       R"(["1970-01-01 00:00:59.123456790", "2000-02-29 23:23:24.000000000",
@@ -2413,16 +2480,16 @@ TEST_F(ScalarTemporalTest, TestCeilTemporalStrictCeil) {
           "2020-01-09", "2020-01-09", "2010-01-01", "2010-01-16", "2010-01-16",
           "2010-01-16", "2006-01-07", "2006-01-07", "2009-01-06", "2009-01-06",
           "2012-01-06", null])";
-  const char* ceil_15_weeks =
-      R"(["1970-04-13", "2000-03-06", "1899-04-10", "2033-07-11", "2020-01-06",
-          "2020-01-06", "2020-01-06", "2010-03-29", "2010-03-29", "2010-03-29",
-          "2010-03-29", "2006-03-20", "2006-03-20", "2009-02-02", "2009-02-02",
-          "2012-04-02", null])";
-  const char* ceil_15_weeks_sunday =
-      R"(["1970-04-12", "2000-03-05", "1899-04-09", "2033-07-10", "2020-01-05",
-          "2020-01-05", "2020-01-05", "2010-03-28", "2010-03-28", "2010-03-28",
-          "2010-03-28", "2006-03-19", "2006-03-19", "2009-02-01", "2009-02-01",
-          "2012-04-01", null])";
+  const char* ceil_3_weeks =
+      R"(["1970-01-19", "2000-03-06", "1899-01-16", "2033-05-30", "2020-01-06",
+          "2020-01-06", "2020-01-06", "2010-01-04", "2010-01-04", "2010-01-04",
+          "2010-01-25", "2006-01-16", "2006-01-16", "2009-01-12", "2009-01-12",
+          "2012-01-09", null])";
+  const char* ceil_3_weeks_sunday =
+      R"(["1970-01-18", "2000-03-05", "1899-01-15", "2033-05-29", "2020-01-05",
+          "2020-01-05", "2020-01-05", "2010-01-03", "2010-01-03", "2010-01-24",
+          "2010-01-24", "2006-01-15", "2006-01-15", "2009-01-11", "2009-01-11",
+          "2012-01-08", null])";
   const char* ceil_15_months =
       R"(["1971-04-01", "2001-04-01", "1900-01-01", "2033-10-01", "2021-04-01",
           "2020-01-01", "2020-01-01", "2010-01-01", "2011-04-01", "2011-04-01",
@@ -2460,40 +2527,15 @@ TEST_F(ScalarTemporalTest, TestCeilTemporalStrictCeil) {
   CheckScalarUnary(op, unit, times, unit, ceil_13_minute, &round_to_13_minutes);
   CheckScalarUnary(op, unit, times, unit, ceil_15_hour, &round_to_15_hours);
   CheckScalarUnary(op, unit, times, unit, ceil_15_day, &round_to_15_days);
-  CheckScalarUnary(op, unit, times, unit, ceil_15_weeks, &round_to_15_weeks);
-  CheckScalarUnary(op, unit, times, unit, ceil_15_weeks_sunday,
-                   &round_to_15_weeks_sunday);
+  CheckScalarUnary(op, unit, times, unit, ceil_3_weeks, &round_to_3_weeks);
+  CheckScalarUnary(op, unit, times, unit, ceil_3_weeks_sunday, &round_to_3_weeks_sunday);
   CheckScalarUnary(op, unit, times, unit, ceil_15_months, &round_to_15_months);
   CheckScalarUnary(op, unit, times, unit, ceil_15_quarters, &round_to_15_quarters);
   CheckScalarUnary(op, unit, times, unit, ceil_15_years, &round_to_15_years);
 }
 
-TEST_F(ScalarTemporalTest, TestCeilTemporalMultipleSinceGreaterUnit) {
+TEST_F(ScalarTemporalTestMultipleSinceGreaterUnit, CeilUTC) {
   std::string op = "ceil_temporal";
-  RoundTemporalOptions round_to_15_nanoseconds =
-      RoundTemporalOptions(15, CalendarUnit::NANOSECOND, true, true, true);
-  RoundTemporalOptions round_to_15_microseconds =
-      RoundTemporalOptions(15, CalendarUnit::MICROSECOND, true, true, true);
-  RoundTemporalOptions round_to_15_milliseconds =
-      RoundTemporalOptions(15, CalendarUnit::MILLISECOND, true, true, true);
-  RoundTemporalOptions round_to_13_seconds =
-      RoundTemporalOptions(13, CalendarUnit::SECOND, true, true, true);
-  RoundTemporalOptions round_to_13_minutes =
-      RoundTemporalOptions(13, CalendarUnit::MINUTE, true, true, true);
-  RoundTemporalOptions round_to_15_hours =
-      RoundTemporalOptions(15, CalendarUnit::HOUR, true, true, true);
-  RoundTemporalOptions round_to_15_days =
-      RoundTemporalOptions(15, CalendarUnit::DAY, true, true, true);
-  RoundTemporalOptions round_to_15_weeks =
-      RoundTemporalOptions(15, CalendarUnit::WEEK, true, true, true);
-  RoundTemporalOptions round_to_15_weeks_sunday =
-      RoundTemporalOptions(15, CalendarUnit::WEEK, false, true, true);
-  RoundTemporalOptions round_to_15_months =
-      RoundTemporalOptions(15, CalendarUnit::MONTH, true, true, true);
-  RoundTemporalOptions round_to_15_quarters =
-      RoundTemporalOptions(15, CalendarUnit::QUARTER, true, true, true);
-  RoundTemporalOptions round_to_15_years =
-      RoundTemporalOptions(15, CalendarUnit::YEAR, true, true, true);
 
   // Data for tests below was generaed via lubridate with the exception
   // of week data because lubridate currently does not support rounding to
@@ -2551,16 +2593,16 @@ TEST_F(ScalarTemporalTest, TestCeilTemporalMultipleSinceGreaterUnit) {
           "2020-01-16", "2020-01-15", "2019-12-31", "2010-01-15",
           "2010-01-16", "2010-01-16", "2010-01-16", "2006-01-16",
           "2006-01-15", "2008-12-31", "2008-12-31", "2012-01-16", null])";
-  const char* ceil_15_weeks =
-      R"(["1970-04-13", "2000-04-17", "1899-04-17", "2033-08-01", "2020-04-13",
-          "2020-04-13", "2020-04-13", "2010-04-19", "2010-04-19", "2010-04-19",
-          "2010-04-19", "2006-04-17", "2006-04-17", "2009-02-23", "2009-04-13",
-          "2012-04-16", null])";
-  const char* ceil_15_weeks_sunday =
-      R"(["1970-04-19", "2000-04-16", "1899-04-16", "2033-07-31", "2020-04-12",
-          "2020-04-12", "2020-04-12", "2010-04-18", "2010-04-18", "2010-04-18",
-          "2010-04-18", "2006-04-16", "2006-04-16", "2009-04-19", "2009-04-19",
-          "2012-04-15", null])";
+  const char* ceil_3_weeks =
+      R"(["1970-01-19", "2000-03-06", "1899-01-23", "2033-05-30", "2020-01-20",
+          "2020-01-20", "2020-01-20", "2010-01-25", "2010-01-25", "2010-01-25",
+          "2010-01-25", "2006-01-23", "2006-01-23", "2009-01-12", "2009-01-19",
+          "2012-01-23", null])";
+  const char* ceil_3_weeks_sunday =
+      R"(["1970-01-25", "2000-03-05", "1899-01-22", "2033-05-29", "2020-01-19",
+          "2020-01-19", "2020-01-19", "2010-01-24", "2010-01-24", "2010-01-24",
+          "2010-01-24", "2006-01-22", "2006-01-22", "2009-01-25", "2009-01-25",
+          "2012-01-22", null])";
   const char* ceil_15_months =
       R"(["1971-04-01", "2001-04-01", "1900-04-01", "2034-04-01",
           "2021-04-01", "2020-04-01", "2020-04-01", "2010-04-01",
@@ -2585,16 +2627,112 @@ TEST_F(ScalarTemporalTest, TestCeilTemporalMultipleSinceGreaterUnit) {
   CheckScalarUnary(op, unit, times, unit, ceil_13_minute, &round_to_13_minutes);
   CheckScalarUnary(op, unit, times, unit, ceil_15_hour, &round_to_15_hours);
   CheckScalarUnary(op, unit, times, unit, ceil_15_day, &round_to_15_days);
-  CheckScalarUnary(op, unit, times, unit, ceil_15_weeks, &round_to_15_weeks);
-  CheckScalarUnary(op, unit, times, unit, ceil_15_weeks_sunday,
-                   &round_to_15_weeks_sunday);
+  CheckScalarUnary(op, unit, times, unit, ceil_3_weeks, &round_to_3_weeks);
+  CheckScalarUnary(op, unit, times, unit, ceil_3_weeks_sunday, &round_to_3_weeks_sunday);
   CheckScalarUnary(op, unit, times, unit, ceil_15_months, &round_to_15_months);
   CheckScalarUnary(op, unit, times, unit, ceil_15_quarters, &round_to_15_quarters);
   CheckScalarUnary(op, unit, times, unit, ceil_15_years, &round_to_15_years);
 }
 
+TEST_F(ScalarTemporalTestMultipleSinceGreaterUnit, CeilZoned) {
+  std::string op = "ceil_temporal";
+
+  // Data for tests below was generated via lubridate with the exception
+  // of week data because lubridate currently does not support rounding to
+  // multiple of week.
+  const char* ceil_15_nanosecond =
+      R"(["1970-01-01 00:00:59.123456795", "2000-02-29 23:23:24.000000005",
+          "1899-01-01 00:59:20.001001015", "2033-05-18 03:33:20.000000015",
+          "2020-01-01 01:05:05.001000015", "2019-12-31 02:10:10.002000015",
+          "2019-12-30 03:15:15.003000015", "2009-12-31 04:20:20.004132015",
+          "2010-01-01 05:25:25.005321015", "2010-01-03 06:30:30.006163015",
+          "2010-01-04 07:35:35.000000015", "2006-01-01 08:40:40.000000015",
+          "2005-12-31 09:45:45.000000015", "2008-12-28 00:00:00.000000015",
+          "2008-12-29 00:00:00.000000015", "2012-01-01 01:02:03.000000015", null])";
+  const char* ceil_15_microsecond =
+      R"(["1970-01-01 00:00:59.123465", "2000-02-29 23:23:24.000005",
+          "1899-01-01 00:59:20.001015", "2033-05-18 03:33:20.000015",
+          "2020-01-01 01:05:05.001015", "2019-12-31 02:10:10.002015",
+          "2019-12-30 03:15:15.003015", "2009-12-31 04:20:20.004135",
+          "2010-01-01 05:25:25.005330", "2010-01-03 06:30:30.006165",
+          "2010-01-04 07:35:35.000015", "2006-01-01 08:40:40.000015",
+          "2005-12-31 09:45:45.000015", "2008-12-28 00:00:00.000015",
+          "2008-12-29 00:00:00.000015", "2012-01-01 01:02:03.000015", null])";
+  const char* ceil_15_millisecond =
+      R"(["1970-01-01 00:00:59.135", "2000-02-29 23:23:24.005",
+          "1899-01-01 00:59:20.015", "2033-05-18 03:33:20.015",
+          "2020-01-01 01:05:05.015", "2019-12-31 02:10:10.015",
+          "2019-12-30 03:15:15.015", "2009-12-31 04:20:20.015",
+          "2010-01-01 05:25:25.015", "2010-01-03 06:30:30.015",
+          "2010-01-04 07:35:35.015", "2006-01-01 08:40:40.015",
+          "2005-12-31 09:45:45.015", "2008-12-28 00:00:00.015",
+          "2008-12-29 00:00:00.015", "2012-01-01 01:02:03.015", null])";
+  const char* ceil_13_second = R"([
+    "1970-01-01 00:01:05", "2000-02-29 23:23:26", "1899-01-01 00:59:26", "2033-05-18 03:33:26",
+    "2020-01-01 01:05:13", "2019-12-31 02:10:13", "2019-12-30 03:15:26", "2009-12-31 04:20:26",
+    "2010-01-01 05:25:26", "2010-01-03 06:30:39", "2010-01-04 07:35:39", "2006-01-01 08:40:52",
+    "2005-12-31 09:45:52", "2008-12-28 00:00:13", "2008-12-29 00:00:13", "2012-01-01 01:02:13", null])";
+  const char* ceil_13_minute = R"([
+    "1970-01-01 00:09:00", "2000-02-29 23:35:00", "1899-01-01 01:05:00", "2033-05-18 03:43:00",
+    "2020-01-01 01:09:00", "2019-12-31 02:22:00", "2019-12-30 03:22:00", "2009-12-31 04:22:00",
+    "2010-01-01 05:35:00", "2010-01-03 06:43:00", "2010-01-04 07:43:00", "2006-01-01 08:43:00",
+    "2005-12-31 09:56:00", "2008-12-28 00:09:00", "2008-12-29 00:09:00", "2012-01-01 01:09:00", null])";
+  const char* ceil_15_hour = R"([
+    "1970-01-01 05:30:00", "2000-03-01 04:30:00", "1899-01-01 06:00:00", "2033-05-18 05:30:00",
+    "2020-01-01 04:30:00", "2019-12-31 04:30:00", "2019-12-30 04:30:00", "2009-12-31 04:30:00",
+    "2010-01-01 19:30:00", "2010-01-03 19:30:00", "2010-01-04 19:30:00", "2006-01-01 19:30:00",
+    "2005-12-31 19:30:00", "2008-12-28 04:30:00", "2008-12-29 04:30:00", "2012-01-01 04:30:00", null])";
+  const char* ceil_15_day = R"([
+    "1970-01-15 14:30:00", "2000-03-15 13:30:00", "1899-01-15 15:00:00", "2033-05-30 14:30:00",
+    "2020-01-15 13:30:00", "2020-01-14 13:30:00", "2019-12-30 13:30:00", "2010-01-14 13:30:00",
+    "2010-01-15 13:30:00", "2010-01-15 13:30:00", "2010-01-15 13:30:00", "2006-01-15 13:30:00",
+    "2006-01-14 13:30:00", "2008-12-30 13:30:00", "2008-12-30 13:30:00", "2012-01-15 13:30:00", null])";
+  const char* ceil_3_weeks = R"([
+    "1970-01-18 14:30:00", "2000-03-05 13:30:00", "1899-01-22 15:00:00", "2033-05-29 14:30:00",
+    "2020-01-19 13:30:00", "2020-01-19 13:30:00", "2020-01-19 13:30:00", "2010-01-24 13:30:00",
+    "2010-01-24 13:30:00", "2010-01-24 13:30:00", "2010-01-24 13:30:00", "2006-01-22 13:30:00",
+    "2006-01-22 13:30:00", "2009-01-11 13:30:00", "2009-01-18 13:30:00", "2012-01-22 13:30:00", null])";
+  const char* ceil_3_weeks_sunday = R"([
+    "1970-01-24 14:30:00", "2000-03-25 13:30:00", "1899-01-21 15:00:00", "2033-05-28 14:30:00",
+    "2020-01-18 13:30:00", "2020-01-18 13:30:00", "2020-01-18 13:30:00", "2010-01-23 13:30:00",
+    "2010-01-23 13:30:00", "2010-01-23 13:30:00", "2010-01-23 13:30:00", "2006-01-21 13:30:00",
+    "2006-01-21 13:30:00", "2009-01-24 13:30:00", "2009-01-24 13:30:00", "2012-01-21 13:30:00", null])";
+  const char* ceil_5_months = R"([
+    "1970-05-31 14:30:00", "2000-05-31 14:30:00", "1899-05-31 14:30:00", "2033-05-31 14:30:00",
+    "2020-05-31 14:30:00", "2020-03-31 13:30:00", "2020-03-31 13:30:00", "2010-03-31 13:30:00",
+    "2010-05-31 14:30:00", "2010-05-31 14:30:00", "2010-05-31 14:30:00", "2006-05-31 14:30:00",
+    "2006-03-31 13:30:00", "2009-03-31 13:30:00", "2009-03-31 13:30:00", "2012-05-31 14:30:00", null])";
+  const char* ceil_3_quarters = R"([
+    "1970-09-30 14:30:00", "2000-09-30 14:30:00", "1899-09-30 14:30:00", "2033-09-30 14:30:00",
+    "2020-09-30 14:30:00", "2020-06-30 14:30:00", "2020-06-30 14:30:00", "2010-06-30 14:30:00",
+    "2010-09-30 14:30:00", "2010-09-30 14:30:00", "2010-09-30 14:30:00", "2006-09-30 14:30:00",
+    "2006-06-30 14:30:00", "2009-06-30 14:30:00", "2009-06-30 14:30:00", "2012-09-30 14:30:00", null])";
+  const char* ceil_15_years = R"([
+    "1979-12-31 13:30:00", "2009-12-31 13:30:00", "1904-12-31 14:30:00", "2039-12-31 13:30:00",
+    "2024-12-31 13:30:00", "2024-12-31 13:30:00", "2024-12-31 13:30:00", "2009-12-31 13:30:00",
+    "2024-12-31 13:30:00", "2024-12-31 13:30:00", "2024-12-31 13:30:00", "2009-12-31 13:30:00",
+    "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2024-12-31 13:30:00", null])";
+
+  // Australia/Broken_Hill timezone is defined as UTC+9:30 and UTC+10:30 during DST.
+  // DST runs from first Sunday in October to first Sunday in April.
+  auto unit = timestamp(TimeUnit::NANO, "Australia/Broken_Hill");
+  CheckScalarUnary(op, unit, times, unit, ceil_15_nanosecond, &round_to_15_nanoseconds);
+  CheckScalarUnary(op, unit, times, unit, ceil_15_microsecond, &round_to_15_microseconds);
+  CheckScalarUnary(op, unit, times, unit, ceil_15_millisecond, &round_to_15_milliseconds);
+  CheckScalarUnary(op, unit, times, unit, ceil_13_second, &round_to_13_seconds);
+  CheckScalarUnary(op, unit, times, unit, ceil_13_minute, &round_to_13_minutes);
+  CheckScalarUnary(op, unit, times, unit, ceil_15_hour, &round_to_15_hours);
+  CheckScalarUnary(op, unit, times, unit, ceil_15_day, &round_to_15_days);
+  CheckScalarUnary(op, unit, times, unit, ceil_3_weeks, &round_to_3_weeks);
+  CheckScalarUnary(op, unit, times, unit, ceil_3_weeks_sunday, &round_to_3_weeks_sunday);
+  CheckScalarUnary(op, unit, times, unit, ceil_5_months, &round_to_5_months);
+  CheckScalarUnary(op, unit, times, unit, ceil_3_quarters, &round_to_3_quarters);
+  CheckScalarUnary(op, unit, times, unit, ceil_15_years, &round_to_15_years);
+}
+
 TEST_F(ScalarTemporalTest, TestFloorTemporal) {
   std::string op = "floor_temporal";
+
   const char* floor_1_nanosecond =
       R"(["1970-01-01 00:00:59.123456789", "2000-02-29 23:23:23.999999999",
           "1899-01-01 00:59:20.001001001", "2033-05-18 03:33:20.000000000",
@@ -2784,32 +2922,8 @@ TEST_F(ScalarTemporalTest, TestFloorTemporal) {
   CheckScalarUnary(op, unit, times, unit, floor_15_years, &round_to_15_years);
 }
 
-TEST_F(ScalarTemporalTest, TestFloorTemporalMultipleSinceGreaterUnit) {
+TEST_F(ScalarTemporalTestMultipleSinceGreaterUnit, FloorUTC) {
   std::string op = "floor_temporal";
-  RoundTemporalOptions round_to_15_nanoseconds =
-      RoundTemporalOptions(15, CalendarUnit::NANOSECOND, true, true, true);
-  RoundTemporalOptions round_to_15_microseconds =
-      RoundTemporalOptions(15, CalendarUnit::MICROSECOND, true, true, true);
-  RoundTemporalOptions round_to_15_milliseconds =
-      RoundTemporalOptions(15, CalendarUnit::MILLISECOND, true, true, true);
-  RoundTemporalOptions round_to_13_seconds =
-      RoundTemporalOptions(13, CalendarUnit::SECOND, true, true, true);
-  RoundTemporalOptions round_to_13_minutes =
-      RoundTemporalOptions(13, CalendarUnit::MINUTE, true, true, true);
-  RoundTemporalOptions round_to_15_hours =
-      RoundTemporalOptions(15, CalendarUnit::HOUR, true, true, true);
-  RoundTemporalOptions round_to_15_days =
-      RoundTemporalOptions(15, CalendarUnit::DAY, true, true, true);
-  RoundTemporalOptions round_to_15_weeks =
-      RoundTemporalOptions(15, CalendarUnit::WEEK, true, true, true);
-  RoundTemporalOptions round_to_15_weeks_sunday =
-      RoundTemporalOptions(15, CalendarUnit::WEEK, false, true, true);
-  RoundTemporalOptions round_to_15_months =
-      RoundTemporalOptions(15, CalendarUnit::MONTH, true, true, true);
-  RoundTemporalOptions round_to_15_quarters =
-      RoundTemporalOptions(15, CalendarUnit::QUARTER, true, true, true);
-  RoundTemporalOptions round_to_15_years =
-      RoundTemporalOptions(15, CalendarUnit::YEAR, true, true, true);
 
   // Data for tests below was generaed via lubridate with the exception
   // of week data because lubridate currently does not support rounding to
@@ -2867,13 +2981,13 @@ TEST_F(ScalarTemporalTest, TestFloorTemporalMultipleSinceGreaterUnit) {
           "2020-01-01", "2019-12-31", "2019-12-16", "2009-12-31",
           "2010-01-01", "2010-01-01", "2010-01-01", "2006-01-01",
           "2005-12-31", "2008-12-16", "2008-12-16", "2012-01-01", null])";
-  const char* floor_15_weeks =
-      R"(["1969-12-29", "2000-01-03", "1899-01-02", "2033-04-18",
+  const char* floor_3_weeks =
+      R"(["1969-12-29", "2000-02-14", "1899-01-02", "2033-05-09",
           "2019-12-30", "2019-12-30", "2019-12-30", "2010-01-04",
           "2010-01-04", "2010-01-04", "2010-01-04", "2006-01-02",
-          "2006-01-02", "2008-11-10", "2008-12-29", "2012-01-02", null])";
-  const char* floor_15_weeks_sunday =
-      R"(["1970-01-04", "2000-01-02", "1899-01-01", "2033-04-17",
+          "2006-01-02", "2008-12-22", "2008-12-29", "2012-01-02",  null])";
+  const char* floor_3_weeks_sunday =
+      R"(["1970-01-04", "2000-02-13", "1899-01-01", "2033-05-08",
           "2019-12-29", "2019-12-29", "2019-12-29", "2010-01-03",
           "2010-01-03", "2010-01-03", "2010-01-03", "2006-01-01",
           "2006-01-01", "2009-01-04", "2009-01-04", "2012-01-01", null])";
@@ -2903,16 +3017,114 @@ TEST_F(ScalarTemporalTest, TestFloorTemporalMultipleSinceGreaterUnit) {
   CheckScalarUnary(op, unit, times, unit, floor_13_minute, &round_to_13_minutes);
   CheckScalarUnary(op, unit, times, unit, floor_15_hour, &round_to_15_hours);
   CheckScalarUnary(op, unit, times, unit, floor_15_day, &round_to_15_days);
-  CheckScalarUnary(op, unit, times, unit, floor_15_weeks, &round_to_15_weeks);
-  CheckScalarUnary(op, unit, times, unit, floor_15_weeks_sunday,
-                   &round_to_15_weeks_sunday);
+  CheckScalarUnary(op, unit, times, unit, floor_3_weeks, &round_to_3_weeks);
+  CheckScalarUnary(op, unit, times, unit, floor_3_weeks_sunday, &round_to_3_weeks_sunday);
   CheckScalarUnary(op, unit, times, unit, floor_15_months, &round_to_15_months);
   CheckScalarUnary(op, unit, times, unit, floor_15_quarters, &round_to_15_quarters);
   CheckScalarUnary(op, unit, times, unit, floor_15_years, &round_to_15_years);
 }
 
+TEST_F(ScalarTemporalTestMultipleSinceGreaterUnit, FloorZoned) {
+  std::string op = "floor_temporal";
+
+  // Data for tests below was generated via lubridate with the exception
+  // of week data because lubridate currently does not support rounding to
+  // multiple of week.
+  const char* floor_15_nanosecond =
+      R"(["1970-01-01 00:00:59.123456780", "2000-02-29 23:23:23.999999990",
+          "1899-01-01 00:59:20.001001000", "2033-05-18 03:33:20.000000000",
+          "2020-01-01 01:05:05.001000000", "2019-12-31 02:10:10.002000000",
+          "2019-12-30 03:15:15.003000000", "2009-12-31 04:20:20.004132000",
+          "2010-01-01 05:25:25.005321000", "2010-01-03 06:30:30.006163000",
+          "2010-01-04 07:35:35.000000000", "2006-01-01 08:40:40.000000000",
+          "2005-12-31 09:45:45.000000000", "2008-12-28 00:00:00.000000000",
+          "2008-12-29 00:00:00.000000000", "2012-01-01 01:02:03.000000000", null])";
+  const char* floor_15_microsecond =
+      R"(["1970-01-01 00:00:59.123450", "2000-02-29 23:23:23.999990",
+          "1899-01-01 00:59:20.001000", "2033-05-18 03:33:20.000000",
+          "2020-01-01 01:05:05.001000", "2019-12-31 02:10:10.002000",
+          "2019-12-30 03:15:15.003000", "2009-12-31 04:20:20.004120",
+          "2010-01-01 05:25:25.005315", "2010-01-03 06:30:30.006150",
+          "2010-01-04 07:35:35.000000", "2006-01-01 08:40:40.000000",
+          "2005-12-31 09:45:45.000000", "2008-12-28 00:00:00.000000",
+          "2008-12-29 00:00:00.000000", "2012-01-01 01:02:03.000000", null])";
+  const char* floor_15_millisecond =
+      R"(["1970-01-01 00:00:59.120", "2000-02-29 23:23:23.990",
+          "1899-01-01 00:59:20.000", "2033-05-18 03:33:20.000",
+          "2020-01-01 01:05:05.000", "2019-12-31 02:10:10.000",
+          "2019-12-30 03:15:15.000", "2009-12-31 04:20:20.000",
+          "2010-01-01 05:25:25.000", "2010-01-03 06:30:30.000",
+          "2010-01-04 07:35:35.000", "2006-01-01 08:40:40.000",
+          "2005-12-31 09:45:45.000", "2008-12-28 00:00:00.000",
+          "2008-12-29 00:00:00.000", "2012-01-01 01:02:03.000", null])";
+  const char* floor_13_second = R"([
+    "1970-01-01 00:00:52", "2000-02-29 23:23:13", "1899-01-01 00:59:13", "2033-05-18 03:33:13",
+    "2020-01-01 01:05:00", "2019-12-31 02:10:00", "2019-12-30 03:15:13", "2009-12-31 04:20:13",
+    "2010-01-01 05:25:13", "2010-01-03 06:30:26", "2010-01-04 07:35:26", "2006-01-01 08:40:39",
+    "2005-12-31 09:45:39", "2008-12-28 00:00:00", "2008-12-29 00:00:00", "2012-01-01 01:02:00", null])";
+  const char* floor_13_minute = R"([
+    "1969-12-31 23:56:00", "2000-02-29 23:22:00", "1899-01-01 00:52:00", "2033-05-18 03:30:00",
+    "2020-01-01 00:56:00", "2019-12-31 02:09:00", "2019-12-30 03:09:00", "2009-12-31 04:09:00",
+    "2010-01-01 05:22:00", "2010-01-03 06:30:00", "2010-01-04 07:30:00", "2006-01-01 08:30:00",
+    "2005-12-31 09:43:00", "2008-12-27 23:56:00", "2008-12-28 23:56:00", "2012-01-01 00:56:00", null])";
+  const char* floor_15_hour = R"([
+    "1969-12-31 14:30:00", "2000-02-29 13:30:00", "1898-12-31 15:00:00", "2033-05-17 14:30:00",
+    "2019-12-31 13:30:00", "2019-12-30 13:30:00", "2019-12-29 13:30:00", "2009-12-30 13:30:00",
+    "2010-01-01 04:30:00", "2010-01-03 04:30:00", "2010-01-04 04:30:00", "2006-01-01 04:30:00",
+    "2005-12-31 04:30:00", "2008-12-27 13:30:00", "2008-12-28 13:30:00", "2011-12-31 13:30:00", null])";
+  const char* floor_15_day = R"([
+    "1969-12-31 14:30:00", "2000-02-29 13:30:00", "1898-12-31 15:00:00", "2033-05-15 14:30:00",
+    "2019-12-31 13:30:00", "2019-12-30 13:30:00", "2019-12-15 13:30:00", "2009-12-30 13:30:00",
+    "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2005-12-31 13:30:00",
+    "2005-12-30 13:30:00", "2008-12-15 13:30:00", "2008-12-15 13:30:00", "2011-12-31 13:30:00", null])";
+  const char* floor_3_weeks = R"([
+    "1969-12-28 14:30:00", "2000-02-13 13:30:00", "1899-01-01 15:00:00", "2033-05-08 14:30:00",
+    "2019-12-29 13:30:00", "2019-12-29 13:30:00", "2019-12-29 13:30:00", "2010-01-03 13:30:00",
+    "2010-01-03 13:30:00", "2010-01-03 13:30:00", "2010-01-03 13:30:00", "2006-01-01 13:30:00",
+    "2006-01-01 13:30:00", "2008-12-21 13:30:00", "2008-12-28 13:30:00", "2012-01-01 13:30:00", null])";
+  const char* floor_3_weeks_sunday = R"([
+    "1970-01-03 14:30:00", "2000-03-04 13:30:00", "1898-12-31 15:00:00", "2033-05-07 14:30:00",
+    "2019-12-28 13:30:00", "2019-12-28 13:30:00", "2019-12-28 13:30:00", "2010-01-02 13:30:00",
+    "2010-01-02 13:30:00", "2010-01-02 13:30:00", "2010-01-02 13:30:00", "2005-12-31 13:30:00",
+    "2005-12-31 13:30:00", "2009-01-03 13:30:00", "2009-01-03 13:30:00", "2011-12-31 13:30:00", null])";
+  const char* floor_5_months = R"([
+    "1969-12-31 14:30:00", "1999-12-31 13:30:00", "1898-12-31 15:00:00", "2032-12-31 13:30:00",
+    "2019-12-31 13:30:00", "2019-10-31 13:30:00", "2019-10-31 13:30:00", "2009-10-31 13:30:00",
+    "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2005-12-31 13:30:00",
+    "2005-10-31 13:30:00", "2008-10-31 13:30:00", "2008-10-31 13:30:00", "2011-12-31 13:30:00", null])";
+  const char* floor_3_quarters = R"([
+    "1969-12-31 14:30:00", "1999-12-31 13:30:00", "1898-12-31 15:00:00", "2032-12-31 13:30:00",
+    "2019-12-31 13:30:00", "2019-09-30 14:30:00", "2019-09-30 14:30:00", "2009-09-30 14:30:00",
+    "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2005-12-31 13:30:00",
+    "2005-09-30 14:30:00", "2008-09-30 14:30:00", "2008-09-30 14:30:00", "2011-12-31 13:30:00", null])";
+  const char* floor_15_years = R"([
+    "1964-12-31 14:30:00", "1994-12-31 13:30:00", "1889-12-31 14:34:12", "2024-12-31 13:30:00",
+    "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", "1994-12-31 13:30:00",
+    "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", "1994-12-31 13:30:00",
+    "1994-12-31 13:30:00", "1994-12-31 13:30:00", "1994-12-31 13:30:00", "2009-12-31 13:30:00", null])";
+
+  // Australia/Broken_Hill timezone is defined as UTC+9:30 and UTC+10:30 during DST.
+  // DST runs from first Sunday in October to first Sunday in April.
+  auto unit = timestamp(TimeUnit::NANO, "Australia/Broken_Hill");
+  CheckScalarUnary(op, unit, times, unit, floor_15_nanosecond, &round_to_15_nanoseconds);
+  CheckScalarUnary(op, unit, times, unit, floor_15_microsecond,
+                   &round_to_15_microseconds);
+  CheckScalarUnary(op, unit, times, unit, floor_15_millisecond,
+                   &round_to_15_milliseconds);
+  CheckScalarUnary(op, unit, times, unit, floor_13_second, &round_to_13_seconds);
+  CheckScalarUnary(op, unit, times, unit, floor_13_minute, &round_to_13_minutes);
+  CheckScalarUnary(op, unit, times, unit, floor_15_hour, &round_to_15_hours);
+  CheckScalarUnary(op, unit, times, unit, floor_15_day, &round_to_15_days);
+  CheckScalarUnary(op, unit, times, unit, floor_3_weeks, &round_to_3_weeks);
+  CheckScalarUnary(op, unit, times, unit, floor_3_weeks_sunday, &round_to_3_weeks_sunday);
+  CheckScalarUnary(op, unit, times, unit, floor_5_months, &round_to_5_months);
+  CheckScalarUnary(op, unit, times, unit, floor_3_quarters, &round_to_3_quarters);
+  CheckScalarUnary(op, unit, times, unit, floor_15_years, &round_to_15_years);
+}
+
 TEST_F(ScalarTemporalTest, TestRoundTemporal) {
   std::string op = "round_temporal";
+
   const char* round_1_nanoseconds =
       R"(["1970-01-01 00:00:59.123456789", "2000-02-29 23:23:23.999999999",
           "1899-01-01 00:59:20.001001001", "2033-05-18 03:33:20.000000000",
@@ -3123,32 +3335,8 @@ TEST_F(ScalarTemporalTest, TestCeilFloorRoundTemporalBrussels) {
   CheckScalarUnary("round_temporal", unit, times, unit, round_2_hours, &round_to_2_hours);
 }
 
-TEST_F(ScalarTemporalTest, TestRoundTemporalMultipleSinceGreaterUnit) {
+TEST_F(ScalarTemporalTestMultipleSinceGreaterUnit, RoundUTC) {
   std::string op = "round_temporal";
-  RoundTemporalOptions round_to_15_nanoseconds =
-      RoundTemporalOptions(15, CalendarUnit::NANOSECOND, true, true, true);
-  RoundTemporalOptions round_to_15_microseconds =
-      RoundTemporalOptions(15, CalendarUnit::MICROSECOND, true, true, true);
-  RoundTemporalOptions round_to_15_milliseconds =
-      RoundTemporalOptions(15, CalendarUnit::MILLISECOND, true, true, true);
-  RoundTemporalOptions round_to_13_seconds =
-      RoundTemporalOptions(13, CalendarUnit::SECOND, true, true, true);
-  RoundTemporalOptions round_to_13_minutes =
-      RoundTemporalOptions(13, CalendarUnit::MINUTE, true, true, true);
-  RoundTemporalOptions round_to_15_hours =
-      RoundTemporalOptions(15, CalendarUnit::HOUR, true, true, true);
-  RoundTemporalOptions round_to_15_days =
-      RoundTemporalOptions(15, CalendarUnit::DAY, true, true, true);
-  RoundTemporalOptions round_to_15_weeks =
-      RoundTemporalOptions(15, CalendarUnit::WEEK, true, true, true);
-  RoundTemporalOptions round_to_15_weeks_sunday =
-      RoundTemporalOptions(15, CalendarUnit::WEEK, false, true, true);
-  RoundTemporalOptions round_to_5_months =
-      RoundTemporalOptions(5, CalendarUnit::MONTH, true, true, true);
-  RoundTemporalOptions round_to_15_quarters =
-      RoundTemporalOptions(15, CalendarUnit::QUARTER, true, true, true);
-  RoundTemporalOptions round_to_15_years =
-      RoundTemporalOptions(15, CalendarUnit::YEAR, true, true, true);
 
   // Data for tests below was generaed via lubridate with the exception
   // of week data because lubridate currently does not support rounding to
@@ -3206,13 +3394,13 @@ TEST_F(ScalarTemporalTest, TestRoundTemporalMultipleSinceGreaterUnit) {
           "2020-01-01", "2019-12-31", "2019-12-31", "2009-12-31",
           "2010-01-01", "2010-01-01", "2010-01-01", "2006-01-01",
           "2005-12-31", "2008-12-31", "2008-12-31", "2012-01-01", null])";
-  const char* round_15_weeks =
-      R"(["1969-12-29", "2000-04-17", "1899-01-02", "2033-04-18",
+  const char* round_3_weeks =
+      R"(["1969-12-29", "2000-03-06", "1899-01-02", "2033-05-09",
           "2019-12-30", "2019-12-30", "2019-12-30", "2010-01-04",
           "2010-01-04", "2010-01-04", "2010-01-04", "2006-01-02",
-          "2006-01-02", "2008-11-10", "2008-12-29", "2012-01-02", null])";
-  const char* round_15_weeks_sunday =
-      R"(["1970-01-04", "2000-04-16", "1899-01-01", "2033-04-17",
+          "2006-01-02", "2008-12-22", "2008-12-29", "2012-01-02", null])";
+  const char* round_3_weeks_sunday =
+      R"(["1970-01-04", "2000-03-05", "1899-01-01", "2033-05-08",
           "2019-12-29", "2019-12-29", "2019-12-29", "2010-01-03",
           "2010-01-03", "2010-01-03", "2010-01-03", "2006-01-01",
           "2006-01-01", "2009-01-04", "2009-01-04", "2012-01-01", null])";
@@ -3241,14 +3429,111 @@ TEST_F(ScalarTemporalTest, TestRoundTemporalMultipleSinceGreaterUnit) {
   CheckScalarUnary(op, unit, times, unit, round_13_minute, &round_to_13_minutes);
   CheckScalarUnary(op, unit, times, unit, round_15_hour, &round_to_15_hours);
   CheckScalarUnary(op, unit, times, unit, round_15_day, &round_to_15_days);
-  CheckScalarUnary(op, unit, times, unit, round_15_weeks, &round_to_15_weeks);
-  CheckScalarUnary(op, unit, times, unit, round_15_weeks_sunday,
-                   &round_to_15_weeks_sunday);
+  CheckScalarUnary(op, unit, times, unit, round_3_weeks, &round_to_3_weeks);
+  CheckScalarUnary(op, unit, times, unit, round_3_weeks_sunday, &round_to_3_weeks_sunday);
   CheckScalarUnary(op, unit, times, unit, round_5_months, &round_to_5_months);
   CheckScalarUnary(op, unit, times, unit, round_15_quarters, &round_to_15_quarters);
   CheckScalarUnary(op, unit, times, unit, round_15_years, &round_to_15_years);
 }
 
+TEST_F(ScalarTemporalTestMultipleSinceGreaterUnit, RoundZoned) {
+  std::string op = "round_temporal";
+
+  // Data for tests below was generated via lubridate with the exception
+  // of week data because lubridate currently does not support rounding to
+  // multiple of week.
+  const char* round_15_nanosecond =
+      R"(["1970-01-01 00:00:59.123456795", "2000-02-29 23:23:24.000000005",
+          "1899-01-01 00:59:20.001001000", "2033-05-18 03:33:20.000000000",
+          "2020-01-01 01:05:05.001000000", "2019-12-31 02:10:10.002000000",
+          "2019-12-30 03:15:15.003000000", "2009-12-31 04:20:20.004132000",
+          "2010-01-01 05:25:25.005321000", "2010-01-03 06:30:30.006163000",
+          "2010-01-04 07:35:35.000000000", "2006-01-01 08:40:40.000000000",
+          "2005-12-31 09:45:45.000000000", "2008-12-28 00:00:00.000000000",
+          "2008-12-29 00:00:00.000000000", "2012-01-01 01:02:03.000000000", null])";
+  const char* round_15_microsecond =
+      R"(["1970-01-01 00:00:59.123450", "2000-02-29 23:23:24.000005",
+          "1899-01-01 00:59:20.001000", "2033-05-18 03:33:20.000000",
+          "2020-01-01 01:05:05.001000", "2019-12-31 02:10:10.002000",
+          "2019-12-30 03:15:15.003000", "2009-12-31 04:20:20.004135",
+          "2010-01-01 05:25:25.005315", "2010-01-03 06:30:30.006165",
+          "2010-01-04 07:35:35.000000", "2006-01-01 08:40:40.000000",
+          "2005-12-31 09:45:45.000000", "2008-12-28 00:00:00.000000",
+          "2008-12-29 00:00:00.000000", "2012-01-01 01:02:03.000000", null])";
+  const char* round_15_millisecond =
+      R"(["1970-01-01 00:00:59.120", "2000-02-29 23:23:24.005",
+          "1899-01-01 00:59:20.000", "2033-05-18 03:33:20.000",
+          "2020-01-01 01:05:05.000", "2019-12-31 02:10:10.000",
+          "2019-12-30 03:15:15.000", "2009-12-31 04:20:20.000",
+          "2010-01-01 05:25:25.000", "2010-01-03 06:30:30.000",
+          "2010-01-04 07:35:35.000", "2006-01-01 08:40:40.000",
+          "2005-12-31 09:45:45.000", "2008-12-28 00:00:00.000",
+          "2008-12-29 00:00:00.000", "2012-01-01 01:02:03.000", null])";
+  const char* round_13_second = R"([
+    "1970-01-01 00:01:05", "2000-02-29 23:23:26", "1899-01-01 00:59:26", "2033-05-18 03:33:26",
+    "2020-01-01 01:05:00", "2019-12-31 02:10:13", "2019-12-30 03:15:13", "2009-12-31 04:20:26",
+    "2010-01-01 05:25:26", "2010-01-03 06:30:26", "2010-01-04 07:35:39", "2006-01-01 08:40:39",
+    "2005-12-31 09:45:39", "2008-12-28 00:00:00", "2008-12-29 00:00:00", "2012-01-01 01:02:00", null])";
+  const char* round_13_minute = R"([
+    "1969-12-31 23:56:00", "2000-02-29 23:22:00", "1899-01-01 01:05:00", "2033-05-18 03:30:00",
+    "2020-01-01 01:09:00", "2019-12-31 02:09:00", "2019-12-30 03:09:00", "2009-12-31 04:22:00",
+    "2010-01-01 05:22:00", "2010-01-03 06:30:00", "2010-01-04 07:30:00", "2006-01-01 08:43:00",
+    "2005-12-31 09:43:00", "2008-12-27 23:56:00", "2008-12-28 23:56:00", "2012-01-01 00:56:00", null])";
+  const char* round_15_hour = R"([
+    "1970-01-01 05:30:00", "2000-03-01 04:30:00", "1899-01-01 06:00:00", "2033-05-18 05:30:00",
+    "2020-01-01 04:30:00", "2019-12-31 04:30:00", "2019-12-30 04:30:00", "2009-12-31 04:30:00",
+    "2010-01-01 04:30:00", "2010-01-03 04:30:00", "2010-01-04 04:30:00", "2006-01-01 04:30:00",
+    "2005-12-31 04:30:00", "2008-12-28 04:30:00", "2008-12-29 04:30:00", "2012-01-01 04:30:00", null])";
+  const char* round_15_day = R"([
+    "1969-12-31 14:30:00", "2000-02-29 13:30:00", "1898-12-31 15:00:00", "2033-05-15 14:30:00",
+    "2019-12-31 13:30:00", "2019-12-30 13:30:00", "2019-12-30 13:30:00", "2009-12-30 13:30:00",
+    "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2005-12-31 13:30:00",
+    "2005-12-30 13:30:00", "2008-12-30 13:30:00", "2008-12-30 13:30:00", "2011-12-31 13:30:00", null])";
+  const char* round_3_weeks = R"([
+    "1969-12-28 14:30:00", "2000-03-05 13:30:00", "1899-01-01 15:00:00", "2033-05-08 14:30:00",
+    "2019-12-29 13:30:00", "2019-12-29 13:30:00", "2019-12-29 13:30:00", "2010-01-03 13:30:00",
+    "2010-01-03 13:30:00", "2010-01-03 13:30:00", "2010-01-03 13:30:00", "2006-01-01 13:30:00",
+    "2006-01-01 13:30:00", "2008-12-21 13:30:00", "2008-12-28 13:30:00", "2012-01-01 13:30:00",null])";
+  const char* round_3_weeks_sunday = R"([
+    "1970-01-03 14:30:00", "2000-03-04 13:30:00", "1898-12-31 15:00:00", "2033-05-28 14:30:00",
+    "2019-12-28 13:30:00", "2019-12-28 13:30:00", "2019-12-28 13:30:00", "2010-01-02 13:30:00",
+    "2010-01-02 13:30:00", "2010-01-02 13:30:00", "2010-01-02 13:30:00", "2005-12-31 13:30:00",
+    "2005-12-31 13:30:00", "2009-01-03 13:30:00", "2009-01-03 13:30:00", "2011-12-31 13:30:00", null])";
+  const char* round_5_months = R"([
+    "1969-12-31 14:30:00", "1999-12-31 13:30:00", "1898-12-31 15:00:00", "2033-05-31 14:30:00",
+    "2019-12-31 13:30:00", "2019-10-31 13:30:00", "2019-10-31 13:30:00", "2009-10-31 13:30:00",
+    "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2005-12-31 13:30:00",
+    "2005-10-31 13:30:00", "2008-10-31 13:30:00", "2008-10-31 13:30:00", "2011-12-31 13:30:00", null])";
+  const char* round_3_quarters = R"([
+    "1969-12-31 14:30:00", "1999-12-31 13:30:00", "1898-12-31 15:00:00", "2033-09-30 14:30:00",
+    "2019-12-31 13:30:00", "2019-09-30 14:30:00", "2019-09-30 14:30:00", "2009-09-30 14:30:00",
+    "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2005-12-31 13:30:00",
+    "2005-09-30 14:30:00", "2008-09-30 14:30:00", "2008-09-30 14:30:00", "2011-12-31 13:30:00", null])";
+  const char* round_15_years = R"([
+    "1964-12-31 14:30:00", "1994-12-31 13:30:00", "1904-12-31 14:30:00", "2039-12-31 13:30:00",
+    "2024-12-31 13:30:00", "2024-12-31 13:30:00", "2024-12-31 13:30:00", "2009-12-31 13:30:00",
+    "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00",
+    "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", "2009-12-31 13:30:00", null])";
+
+  // Australia/Broken_Hill timezone is defined as UTC+9:30 and UTC+10:30 during DST.
+  // DST runs from first Sunday in October to first Sunday in April.
+  auto unit = timestamp(TimeUnit::NANO, "Australia/Broken_Hill");
+  CheckScalarUnary(op, unit, times, unit, round_15_nanosecond, &round_to_15_nanoseconds);
+  CheckScalarUnary(op, unit, times, unit, round_15_microsecond,
+                   &round_to_15_microseconds);
+  CheckScalarUnary(op, unit, times, unit, round_15_millisecond,
+                   &round_to_15_milliseconds);
+  CheckScalarUnary(op, unit, times, unit, round_13_second, &round_to_13_seconds);
+  CheckScalarUnary(op, unit, times, unit, round_13_minute, &round_to_13_minutes);
+  CheckScalarUnary(op, unit, times, unit, round_15_hour, &round_to_15_hours);
+  CheckScalarUnary(op, unit, times, unit, round_15_day, &round_to_15_days);
+  CheckScalarUnary(op, unit, times, unit, round_3_weeks, &round_to_3_weeks);
+  CheckScalarUnary(op, unit, times, unit, round_3_weeks_sunday, &round_to_3_weeks_sunday);
+  CheckScalarUnary(op, unit, times, unit, round_5_months, &round_to_5_months);
+  CheckScalarUnary(op, unit, times, unit, round_3_quarters, &round_to_3_quarters);
+  CheckScalarUnary(op, unit, times, unit, round_15_years, &round_to_15_years);
+}
+
 TEST_F(ScalarTemporalTest, TestCeilFloorRoundTemporalKolkata) {
   // Kolkata timezone was defined as UTC+5:21:10 from 1871 to 1906 when it changed to
   // IST (UTC+05:30) without DST. This test is to check rounding is done in historical
diff --git a/cpp/src/arrow/compute/kernels/scalar_temporal_unary.cc b/cpp/src/arrow/compute/kernels/scalar_temporal_unary.cc
index 96b9390840..f8da1338b5 100644
--- a/cpp/src/arrow/compute/kernels/scalar_temporal_unary.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_temporal_unary.cc
@@ -706,14 +706,14 @@ year_month_day GetFlooredYmd(int64_t arg, const int multiple,
     // covered here.
     switch (options.unit) {
       case compute::CalendarUnit::MONTH: {
-        const auto m =
-            static_cast<uint32_t>(ymd.month()) / options.multiple * options.multiple;
-        return year_month_day(ymd.year() / 1 / 1) + months{m};
+        const auto m = (static_cast<uint32_t>(ymd.month()) - 1) / options.multiple *
+                       options.multiple;
+        return year_month_day(ymd.year() / jan / 1) + months{m};
       }
       case compute::CalendarUnit::QUARTER: {
-        const auto m = static_cast<uint32_t>(ymd.month()) / (options.multiple * 3) *
+        const auto m = (static_cast<uint32_t>(ymd.month()) - 1) / (options.multiple * 3) *
                        (options.multiple * 3);
-        return year_month_day(ymd.year() / 1 / 1) + months{m};
+        return year_month_day(ymd.year() / jan / 1) + months{m};
       }
       default:
         return ymd;
@@ -945,14 +945,16 @@ struct CeilTemporal {
         year_month_day ymd = GetFlooredYmd<Duration, Localizer>(arg, options.multiple,
                                                                 options, localizer_);
         ymd += months{options.multiple};
-        t = localizer_.ConvertDays(ymd.year() / ymd.month() / 1).time_since_epoch();
+        t = localizer_.template ConvertLocalToSys<Duration>(
+            local_days(ymd.year() / ymd.month() / 1).time_since_epoch(), st);
         break;
       }
       case compute::CalendarUnit::QUARTER: {
         year_month_day ymd = GetFlooredYmd<Duration, Localizer>(arg, 3 * options.multiple,
                                                                 options, localizer_);
         ymd += months{3 * options.multiple};
-        t = localizer_.ConvertDays(ymd.year() / ymd.month() / 1).time_since_epoch();
+        t = localizer_.template ConvertLocalToSys<Duration>(
+            local_days(ymd.year() / ymd.month() / 1).time_since_epoch(), st);
         break;
       }
       case compute::CalendarUnit::YEAR: {
@@ -960,7 +962,8 @@ struct CeilTemporal {
             floor<days>(localizer_.template ConvertTimePoint<Duration>(arg)));
         year y{(static_cast<int32_t>(ymd.year()) / options.multiple + 1) *
                options.multiple};
-        t = localizer_.ConvertDays(y / jan / 1).time_since_epoch();
+        t = localizer_.template ConvertLocalToSys<Duration>(
+            local_days(y / jan / 1).time_since_epoch(), st);
         break;
       }
       default:
@@ -1020,20 +1023,23 @@ struct FloorTemporal {
       case compute::CalendarUnit::MONTH: {
         year_month_day ymd = GetFlooredYmd<Duration, Localizer>(arg, options.multiple,
                                                                 options, localizer_);
-        t = localizer_.ConvertDays(ymd.year() / ymd.month() / 1).time_since_epoch();
+        t = localizer_.template ConvertLocalToSys<Duration>(
+            local_days(ymd.year() / ymd.month() / 1).time_since_epoch(), st);
         break;
       }
       case compute::CalendarUnit::QUARTER: {
         year_month_day ymd = GetFlooredYmd<Duration, Localizer>(arg, 3 * options.multiple,
                                                                 options, localizer_);
-        t = localizer_.ConvertDays(ymd.year() / ymd.month() / 1).time_since_epoch();
+        t = localizer_.template ConvertLocalToSys<Duration>(
+            local_days(ymd.year() / ymd.month() / 1).time_since_epoch(), st);
         break;
       }
       case compute::CalendarUnit::YEAR: {
         year_month_day ymd(
             floor<days>(localizer_.template ConvertTimePoint<Duration>(arg)));
         year y{(static_cast<int32_t>(ymd.year()) / options.multiple) * options.multiple};
-        t = localizer_.ConvertDays(y / jan / 1).time_since_epoch();
+        t = localizer_.template ConvertLocalToSys<Duration>(
+            local_days(y / jan / 1).time_since_epoch(), st);
         break;
       }
       default:
@@ -1095,11 +1101,13 @@ struct RoundTemporal {
         year_month_day ymd = GetFlooredYmd<Duration, Localizer>(arg, options.multiple,
                                                                 options, localizer_);
 
-        auto f = localizer_.ConvertDays(ymd.year() / ymd.month() / 1);
+        auto f = localizer_.template ConvertLocalToSys<Duration>(
+            local_days(ymd.year() / ymd.month() / 1).time_since_epoch(), st);
         ymd += months{options.multiple};
-        auto c = localizer_.ConvertDays(ymd.year() / ymd.month() / 1);
+        auto c = localizer_.template ConvertLocalToSys<Duration>(
+            local_days(ymd.year() / ymd.month() / 1).time_since_epoch(), st);
 
-        t = (t0 - f >= c - t0) ? c.time_since_epoch() : f.time_since_epoch();
+        t = (t0.time_since_epoch() - f >= c - t0.time_since_epoch()) ? c : f;
         break;
       }
       case compute::CalendarUnit::QUARTER: {
@@ -1107,21 +1115,25 @@ struct RoundTemporal {
         year_month_day ymd = GetFlooredYmd<Duration, Localizer>(arg, 3 * options.multiple,
                                                                 options, localizer_);
 
-        auto f = localizer_.ConvertDays(ymd.year() / ymd.month() / 1);
+        auto f = localizer_.template ConvertLocalToSys<Duration>(
+            local_days(ymd.year() / ymd.month() / 1).time_since_epoch(), st);
         ymd += months{3 * options.multiple};
-        auto c = localizer_.ConvertDays(ymd.year() / ymd.month() / 1);
+        auto c = localizer_.template ConvertLocalToSys<Duration>(
+            local_days(ymd.year() / ymd.month() / 1).time_since_epoch(), st);
 
-        t = (t0 - f >= c - t0) ? c.time_since_epoch() : f.time_since_epoch();
+        t = (t0.time_since_epoch() - f >= c - t0.time_since_epoch()) ? c : f;
         break;
       }
       case compute::CalendarUnit::YEAR: {
         auto t0 = localizer_.template ConvertTimePoint<Duration>(arg);
         year_month_day ymd(floor<days>(t0));
         year y{(static_cast<int32_t>(ymd.year()) / options.multiple) * options.multiple};
-        auto f = localizer_.ConvertDays(y / jan / 1);
-        auto c = localizer_.ConvertDays((y + years{options.multiple}) / jan / 1);
+        auto f = localizer_.template ConvertLocalToSys<Duration>(
+            local_days(y / jan / 1).time_since_epoch(), st);
+        auto c = localizer_.template ConvertLocalToSys<Duration>(
+            local_days((y + years{options.multiple}) / jan / 1).time_since_epoch(), st);
 
-        t = (t0 - f >= c - t0) ? c.time_since_epoch() : f.time_since_epoch();
+        t = (t0.time_since_epoch() - f >= c - t0.time_since_epoch()) ? c : f;
         break;
       }
       default: