From 03efbf4c73e75f1dca5c567e292f688c516b08c4 Mon Sep 17 00:00:00 2001 From: Sten Larsson Date: Thu, 4 Dec 2025 22:08:20 +0100 Subject: [PATCH] Add GArrowRoundTemporalOptions --- c_glib/arrow-glib/compute.cpp | 231 +++++++++++++++++++++ c_glib/arrow-glib/compute.h | 50 +++++ c_glib/arrow-glib/compute.hpp | 6 + c_glib/test/test-round-temporal-options.rb | 68 ++++++ 4 files changed, 355 insertions(+) create mode 100644 c_glib/test/test-round-temporal-options.rb diff --git a/c_glib/arrow-glib/compute.cpp b/c_glib/arrow-glib/compute.cpp index e0b8ac8a7ce..164e1e641c1 100644 --- a/c_glib/arrow-glib/compute.cpp +++ b/c_glib/arrow-glib/compute.cpp @@ -315,6 +315,9 @@ G_BEGIN_DECLS * #GArrowRoundBinaryOptions is a class to customize the * `round_binary` function. * + * #GArrowRoundTemporalOptions is a class to customize the `round_temporal`, + * `floor_temporal`, and `ceil_temporal` functions. + * * There are many functions to compute data on an array. */ @@ -9158,6 +9161,203 @@ garrow_round_binary_options_new(void) g_object_new(GARROW_TYPE_ROUND_BINARY_OPTIONS, nullptr)); } +enum { + PROP_ROUND_TEMPORAL_OPTIONS_MULTIPLE = 1, + PROP_ROUND_TEMPORAL_OPTIONS_UNIT, + PROP_ROUND_TEMPORAL_OPTIONS_WEEK_STARTS_MONDAY, + PROP_ROUND_TEMPORAL_OPTIONS_CEIL_IS_STRICTLY_GREATER, + PROP_ROUND_TEMPORAL_OPTIONS_CALENDAR_BASED_ORIGIN, +}; + +G_DEFINE_TYPE(GArrowRoundTemporalOptions, + garrow_round_temporal_options, + GARROW_TYPE_FUNCTION_OPTIONS) + +static void +garrow_round_temporal_options_set_property(GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + auto options = + garrow_round_temporal_options_get_raw(GARROW_ROUND_TEMPORAL_OPTIONS(object)); + + switch (prop_id) { + case PROP_ROUND_TEMPORAL_OPTIONS_MULTIPLE: + options->multiple = g_value_get_int(value); + break; + case PROP_ROUND_TEMPORAL_OPTIONS_UNIT: + options->unit = static_cast(g_value_get_enum(value)); + break; + case PROP_ROUND_TEMPORAL_OPTIONS_WEEK_STARTS_MONDAY: + options->week_starts_monday = g_value_get_boolean(value); + break; + case PROP_ROUND_TEMPORAL_OPTIONS_CEIL_IS_STRICTLY_GREATER: + options->ceil_is_strictly_greater = g_value_get_boolean(value); + break; + case PROP_ROUND_TEMPORAL_OPTIONS_CALENDAR_BASED_ORIGIN: + options->calendar_based_origin = g_value_get_boolean(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +garrow_round_temporal_options_get_property(GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + auto options = + garrow_round_temporal_options_get_raw(GARROW_ROUND_TEMPORAL_OPTIONS(object)); + + switch (prop_id) { + case PROP_ROUND_TEMPORAL_OPTIONS_MULTIPLE: + g_value_set_int(value, options->multiple); + break; + case PROP_ROUND_TEMPORAL_OPTIONS_UNIT: + g_value_set_enum(value, static_cast(options->unit)); + break; + case PROP_ROUND_TEMPORAL_OPTIONS_WEEK_STARTS_MONDAY: + g_value_set_boolean(value, options->week_starts_monday); + break; + case PROP_ROUND_TEMPORAL_OPTIONS_CEIL_IS_STRICTLY_GREATER: + g_value_set_boolean(value, options->ceil_is_strictly_greater); + break; + case PROP_ROUND_TEMPORAL_OPTIONS_CALENDAR_BASED_ORIGIN: + g_value_set_boolean(value, options->calendar_based_origin); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +garrow_round_temporal_options_init(GArrowRoundTemporalOptions *object) +{ + auto arrow_priv = GARROW_FUNCTION_OPTIONS_GET_PRIVATE(object); + arrow_priv->options = static_cast( + new arrow::compute::RoundTemporalOptions()); +} + +static void +garrow_round_temporal_options_class_init(GArrowRoundTemporalOptionsClass *klass) +{ + auto gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->set_property = garrow_round_temporal_options_set_property; + gobject_class->get_property = garrow_round_temporal_options_get_property; + + arrow::compute::RoundTemporalOptions options; + + GParamSpec *spec; + /** + * GArrowRoundTemporalOptions:multiple: + * + * Number of units to round to. + * + * Since: 23.0.0 + */ + spec = g_param_spec_int("multiple", + "Multiple", + "Number of units to round to", + G_MININT, + G_MAXINT, + options.multiple, + static_cast(G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, + PROP_ROUND_TEMPORAL_OPTIONS_MULTIPLE, + spec); + + /** + * GArrowRoundTemporalOptions:unit: + * + * The unit used for rounding of time. + * + * Since: 23.0.0 + */ + spec = g_param_spec_enum("unit", + "Unit", + "The unit used for rounding of time", + GARROW_TYPE_CALENDAR_UNIT, + static_cast(options.unit), + static_cast(G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, PROP_ROUND_TEMPORAL_OPTIONS_UNIT, spec); + + /** + * GArrowRoundTemporalOptions:week-starts-monday: + * + * What day does the week start with (Monday=true, Sunday=false). + * + * Since: 23.0.0 + */ + spec = + g_param_spec_boolean("week-starts-monday", + "Week Starts Monday", + "What day does the week start with (Monday=true, Sunday=false)", + options.week_starts_monday, + static_cast(G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, + PROP_ROUND_TEMPORAL_OPTIONS_WEEK_STARTS_MONDAY, + spec); + + /** + * GArrowRoundTemporalOptions:ceil-is-strictly-greater: + * + * Enable this flag to return a rounded value that is strictly greater than the input. + * This applies for ceiling only. + * + * Since: 23.0.0 + */ + spec = g_param_spec_boolean( + "ceil-is-strictly-greater", + "Ceil Is Strictly Greater", + "Enable this flag to return a rounded value that is strictly greater than the input", + options.ceil_is_strictly_greater, + static_cast(G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, + PROP_ROUND_TEMPORAL_OPTIONS_CEIL_IS_STRICTLY_GREATER, + spec); + + /** + * GArrowRoundTemporalOptions:calendar-based-origin: + * + * By default time is rounded to a multiple of units since 1970-01-01T00:00:00. + * By setting calendar_based_origin to true, time will be rounded to a number + * of units since the last greater calendar unit. + * + * Since: 23.0.0 + */ + spec = g_param_spec_boolean( + "calendar-based-origin", + "Calendar Based Origin", + "By default time is rounded to a multiple of units since 1970-01-01T00:00:00. By " + "setting calendar_based_origin to true, time will be rounded to a number of units " + "since the last greater calendar unit", + options.calendar_based_origin, + static_cast(G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, + PROP_ROUND_TEMPORAL_OPTIONS_CALENDAR_BASED_ORIGIN, + spec); +} + +/** + * garrow_round_temporal_options_new: + * + * Returns: A newly created #GArrowRoundTemporalOptions. + * + * Since: 23.0.0 + */ +GArrowRoundTemporalOptions * +garrow_round_temporal_options_new(void) +{ + return GARROW_ROUND_TEMPORAL_OPTIONS( + g_object_new(GARROW_TYPE_ROUND_TEMPORAL_OPTIONS, nullptr)); +} + G_END_DECLS arrow::Result @@ -9393,6 +9593,11 @@ garrow_function_options_new_raw(const arrow::compute::FunctionOptions *arrow_opt static_cast(arrow_options); auto options = garrow_round_binary_options_new_raw(arrow_round_binary_options); return GARROW_FUNCTION_OPTIONS(options); + } else if (arrow_type_name == "RoundTemporalOptions") { + const auto arrow_round_temporal_options = + static_cast(arrow_options); + auto options = garrow_round_temporal_options_new_raw(arrow_round_temporal_options); + return GARROW_FUNCTION_OPTIONS(options); } else { auto options = g_object_new(GARROW_TYPE_FUNCTION_OPTIONS, NULL); return GARROW_FUNCTION_OPTIONS(options); @@ -10343,3 +10548,29 @@ garrow_round_binary_options_get_raw(GArrowRoundBinaryOptions *options) return static_cast( garrow_function_options_get_raw(GARROW_FUNCTION_OPTIONS(options))); } + +GArrowRoundTemporalOptions * +garrow_round_temporal_options_new_raw( + const arrow::compute::RoundTemporalOptions *arrow_options) +{ + return GARROW_ROUND_TEMPORAL_OPTIONS( + g_object_new(GARROW_TYPE_ROUND_TEMPORAL_OPTIONS, + "multiple", + arrow_options->multiple, + "unit", + static_cast(arrow_options->unit), + "week-starts-monday", + arrow_options->week_starts_monday, + "ceil-is-strictly-greater", + arrow_options->ceil_is_strictly_greater, + "calendar-based-origin", + arrow_options->calendar_based_origin, + nullptr)); +} + +arrow::compute::RoundTemporalOptions * +garrow_round_temporal_options_get_raw(GArrowRoundTemporalOptions *options) +{ + return static_cast( + garrow_function_options_get_raw(GARROW_FUNCTION_OPTIONS(options))); +} diff --git a/c_glib/arrow-glib/compute.h b/c_glib/arrow-glib/compute.h index ceb85a9a46b..45442bf1aaf 100644 --- a/c_glib/arrow-glib/compute.h +++ b/c_glib/arrow-glib/compute.h @@ -1599,4 +1599,54 @@ GARROW_AVAILABLE_IN_23_0 GArrowRoundBinaryOptions * garrow_round_binary_options_new(void); +/** + * GArrowCalendarUnit: + * @GARROW_CALENDAR_UNIT_NANOSECOND: Nanosecond + * @GARROW_CALENDAR_UNIT_MICROSECOND: Microsecond + * @GARROW_CALENDAR_UNIT_MILLISECOND: Millisecond + * @GARROW_CALENDAR_UNIT_SECOND: Second + * @GARROW_CALENDAR_UNIT_MINUTE: Minute + * @GARROW_CALENDAR_UNIT_HOUR: Hour + * @GARROW_CALENDAR_UNIT_DAY: Day + * @GARROW_CALENDAR_UNIT_WEEK: Week + * @GARROW_CALENDAR_UNIT_MONTH: Month + * @GARROW_CALENDAR_UNIT_QUARTER: Quarter + * @GARROW_CALENDAR_UNIT_YEAR: Year + * + * They correspond to the values of `arrow::compute::CalendarUnit`. + * + * Since: 23.0.0 + */ +typedef enum { + GARROW_CALENDAR_UNIT_NANOSECOND, + GARROW_CALENDAR_UNIT_MICROSECOND, + GARROW_CALENDAR_UNIT_MILLISECOND, + GARROW_CALENDAR_UNIT_SECOND, + GARROW_CALENDAR_UNIT_MINUTE, + GARROW_CALENDAR_UNIT_HOUR, + GARROW_CALENDAR_UNIT_DAY, + GARROW_CALENDAR_UNIT_WEEK, + GARROW_CALENDAR_UNIT_MONTH, + GARROW_CALENDAR_UNIT_QUARTER, + GARROW_CALENDAR_UNIT_YEAR, +} GArrowCalendarUnit; + +#define GARROW_TYPE_CALENDAR_UNIT (garrow_calendar_unit_get_type()) + +#define GARROW_TYPE_ROUND_TEMPORAL_OPTIONS (garrow_round_temporal_options_get_type()) +GARROW_AVAILABLE_IN_23_0 +G_DECLARE_DERIVABLE_TYPE(GArrowRoundTemporalOptions, + garrow_round_temporal_options, + GARROW, + ROUND_TEMPORAL_OPTIONS, + GArrowFunctionOptions) +struct _GArrowRoundTemporalOptionsClass +{ + GArrowFunctionOptionsClass parent_class; +}; + +GARROW_AVAILABLE_IN_23_0 +GArrowRoundTemporalOptions * +garrow_round_temporal_options_new(void); + G_END_DECLS diff --git a/c_glib/arrow-glib/compute.hpp b/c_glib/arrow-glib/compute.hpp index 44793ff0bc2..3a8fc0ff2df 100644 --- a/c_glib/arrow-glib/compute.hpp +++ b/c_glib/arrow-glib/compute.hpp @@ -292,3 +292,9 @@ garrow_round_binary_options_new_raw( const arrow::compute::RoundBinaryOptions *arrow_options); arrow::compute::RoundBinaryOptions * garrow_round_binary_options_get_raw(GArrowRoundBinaryOptions *options); + +GArrowRoundTemporalOptions * +garrow_round_temporal_options_new_raw( + const arrow::compute::RoundTemporalOptions *arrow_options); +arrow::compute::RoundTemporalOptions * +garrow_round_temporal_options_get_raw(GArrowRoundTemporalOptions *options); diff --git a/c_glib/test/test-round-temporal-options.rb b/c_glib/test/test-round-temporal-options.rb new file mode 100644 index 00000000000..8e12b84bd60 --- /dev/null +++ b/c_glib/test/test-round-temporal-options.rb @@ -0,0 +1,68 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +class TestRoundTemporalOptions < Test::Unit::TestCase + include Helper::Buildable + + def setup + @options = Arrow::RoundTemporalOptions.new + end + + def test_multiple + assert_equal(1, @options.multiple) + @options.multiple = 3 + assert_equal(3, @options.multiple) + end + + def test_unit + assert_equal(Arrow::CalendarUnit::DAY, @options.unit) + @options.unit = :hour + assert_equal(Arrow::CalendarUnit::HOUR, @options.unit) + end + + def test_week_starts_monday + assert_equal(true, @options.week_starts_monday?) + @options.week_starts_monday = false + assert_equal(false, @options.week_starts_monday?) + end + + def test_ceil_is_strictly_greater + assert_equal(false, @options.ceil_is_strictly_greater?) + @options.ceil_is_strictly_greater = true + assert_equal(true, @options.ceil_is_strictly_greater?) + end + + def test_calendar_based_origin + assert_equal(false, @options.calendar_based_origin?) + @options.calendar_based_origin = true + assert_equal(true, @options.calendar_based_origin?) + end + + def test_round_temporal_function + # 1504953190000 = 2017-09-09 10:33:10 UTC + args = [ + Arrow::ArrayDatum.new(build_timestamp_array(:milli, [1504953190000])), + ] + @options.multiple = 5 + @options.unit = :minute + round_temporal_function = Arrow::Function.find("round_temporal") + result = round_temporal_function.execute(args, @options).value + # 1504953300000 = 2017-09-09 10:35:00 UTC + expected = build_timestamp_array(:milli, [1504953300000]) + assert_equal(expected, result) + end +end