Skip to content

Commit

Permalink
GH-39702: [GLib] Add support for time zone in GArrowTimestampDataType (
Browse files Browse the repository at this point in the history
…#39717)

### Rationale for this change

Timestamp data type in Apache Arrow supports time zone but Apache Arrow C GLib didn't support it. Timestamp data type has "timezone-aware" mode and "timezone-naive" mode. If a timestamp data type has a valid time zone information, it uses "timezone-aware" mode. If a timestamp data type doesn't have a valid time zone information, it uses "timezone-naive" mode. Apache Arrow C GLib should support both of them.

### What changes are included in this PR?

This change adds a new `GTimeZone *time_zone` argument to `garrow_timestamp_data_type_new()` instead of adding a new `garrow_timestamp_data_type_new_time_zone()` function. This breaks backward compatibility for Apache Arrow C GLib users. But this still keeps backward compatibility for users of bindings such as Ruby and Vala. Because the new `GTimeZone *time_zone` is nullable.

I tried to use the "adding a new
`garrow_timestamp_data_type_new_time_zone()` function" approach but Vala didn't like it. Both of
`garrow_timestamp_data_type_new_time_zone()` (constructor) and `garrow_timestamp_data_type_get_time_zone()` (instance method or property reader) were mapped to
`GArrow.TimestampDataType.time_zone()`.

So I chose the "adding a new argument to
`garrow_timestamp_data_type_new()`" approach.

### Are these changes tested?

Yes.

### Are there any user-facing changes?

Yes.

**This PR includes breaking changes to public APIs.**

* Closes: #39702

Authored-by: Sutou Kouhei <[email protected]>
Signed-off-by: Sutou Kouhei <[email protected]>
  • Loading branch information
kou authored Jan 22, 2024
1 parent 26f515a commit c33ffb0
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 12 deletions.
109 changes: 99 additions & 10 deletions c_glib/arrow-glib/basic-data-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ G_BEGIN_DECLS
* data types.
*/

typedef struct GArrowDataTypePrivate_ {
struct GArrowDataTypePrivate {
std::shared_ptr<arrow::DataType> data_type;
} GArrowDataTypePrivate;
};

enum {
PROP_DATA_TYPE = 1
Expand Down Expand Up @@ -1113,9 +1113,71 @@ garrow_date64_data_type_new(void)
}


G_DEFINE_TYPE(GArrowTimestampDataType,
garrow_timestamp_data_type,
GARROW_TYPE_TEMPORAL_DATA_TYPE)
struct GArrowTimestampDataTypePrivate {
GTimeZone *time_zone;
};

enum {
PROP_TIME_ZONE = 1
};

G_DEFINE_TYPE_WITH_PRIVATE(GArrowTimestampDataType,
garrow_timestamp_data_type,
GARROW_TYPE_TEMPORAL_DATA_TYPE)

#define GARROW_TIMESTAMP_DATA_TYPE_GET_PRIVATE(object) \
static_cast<GArrowTimestampDataTypePrivate *>( \
garrow_timestamp_data_type_get_instance_private( \
GARROW_TIMESTAMP_DATA_TYPE(object)))

static void
garrow_timestamp_data_type_dispose(GObject *object)
{
auto priv = GARROW_TIMESTAMP_DATA_TYPE_GET_PRIVATE(object);

if (priv->time_zone) {
g_time_zone_unref(priv->time_zone);
priv->time_zone = nullptr;
}

G_OBJECT_CLASS(garrow_timestamp_data_type_parent_class)->dispose(object);
}

static void
garrow_timestamp_data_type_set_property(GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
auto priv = GARROW_TIMESTAMP_DATA_TYPE_GET_PRIVATE(object);

switch (prop_id) {
case PROP_TIME_ZONE:
priv->time_zone = static_cast<GTimeZone *>(g_value_dup_boxed(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}

static void
garrow_timestamp_data_type_get_property(GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
auto priv = GARROW_TIMESTAMP_DATA_TYPE_GET_PRIVATE(object);

switch (prop_id) {
case PROP_TIME_ZONE:
g_value_set_boxed(value, priv->time_zone);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}

static void
garrow_timestamp_data_type_init(GArrowTimestampDataType *object)
Expand All @@ -1125,11 +1187,32 @@ garrow_timestamp_data_type_init(GArrowTimestampDataType *object)
static void
garrow_timestamp_data_type_class_init(GArrowTimestampDataTypeClass *klass)
{
auto gobject_class = G_OBJECT_CLASS(klass);
gobject_class->dispose = garrow_timestamp_data_type_dispose;
gobject_class->set_property = garrow_timestamp_data_type_set_property;
gobject_class->get_property = garrow_timestamp_data_type_get_property;

GParamSpec *spec;
/**
* GArrowTimestampDataType:time-zone:
*
* The time zone of this data type.
*
* Since: 16.0.0
*/
spec = g_param_spec_boxed("time-zone",
"Time zone",
"The time zone of this data type",
G_TYPE_TIME_ZONE,
static_cast<GParamFlags>(G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property(gobject_class, PROP_TIME_ZONE, spec);
}

/**
* garrow_timestamp_data_type_new:
* @unit: The unit of the timestamp data.
* @time_zone: (nullable): The time zone of the timestamp data.
*
* Returns: A newly created the number of
* seconds/milliseconds/microseconds/nanoseconds since UNIX epoch in
Expand All @@ -1138,30 +1221,36 @@ garrow_timestamp_data_type_class_init(GArrowTimestampDataTypeClass *klass)
* Since: 0.7.0
*/
GArrowTimestampDataType *
garrow_timestamp_data_type_new(GArrowTimeUnit unit)
garrow_timestamp_data_type_new(GArrowTimeUnit unit,
GTimeZone *time_zone)
{
auto arrow_unit = garrow_time_unit_to_raw(unit);
auto arrow_data_type = arrow::timestamp(arrow_unit);
std::string arrow_timezone;
if (time_zone) {
arrow_timezone = g_time_zone_get_identifier(time_zone);
}
auto arrow_data_type = arrow::timestamp(arrow_unit, arrow_timezone);
auto data_type =
GARROW_TIMESTAMP_DATA_TYPE(g_object_new(GARROW_TYPE_TIMESTAMP_DATA_TYPE,
"data-type", &arrow_data_type,
"time-zone", time_zone,
NULL));
return data_type;
}

/**
* garrow_timestamp_data_type_get_unit:
* @timestamp_data_type: The #GArrowTimestampDataType.
* @data_type: The #GArrowTimestampDataType.
*
* Returns: The unit of the timestamp data type.
*
* Since: 0.8.0
*/
GArrowTimeUnit
garrow_timestamp_data_type_get_unit(GArrowTimestampDataType *timestamp_data_type)
garrow_timestamp_data_type_get_unit(GArrowTimestampDataType *data_type)
{
const auto arrow_data_type =
garrow_data_type_get_raw(GARROW_DATA_TYPE(timestamp_data_type));
garrow_data_type_get_raw(GARROW_DATA_TYPE(data_type));
const auto arrow_timestamp_data_type =
std::static_pointer_cast<arrow::TimestampType>(arrow_data_type);
return garrow_time_unit_from_raw(arrow_timestamp_data_type->unit());
Expand Down
6 changes: 4 additions & 2 deletions c_glib/arrow-glib/basic-data-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,9 +425,11 @@ struct _GArrowTimestampDataTypeClass
GArrowTemporalDataTypeClass parent_class;
};

GArrowTimestampDataType *garrow_timestamp_data_type_new (GArrowTimeUnit unit);
GArrowTimestampDataType *
garrow_timestamp_data_type_new(GArrowTimeUnit unit,
GTimeZone *time_zone);
GArrowTimeUnit
garrow_timestamp_data_type_get_unit (GArrowTimestampDataType *timestamp_data_type);
garrow_timestamp_data_type_get_unit(GArrowTimestampDataType *data_type);


#define GARROW_TYPE_TIME_DATA_TYPE (garrow_time_data_type_get_type())
Expand Down
23 changes: 23 additions & 0 deletions c_glib/arrow-glib/version.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@
# define GARROW_UNAVAILABLE(major, minor) G_UNAVAILABLE(major, minor)
#endif

/**
* GARROW_VERSION_16_0:
*
* You can use this macro value for compile time API version check.
*
* Since: 16.0.0
*/
#define GARROW_VERSION_16_0 G_ENCODE_VERSION(16, 0)

/**
* GARROW_VERSION_15_0:
*
Expand Down Expand Up @@ -355,6 +364,20 @@

#define GARROW_AVAILABLE_IN_ALL

#if GARROW_VERSION_MIN_REQUIRED >= GARROW_VERSION_16_0
# define GARROW_DEPRECATED_IN_16_0 GARROW_DEPRECATED
# define GARROW_DEPRECATED_IN_16_0_FOR(function) GARROW_DEPRECATED_FOR(function)
#else
# define GARROW_DEPRECATED_IN_16_0
# define GARROW_DEPRECATED_IN_16_0_FOR(function)
#endif

#if GARROW_VERSION_MAX_ALLOWED < GARROW_VERSION_16_0
# define GARROW_AVAILABLE_IN_16_0 GARROW_UNAVAILABLE(16, 0)
#else
# define GARROW_AVAILABLE_IN_16_0
#endif

#if GARROW_VERSION_MIN_REQUIRED >= GARROW_VERSION_15_0
# define GARROW_DEPRECATED_IN_15_0 GARROW_DEPRECATED
# define GARROW_DEPRECATED_IN_15_0_FOR(function) GARROW_DEPRECATED_FOR(function)
Expand Down
4 changes: 4 additions & 0 deletions c_glib/doc/arrow-glib/arrow-glib-docs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@
<title>Index of deprecated API</title>
<xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
</index>
<index id="api-index-16-0-0" role="16.0.0">
<title>Index of new symbols in 16.0.0</title>
<xi:include href="xml/api-index-16.0.0.xml"><xi:fallback /></xi:include>
</index>
<index id="api-index-13-0-0" role="13.0.0">
<title>Index of new symbols in 13.0.0</title>
<xi:include href="xml/api-index-13.0.0.xml"><xi:fallback /></xi:include>
Expand Down
17 changes: 17 additions & 0 deletions c_glib/test/test-timestamp-data-type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,23 @@ def test_name
assert_equal("timestamp", data_type.name)
end

sub_test_case("time_zone") do
def test_nil
data_type = Arrow::TimestampDataType.new(:micro)
assert_nil(data_type.time_zone)
end

def test_time_zone
data_type = Arrow::TimestampDataType.new(:micro, GLib::TimeZone.new("UTC"))
time_zone = data_type.time_zone
assert_not_nil(time_zone)
# glib2 gem 4.2.1 or later is required
if time_zone.respond_to?(:identifier)
assert_equal("UTC", time_zone.identifier)
end
end
end

sub_test_case("second") do
def setup
@data_type = Arrow::TimestampDataType.new(:second)
Expand Down

0 comments on commit c33ffb0

Please sign in to comment.