From faccbeff6e8ad0d54c6aaa28e2af1766d8ecd929 Mon Sep 17 00:00:00 2001 From: sgilmore10 <74676073+sgilmore10@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:55:05 -0400 Subject: [PATCH] GH-37337: [MATLAB] Add `arrow.array.Time64Array` class (#37368) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Rationale for this change Now that `arrow.type.Time64Type` class has been added to the MATLAB Interface (#37287), we can add the `arrow.array.Time64Array` class. ### What changes are included in this PR? 1. New `arrow.array.Time64Array` class. 2. New `arrow.type.traits.Time64Traits` class. 3. Enhanced `arrow.array(data)` to return a `arrow.array.Time64Array` instance if the input `data` is a MATLAB `duration` array. The MATLAB `duration` type represents length of time in fixed-length units. 4. Enhanced `arrow.type.traits.traits(id)` to return an `arrow.type.traits.Time64Traits` instance if `id` is an `arrow.type.ID.Time64` value or equal to the string `"duration".` 5. Updated the test utility `arrow.internal.test.tabular.createAllSupportedArrayTypes()` to include an `arrow.array.Time64Array` instance in its output. 6. Updated the test utility `arrow.internal.test.tabular.createTableWithSupportedTypes()` to include a `duration` variable on the returned `table`. 7. Updated the README.md to include `Time32Array` and `Time64Array`. **Example Usage** ```matlab % Create a duration array >> ms = milliseconds([100 150 1001]) ms = 1×3 duration array 0.1 sec 0.15 sec 1.001 sec % Create an arrow.type.Time64Array >> time = arrow.array(ms) time = [ 00:00:00.100000, 00:00:00.150000, 00:00:01.001000 ] % Create a duration array from an arrow.array.Time64Array >> data = duration(time) data = 3×1 duration array 0.1 sec 0.15 sec 1.001 sec ``` ### Are these changes tested? Yes. 1. Added a new test class `tTime64Array.m`. 2. Added a new test class `tTime64Traits.m` 3. Added new test cases to `tArray.m` and `ttraits.m` ### Are there any user-facing changes? Yes. Users can now create `arrow.array.Time64Array`s from MATLAB `duration`s * Closes: #37337 Authored-by: Sarah Gilmore Signed-off-by: Kevin Gurney --- matlab/README.md | 2 + .../arrow/matlab/array/proxy/time32_array.cc | 38 +-- .../arrow/matlab/array/proxy/time64_array.cc | 28 +++ .../arrow/matlab/array/proxy/time64_array.h | 31 +++ .../cpp/arrow/matlab/array/proxy/time_array.h | 72 ++++++ .../src/cpp/arrow/matlab/array/proxy/wrap.cc | 4 +- matlab/src/cpp/arrow/matlab/error/error.h | 1 + matlab/src/cpp/arrow/matlab/proxy/factory.cc | 2 + .../arrow/matlab/type/proxy/time32_type.cc | 21 +- .../arrow/matlab/type/proxy/time64_type.cc | 21 +- .../cpp/arrow/matlab/type/proxy/time_type.cc | 41 ++- .../cpp/arrow/matlab/type/proxy/time_type.h | 4 + .../src/cpp/arrow/matlab/type/proxy/traits.h | 12 + .../src/cpp/arrow/matlab/type/proxy/wrap.cc | 3 + matlab/src/cpp/arrow/matlab/type/time_unit.cc | 20 ++ matlab/src/cpp/arrow/matlab/type/time_unit.h | 3 + matlab/src/matlab/+arrow/+array/Time64Array.m | 80 ++++++ .../+tabular/createAllSupportedArrayTypes.m | 14 +- .../+tabular/createTableWithSupportedTypes.m | 4 +- .../+arrow/+type/+traits/Time64Traits.m | 29 +++ .../src/matlab/+arrow/+type/+traits/traits.m | 4 + matlab/src/matlab/+arrow/array.m | 2 + matlab/test/arrow/array/tArray.m | 1 + matlab/test/arrow/array/tTime64Array.m | 236 ++++++++++++++++++ matlab/test/arrow/type/traits/tTime64Traits.m | 32 +++ matlab/test/arrow/type/traits/ttraits.m | 23 ++ matlab/test/tfeather.m | 49 ++-- .../cmake/BuildMatlabArrowInterface.cmake | 1 + 28 files changed, 667 insertions(+), 111 deletions(-) create mode 100644 matlab/src/cpp/arrow/matlab/array/proxy/time64_array.cc create mode 100644 matlab/src/cpp/arrow/matlab/array/proxy/time64_array.h create mode 100644 matlab/src/cpp/arrow/matlab/array/proxy/time_array.h create mode 100644 matlab/src/matlab/+arrow/+array/Time64Array.m create mode 100644 matlab/src/matlab/+arrow/+type/+traits/Time64Traits.m create mode 100644 matlab/test/arrow/array/tTime64Array.m create mode 100644 matlab/test/arrow/type/traits/tTime64Traits.m diff --git a/matlab/README.md b/matlab/README.md index e0acb8b748b6d..d6b08fbee1c15 100644 --- a/matlab/README.md +++ b/matlab/README.md @@ -51,6 +51,8 @@ Supported `arrow.array.Array` types are included in the table below. | `logical` | `BooleanArray` | | `string` | `StringArray` | | `datetime` | `TimestampArray` | +| `duration` | `Time32Array` | +| `duration` | `Time64Array` | ## Prerequisites diff --git a/matlab/src/cpp/arrow/matlab/array/proxy/time32_array.cc b/matlab/src/cpp/arrow/matlab/array/proxy/time32_array.cc index 5def045a9d77d..2be1af6d2c264 100644 --- a/matlab/src/cpp/arrow/matlab/array/proxy/time32_array.cc +++ b/matlab/src/cpp/arrow/matlab/array/proxy/time32_array.cc @@ -16,47 +16,13 @@ // under the License. #include "arrow/matlab/array/proxy/time32_array.h" - -#include "arrow/matlab/type/time_unit.h" -#include "arrow/util/utf8.h" +#include "arrow/matlab/array/proxy/time_array.h" namespace arrow::matlab::array::proxy { // Specialization of NumericArray::Make for arrow::Time32Type template <> libmexclass::proxy::MakeResult Time32Array::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { - namespace mda = ::matlab::data; - using MatlabBuffer = arrow::matlab::buffer::MatlabBuffer; - using Time32Array = arrow::Time32Array; - using Time32ArrayProxy = arrow::matlab::array::proxy::NumericArray; - - mda::StructArray opts = constructor_arguments[0]; - - // Get the mxArray from constructor arguments - const mda::TypedArray time32_mda = opts[0]["MatlabArray"]; - const mda::TypedArray validity_bitmap_mda = opts[0]["Valid"]; - - const mda::TypedArray units_mda = opts[0]["TimeUnit"]; - - // extract the time unit - const std::u16string& u16_timeunit = units_mda[0]; - MATLAB_ASSIGN_OR_ERROR(const auto time_unit, - arrow::matlab::type::timeUnitFromString(u16_timeunit), - error::UKNOWN_TIME_UNIT_ERROR_ID) - - // create the Time32Type - auto data_type = arrow::time32(time_unit); - auto array_length = static_cast(time32_mda.getNumberOfElements()); // cast size_t to int32_t - - auto data_buffer = std::make_shared(time32_mda); - - // Pack the validity bitmap values. - MATLAB_ASSIGN_OR_ERROR(auto packed_validity_bitmap, - bit::packValid(validity_bitmap_mda), - error::BITPACK_VALIDITY_BITMAP_ERROR_ID); - - auto array_data = arrow::ArrayData::Make(data_type, array_length, {packed_validity_bitmap, data_buffer}); - auto time32_array = std::static_pointer_cast(arrow::MakeArray(array_data)); - return std::make_shared(std::move(time32_array)); + return make_time_array(constructor_arguments); } } diff --git a/matlab/src/cpp/arrow/matlab/array/proxy/time64_array.cc b/matlab/src/cpp/arrow/matlab/array/proxy/time64_array.cc new file mode 100644 index 0000000000000..4ef2e990dc7ec --- /dev/null +++ b/matlab/src/cpp/arrow/matlab/array/proxy/time64_array.cc @@ -0,0 +1,28 @@ +// 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. + +#include "arrow/matlab/array/proxy/time64_array.h" +#include "arrow/matlab/array/proxy/time_array.h" + +namespace arrow::matlab::array::proxy { + + // Specialization of NumericArray::Make for arrow::Time64Type + template <> + libmexclass::proxy::MakeResult Time64Array::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { + return make_time_array(constructor_arguments); + } +} diff --git a/matlab/src/cpp/arrow/matlab/array/proxy/time64_array.h b/matlab/src/cpp/arrow/matlab/array/proxy/time64_array.h new file mode 100644 index 0000000000000..aab586defe75e --- /dev/null +++ b/matlab/src/cpp/arrow/matlab/array/proxy/time64_array.h @@ -0,0 +1,31 @@ +// 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. + +#pragma once + +#include "arrow/matlab/array/proxy/numeric_array.h" + +#include "arrow/matlab/api/visibility.h" + +namespace arrow::matlab::array::proxy { + + using Time64Array = NumericArray; + + // Specialization of NumericArray::Make for arrow::Time64Type + template<> + ARROW_MATLAB_EXPORT libmexclass::proxy::MakeResult Time64Array::make(const libmexclass::proxy::FunctionArguments& constructor_arguments); +} diff --git a/matlab/src/cpp/arrow/matlab/array/proxy/time_array.h b/matlab/src/cpp/arrow/matlab/array/proxy/time_array.h new file mode 100644 index 0000000000000..68713a3dcce34 --- /dev/null +++ b/matlab/src/cpp/arrow/matlab/array/proxy/time_array.h @@ -0,0 +1,72 @@ +// 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. + +#include "arrow/matlab/array/proxy/numeric_array.h" + +#include "arrow/matlab/type/time_unit.h" +#include "arrow/util/utf8.h" +#include "arrow/type_traits.h" + +namespace arrow::matlab::array::proxy { + + template + using is_time = arrow::is_time_type; + + template + using enable_if_time = std::enable_if_t::value, bool>; + + template = true> + libmexclass::proxy::MakeResult make_time_array(const libmexclass::proxy::FunctionArguments& constructor_arguments) { + namespace mda = ::matlab::data; + using namespace arrow::matlab::type; + using MatlabBuffer = arrow::matlab::buffer::MatlabBuffer; + using TimeArray = arrow::NumericArray; + using TimeArrayProxy = proxy::NumericArray; + using CType = typename arrow::TypeTraits::CType; + + mda::StructArray opts = constructor_arguments[0]; + + const mda::TypedArray time_mda = opts[0]["MatlabArray"]; + const mda::TypedArray validity_bitmap_mda = opts[0]["Valid"]; + const mda::TypedArray time_unit_mda = opts[0]["TimeUnit"]; + + // extract the time unit + const std::u16string& time_unit_utf16 = time_unit_mda[0]; + MATLAB_ASSIGN_OR_ERROR(const auto time_unit, + timeUnitFromString(time_unit_utf16), + error::UKNOWN_TIME_UNIT_ERROR_ID); + + MATLAB_ERROR_IF_NOT_OK(validateTimeUnit(time_unit), + error::INVALID_TIME_UNIT); + + // create the ArrowType + const auto data_type = std::make_shared(time_unit); + + auto array_length = static_cast(time_mda.getNumberOfElements()); + auto data_buffer = std::make_shared(time_mda); + + // Pack the validity bitmap values. + MATLAB_ASSIGN_OR_ERROR(auto packed_validity_bitmap, + bit::packValid(validity_bitmap_mda), + error::BITPACK_VALIDITY_BITMAP_ERROR_ID); + + auto array_data = arrow::ArrayData::Make(data_type, array_length, {packed_validity_bitmap, data_buffer}); + auto time_array = std::static_pointer_cast(arrow::MakeArray(array_data)); + return std::make_shared(std::move(time_array)); + + } +} \ No newline at end of file diff --git a/matlab/src/cpp/arrow/matlab/array/proxy/wrap.cc b/matlab/src/cpp/arrow/matlab/array/proxy/wrap.cc index e11e9bb7669b1..3d1881a7dfb58 100644 --- a/matlab/src/cpp/arrow/matlab/array/proxy/wrap.cc +++ b/matlab/src/cpp/arrow/matlab/array/proxy/wrap.cc @@ -53,10 +53,12 @@ namespace arrow::matlab::array::proxy { return std::make_shared>(std::static_pointer_cast(array)); case ID::TIME32: return std::make_shared>(std::static_pointer_cast(array)); + case ID::TIME64: + return std::make_shared>(std::static_pointer_cast(array)); case ID::STRING: return std::make_shared(std::static_pointer_cast(array)); default: return arrow::Status::NotImplemented("Unsupported DataType: " + array->type()->ToString()); } } -} \ No newline at end of file +} diff --git a/matlab/src/cpp/arrow/matlab/error/error.h b/matlab/src/cpp/arrow/matlab/error/error.h index deac5e26fc1c0..2f17cfda16f3d 100644 --- a/matlab/src/cpp/arrow/matlab/error/error.h +++ b/matlab/src/cpp/arrow/matlab/error/error.h @@ -171,6 +171,7 @@ namespace arrow::matlab::error { static const char* STRING_BUILDER_APPEND_FAILED = "arrow:matlab:array:string:StringBuilderAppendFailed"; static const char* STRING_BUILDER_FINISH_FAILED = "arrow:matlab:array:string:StringBuilderFinishFailed"; static const char* UKNOWN_TIME_UNIT_ERROR_ID = "arrow:matlab:UnknownTimeUnit"; + static const char* INVALID_TIME_UNIT = "arrow:type:InvalidTimeUnit"; static const char* FIELD_FAILED_TO_CREATE_TYPE_PROXY = "arrow:field:FailedToCreateTypeProxy"; static const char* ARRAY_FAILED_TO_CREATE_TYPE_PROXY = "arrow:array:FailedToCreateTypeProxy"; static const char* ARROW_TABULAR_SCHEMA_INVALID_NUMERIC_FIELD_INDEX = "arrow:tabular:schema:InvalidNumericFieldIndex"; diff --git a/matlab/src/cpp/arrow/matlab/proxy/factory.cc b/matlab/src/cpp/arrow/matlab/proxy/factory.cc index 93dd9245aeb3c..7a84810c5a93c 100644 --- a/matlab/src/cpp/arrow/matlab/proxy/factory.cc +++ b/matlab/src/cpp/arrow/matlab/proxy/factory.cc @@ -20,6 +20,7 @@ #include "arrow/matlab/array/proxy/string_array.h" #include "arrow/matlab/array/proxy/timestamp_array.h" #include "arrow/matlab/array/proxy/time32_array.h" +#include "arrow/matlab/array/proxy/time64_array.h" #include "arrow/matlab/tabular/proxy/record_batch.h" #include "arrow/matlab/tabular/proxy/schema.h" #include "arrow/matlab/error/error.h" @@ -52,6 +53,7 @@ libmexclass::proxy::MakeResult Factory::make_proxy(const ClassName& class_name, REGISTER_PROXY(arrow.array.proxy.StringArray , arrow::matlab::array::proxy::StringArray); REGISTER_PROXY(arrow.array.proxy.TimestampArray, arrow::matlab::array::proxy::NumericArray); REGISTER_PROXY(arrow.array.proxy.Time32Array , arrow::matlab::array::proxy::NumericArray); + REGISTER_PROXY(arrow.array.proxy.Time64Array , arrow::matlab::array::proxy::NumericArray); REGISTER_PROXY(arrow.tabular.proxy.RecordBatch , arrow::matlab::tabular::proxy::RecordBatch); REGISTER_PROXY(arrow.tabular.proxy.Schema , arrow::matlab::tabular::proxy::Schema); REGISTER_PROXY(arrow.type.proxy.Field , arrow::matlab::type::proxy::Field); diff --git a/matlab/src/cpp/arrow/matlab/type/proxy/time32_type.cc b/matlab/src/cpp/arrow/matlab/type/proxy/time32_type.cc index 7b2313d6c312d..a3f54ed56a5da 100644 --- a/matlab/src/cpp/arrow/matlab/type/proxy/time32_type.cc +++ b/matlab/src/cpp/arrow/matlab/type/proxy/time32_type.cc @@ -16,31 +16,12 @@ // under the License. #include "arrow/matlab/type/proxy/time32_type.h" -#include "arrow/matlab/type/time_unit.h" -#include "arrow/matlab/error/error.h" -#include "arrow/util/utf8.h" namespace arrow::matlab::type::proxy { Time32Type::Time32Type(std::shared_ptr time32_type) : TimeType(std::move(time32_type)) {} libmexclass::proxy::MakeResult Time32Type::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { - namespace mda = ::matlab::data; - - using Time32TypeProxy = arrow::matlab::type::proxy::Time32Type; - - mda::StructArray opts = constructor_arguments[0]; - - const mda::StringArray timeunit_mda = opts[0]["TimeUnit"]; - - // extract the time unit - const std::u16string& utf16_timeunit = timeunit_mda[0]; - MATLAB_ASSIGN_OR_ERROR(const auto timeunit, - arrow::matlab::type::timeUnitFromString(utf16_timeunit), - error::UKNOWN_TIME_UNIT_ERROR_ID); - - auto type = arrow::time32(timeunit); - auto time_type = std::static_pointer_cast(type); - return std::make_shared(std::move(time_type)); + return make_time_type(constructor_arguments); } } diff --git a/matlab/src/cpp/arrow/matlab/type/proxy/time64_type.cc b/matlab/src/cpp/arrow/matlab/type/proxy/time64_type.cc index 0ec58337622a7..ab494c909ac56 100644 --- a/matlab/src/cpp/arrow/matlab/type/proxy/time64_type.cc +++ b/matlab/src/cpp/arrow/matlab/type/proxy/time64_type.cc @@ -16,31 +16,12 @@ // under the License. #include "arrow/matlab/type/proxy/time64_type.h" -#include "arrow/matlab/type/time_unit.h" -#include "arrow/matlab/error/error.h" -#include "arrow/util/utf8.h" namespace arrow::matlab::type::proxy { Time64Type::Time64Type(std::shared_ptr time64_type) : TimeType(std::move(time64_type)) {} libmexclass::proxy::MakeResult Time64Type::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { - namespace mda = ::matlab::data; - - using Time64TypeProxy = arrow::matlab::type::proxy::Time64Type; - - mda::StructArray opts = constructor_arguments[0]; - - const mda::StringArray timeunit_mda = opts[0]["TimeUnit"]; - - // extract the time unit - const std::u16string& utf16_timeunit = timeunit_mda[0]; - MATLAB_ASSIGN_OR_ERROR(const auto timeunit, - arrow::matlab::type::timeUnitFromString(utf16_timeunit), - error::UKNOWN_TIME_UNIT_ERROR_ID); - - auto type = arrow::time64(timeunit); - auto time_type = std::static_pointer_cast(type); - return std::make_shared(std::move(time_type)); + return make_time_type(constructor_arguments); } } diff --git a/matlab/src/cpp/arrow/matlab/type/proxy/time_type.cc b/matlab/src/cpp/arrow/matlab/type/proxy/time_type.cc index 1cc92834b9449..84c875ba489ad 100644 --- a/matlab/src/cpp/arrow/matlab/type/proxy/time_type.cc +++ b/matlab/src/cpp/arrow/matlab/type/proxy/time_type.cc @@ -16,6 +16,10 @@ // under the License. #include "arrow/matlab/type/proxy/time_type.h" +#include "arrow/matlab/type/proxy/traits.h" +#include "arrow/matlab/type/time_unit.h" +#include "arrow/matlab/error/error.h" +#include "arrow/util/utf8.h" namespace arrow::matlab::type::proxy { @@ -28,10 +32,43 @@ namespace arrow::matlab::type::proxy { mda::ArrayFactory factory; auto time_type = std::static_pointer_cast(data_type); - const auto timeunit = time_type->unit(); + const auto time_unit = time_type->unit(); // Cast to uint8_t since there are only four supported TimeUnit enumeration values: // Nanosecond, Microsecond, Millisecond, Second - auto timeunit_mda = factory.createScalar(static_cast(timeunit)); + auto timeunit_mda = factory.createScalar(static_cast(time_unit)); context.outputs[0] = timeunit_mda; } + + template + libmexclass::proxy::MakeResult make_time_type(const libmexclass::proxy::FunctionArguments& constructor_arguments) { + namespace mda = ::matlab::data; + using namespace arrow::matlab::type; + using TimeTypeProxy = typename proxy::Traits::TypeProxy; + + mda::StructArray opts = constructor_arguments[0]; + + const mda::StringArray time_unit_mda = opts[0]["TimeUnit"]; + + // extract the time unit + const std::u16string& time_unit_utf16 = time_unit_mda[0]; + MATLAB_ASSIGN_OR_ERROR(const auto timeunit, + timeUnitFromString(time_unit_utf16), + error::UKNOWN_TIME_UNIT_ERROR_ID); + + // validate timeunit + MATLAB_ERROR_IF_NOT_OK(validateTimeUnit(timeunit), + error::INVALID_TIME_UNIT); + + auto type = std::make_shared(timeunit); + auto time_type = std::static_pointer_cast(type); + return std::make_shared(std::move(time_type)); + } + + // Trigger code generation for the allowed template specializations using explicit instantiation. + template + libmexclass::proxy::MakeResult make_time_type(const libmexclass::proxy::FunctionArguments& constructor_arguments); + + template + libmexclass::proxy::MakeResult make_time_type(const libmexclass::proxy::FunctionArguments& constructor_arguments); + } diff --git a/matlab/src/cpp/arrow/matlab/type/proxy/time_type.h b/matlab/src/cpp/arrow/matlab/type/proxy/time_type.h index 05521b9c36b19..91d92aa4c40e2 100644 --- a/matlab/src/cpp/arrow/matlab/type/proxy/time_type.h +++ b/matlab/src/cpp/arrow/matlab/type/proxy/time_type.h @@ -32,4 +32,8 @@ class TimeType : public arrow::matlab::type::proxy::FixedWidthType { void getTimeUnit(libmexclass::proxy::method::Context& context); }; +template +libmexclass::proxy::MakeResult make_time_type(const libmexclass::proxy::FunctionArguments& constructor_arguments); + + } diff --git a/matlab/src/cpp/arrow/matlab/type/proxy/traits.h b/matlab/src/cpp/arrow/matlab/type/proxy/traits.h index 3d9a957a5e3dc..fdae911062b28 100644 --- a/matlab/src/cpp/arrow/matlab/type/proxy/traits.h +++ b/matlab/src/cpp/arrow/matlab/type/proxy/traits.h @@ -21,6 +21,8 @@ #include "arrow/matlab/type/proxy/primitive_ctype.h" #include "arrow/matlab/type/proxy/timestamp_type.h" +#include "arrow/matlab/type/proxy/time32_type.h" +#include "arrow/matlab/type/proxy/time64_type.h" #include "arrow/matlab/type/proxy/string_type.h" namespace arrow::matlab::type::proxy { @@ -87,4 +89,14 @@ namespace arrow::matlab::type::proxy { struct Traits { using TypeProxy = TimestampType; }; + + template <> + struct Traits { + using TypeProxy = Time32Type; + }; + + template <> + struct Traits { + using TypeProxy = Time64Type; + }; } diff --git a/matlab/src/cpp/arrow/matlab/type/proxy/wrap.cc b/matlab/src/cpp/arrow/matlab/type/proxy/wrap.cc index 39c60fea9bc5c..efb510e89751d 100644 --- a/matlab/src/cpp/arrow/matlab/type/proxy/wrap.cc +++ b/matlab/src/cpp/arrow/matlab/type/proxy/wrap.cc @@ -20,6 +20,7 @@ #include "arrow/matlab/type/proxy/primitive_ctype.h" #include "arrow/matlab/type/proxy/timestamp_type.h" #include "arrow/matlab/type/proxy/time32_type.h" +#include "arrow/matlab/type/proxy/time64_type.h" #include "arrow/matlab/type/proxy/string_type.h" namespace arrow::matlab::type::proxy { @@ -53,6 +54,8 @@ namespace arrow::matlab::type::proxy { return std::make_shared(std::static_pointer_cast(type)); case ID::TIME32: return std::make_shared(std::static_pointer_cast(type)); + case ID::TIME64: + return std::make_shared(std::static_pointer_cast(type)); case ID::STRING: return std::make_shared(std::static_pointer_cast(type)); default: diff --git a/matlab/src/cpp/arrow/matlab/type/time_unit.cc b/matlab/src/cpp/arrow/matlab/type/time_unit.cc index eb839b0e78096..7a7cf7f350828 100644 --- a/matlab/src/cpp/arrow/matlab/type/time_unit.cc +++ b/matlab/src/cpp/arrow/matlab/type/time_unit.cc @@ -35,4 +35,24 @@ namespace arrow::matlab::type { return arrow::Status::Invalid(msg); } } + + template<> + arrow::Status validateTimeUnit(arrow::TimeUnit::type unit) { + using arrow::TimeUnit; + if (unit == TimeUnit::type::SECOND || unit == TimeUnit::type::MILLI) { + return arrow::Status::OK(); + } else { + return arrow::Status::Invalid("TimeUnit for Time32 must be Second or Millisecond"); + } + } + + template<> + arrow::Status validateTimeUnit(arrow::TimeUnit::type unit) { + using arrow::TimeUnit; + if (unit == TimeUnit::type::MICRO || unit == TimeUnit::type::NANO) { + return arrow::Status::OK(); + } else { + return arrow::Status::Invalid("TimeUnit for Time64 must be Microsecond or Nanosecond"); + } + } } diff --git a/matlab/src/cpp/arrow/matlab/type/time_unit.h b/matlab/src/cpp/arrow/matlab/type/time_unit.h index 9534b1f902db7..2470fc53cf434 100644 --- a/matlab/src/cpp/arrow/matlab/type/time_unit.h +++ b/matlab/src/cpp/arrow/matlab/type/time_unit.h @@ -24,4 +24,7 @@ namespace arrow::matlab::type { arrow::Result timeUnitFromString(std::u16string_view unit_str); + template + arrow::Status validateTimeUnit(arrow::TimeUnit::type unit); + } diff --git a/matlab/src/matlab/+arrow/+array/Time64Array.m b/matlab/src/matlab/+arrow/+array/Time64Array.m new file mode 100644 index 0000000000000..f85eeb1f8f0c9 --- /dev/null +++ b/matlab/src/matlab/+arrow/+array/Time64Array.m @@ -0,0 +1,80 @@ +% arrow.array.Time64Array + +% 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. + +classdef Time64Array < arrow.array.Array + + properties(Access=private) + NullSubstitutionValue = seconds(NaN); + end + + methods + function obj = Time64Array(proxy) + arguments + proxy(1, 1) libmexclass.proxy.Proxy {validate(proxy, "arrow.array.proxy.Time64Array")} + end + import arrow.internal.proxy.validate + obj@arrow.array.Array(proxy); + end + + function times = toMATLAB(obj) + import arrow.type.TimeUnit + divider = ticksPerSecond(obj.Type.TimeUnit) / ticksPerSecond(TimeUnit.Millisecond); + matlabArray = obj.Proxy.toMATLAB(); + % TODO: This conversion may be lossy. Is it better to cast the + % int64 array to a double before dividing by 1e3 or 1e6? + times = milliseconds(cast(matlabArray, "double") / divider); + times(~obj.Valid) = obj.NullSubstitutionValue; + end + + function times = duration(obj) + times = obj.toMATLAB(); + end + end + + methods(Static, Access=private) + function ticks = convertDurationToTicks(data, timeUnit) + import arrow.type.TimeUnit + multiplier = ticksPerSecond(timeUnit) / ticksPerSecond(TimeUnit.Millisecond); + ticks = cast(milliseconds(data) * multiplier, "int64"); + end + end + + methods(Static) + function array = fromMATLAB(data, opts) + arguments + data + opts.TimeUnit(1, 1) TimeUnit {timeUnit("Time64", opts.TimeUnit)} = TimeUnit.Microsecond + opts.InferNulls(1, 1) logical = true + opts.Valid + end + + import arrow.type.TimeUnit + import arrow.array.Time64Array + import arrow.internal.validate.temporal.timeUnit + + arrow.internal.validate.type(data, "duration"); + arrow.internal.validate.shape(data); + + validElements = arrow.internal.validate.parseValidElements(data, opts); + ticks = Time64Array.convertDurationToTicks(data, opts.TimeUnit); + + args = struct(MatlabArray=ticks, Valid=validElements, TimeUnit=string(opts.TimeUnit)); + proxy = arrow.internal.proxy.create("arrow.array.proxy.Time64Array", args); + array = Time64Array(proxy); + end + end +end \ No newline at end of file diff --git a/matlab/src/matlab/+arrow/+internal/+test/+tabular/createAllSupportedArrayTypes.m b/matlab/src/matlab/+arrow/+internal/+test/+tabular/createAllSupportedArrayTypes.m index 7d3f36cb46e7c..15ee0589d56e4 100644 --- a/matlab/src/matlab/+arrow/+internal/+test/+tabular/createAllSupportedArrayTypes.m +++ b/matlab/src/matlab/+arrow/+internal/+test/+tabular/createAllSupportedArrayTypes.m @@ -31,7 +31,8 @@ arrowArrays = cell(numClasses, 1); matlabData = cell(numClasses, 1); - numericArrayToMatlabTypeDict = getArrowArrayToMatlabTypeDictionary(); + timeClasses = getTimeArrayClasses(); + numericArrayToMatlabTypeDict = getNumericArrayToMatlabDictionary(); for ii = 1:numel(classes) name = classes(ii); @@ -49,9 +50,10 @@ elseif name == "arrow.array.TimestampArray" matlabData{ii} = randomDatetimes(opts.NumRows); arrowArrays{ii} = TimestampArray.fromMATLAB(matlabData{ii}); - elseif name == "arrow.array.Time32Array" + elseif ismember(name, timeClasses) matlabData{ii} = randomDurations(opts.NumRows); - arrowArrays{ii} = Time32Array.fromMATLAB(matlabData{ii}); + cmd = compose("%s.fromMATLAB(matlabData{ii})", name); + arrowArrays{ii} = eval(cmd); else error("arrow:test:SupportedArrayCase", ... "Missing if-branch for array class " + name); @@ -68,7 +70,7 @@ classes = string({metaClass.Name}); end -function dict = getArrowArrayToMatlabTypeDictionary() +function dict = getNumericArrayToMatlabDictionary() pkg = "arrow.array"; unsignedTypes = compose("UInt%d", power(2, 3:6)); signedTypes = compose("Int%d", power(2, 3:6)); @@ -80,6 +82,10 @@ dict = dictionary(keys, values); end +function timeClasses = getTimeArrayClasses() + timeClasses = compose("arrow.array.Time%dArray", [32 64]); +end + function number = randomNumbers(numberType, numElements) number = cast(randi(255, [numElements 1]), numberType); end diff --git a/matlab/src/matlab/+arrow/+internal/+test/+tabular/createTableWithSupportedTypes.m b/matlab/src/matlab/+arrow/+internal/+test/+tabular/createTableWithSupportedTypes.m index c69aef35d18b3..804a8f358c8be 100644 --- a/matlab/src/matlab/+arrow/+internal/+test/+tabular/createTableWithSupportedTypes.m +++ b/matlab/src/matlab/+arrow/+internal/+test/+tabular/createTableWithSupportedTypes.m @@ -35,7 +35,8 @@ "single", ... "double", ... "string", ... - "datetime"]; + "datetime", ... + "duration"]; T = table(uint8 ((1:numRows)'), ... uint16 ((1:numRows)'), ... @@ -50,5 +51,6 @@ double ((1:numRows)'), ... string ((1:numRows)'), ... datetime(2023, 6, 28) + days(0:numRows-1)', ... + seconds((1:numRows)'), ... VariableNames=variableNames); end \ No newline at end of file diff --git a/matlab/src/matlab/+arrow/+type/+traits/Time64Traits.m b/matlab/src/matlab/+arrow/+type/+traits/Time64Traits.m new file mode 100644 index 0000000000000..f9108d6ac3879 --- /dev/null +++ b/matlab/src/matlab/+arrow/+type/+traits/Time64Traits.m @@ -0,0 +1,29 @@ +% 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. + +classdef Time64Traits < arrow.type.traits.TypeTraits + + properties (Constant) + ArrayConstructor = @arrow.array.Time64Array + ArrayClassName = "arrow.array.Time64Array" + ArrayProxyClassName = "arrow.array.proxy.Time64Array" + TypeConstructor = @arrow.type.Time64Type; + TypeClassName = "arrow.type.Time64Type" + TypeProxyClassName = "arrow.type.proxy.Time64Type" + MatlabConstructor = @duration + MatlabClassName = "duration" + end + +end \ No newline at end of file diff --git a/matlab/src/matlab/+arrow/+type/+traits/traits.m b/matlab/src/matlab/+arrow/+type/+traits/traits.m index 0c3628f01e561..e7c7f9b3272d1 100644 --- a/matlab/src/matlab/+arrow/+type/+traits/traits.m +++ b/matlab/src/matlab/+arrow/+type/+traits/traits.m @@ -50,6 +50,8 @@ typeTraits = TimestampTraits(); case ID.Time32 typeTraits = Time32Traits(); + case ID.Time64 + typeTraits = Time64Traits(); otherwise error("arrow:type:traits:UnsupportedArrowTypeID", "Unsupported Arrow type ID: " + type); end @@ -81,6 +83,8 @@ typeTraits = StringTraits(); case "datetime" typeTraits = TimestampTraits(); + case "duration" + typeTraits = Time64Traits(); otherwise error("arrow:type:traits:UnsupportedMatlabClass", "Unsupported MATLAB class: " + type); end diff --git a/matlab/src/matlab/+arrow/array.m b/matlab/src/matlab/+arrow/array.m index a2d0ecd2e8f76..983b3c88680c4 100644 --- a/matlab/src/matlab/+arrow/array.m +++ b/matlab/src/matlab/+arrow/array.m @@ -51,6 +51,8 @@ arrowArray = arrow.array.StringArray.fromMATLAB(data, args{:}); case "datetime" arrowArray = arrow.array.TimestampArray.fromMATLAB(data, args{:}); + case "duration" + arrowArray = arrow.array.Time64Array.fromMATLAB(data, args{:}); otherwise errid = "arrow:array:UnsupportedMATLABType"; msg = join(["Unable to convert MATLAB type" classname "to arrow array."]); diff --git a/matlab/test/arrow/array/tArray.m b/matlab/test/arrow/array/tArray.m index 4a476696bfacf..54b31270b25d2 100644 --- a/matlab/test/arrow/array/tArray.m +++ b/matlab/test/arrow/array/tArray.m @@ -31,6 +31,7 @@ {single([1 2]), "arrow.array.Float32Array"}, ... {[1 2], "arrow.array.Float64Array"}, ... {datetime(2022, 1, 1), "arrow.array.TimestampArray"}, ... + {seconds([1 2]), "arrow.array.Time64Array"}, ... {["A" "B"], "arrow.array.StringArray"}}; end diff --git a/matlab/test/arrow/array/tTime64Array.m b/matlab/test/arrow/array/tTime64Array.m new file mode 100644 index 0000000000000..973a8e6b6af75 --- /dev/null +++ b/matlab/test/arrow/array/tTime64Array.m @@ -0,0 +1,236 @@ +%TTIME64ARRAY Unit tests for arrow.array.Time64Array + +% 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. + +classdef tTime64Array < matlab.unittest.TestCase + + properties + ArrowArrayConstructorFcn = @arrow.array.Time64Array.fromMATLAB + end + + properties(TestParameter) + Unit = {arrow.type.TimeUnit.Microsecond, arrow.type.TimeUnit.Nanosecond} + end + + methods (Test) + function Basic(tc) + times = seconds(1:4); + array = tc.ArrowArrayConstructorFcn(times); + tc.verifyInstanceOf(array, "arrow.array.Time64Array"); + end + + function TypeIsTime64(tc) + times = seconds(1:4); + array = tc.ArrowArrayConstructorFcn(times); + tc.verifyTime64Type(array.Type, arrow.type.TimeUnit.Microsecond); + end + + function SupportedTimeUnit(tc) + import arrow.type.TimeUnit + times = seconds(1:4); + + array = tc.ArrowArrayConstructorFcn(times, TimeUnit="Microsecond"); + tc.verifyTime64Type(array.Type, arrow.type.TimeUnit.Microsecond); + + array = tc.ArrowArrayConstructorFcn(times, TimeUnit=TimeUnit.Microsecond); + tc.verifyTime64Type(array.Type, arrow.type.TimeUnit.Microsecond); + + array = tc.ArrowArrayConstructorFcn(times, TimeUnit="Nanosecond"); + tc.verifyTime64Type(array.Type, arrow.type.TimeUnit.Nanosecond); + + array = tc.ArrowArrayConstructorFcn(times, TimeUnit=TimeUnit.Nanosecond); + tc.verifyTime64Type(array.Type, arrow.type.TimeUnit.Nanosecond); + end + + function UnsupportedTimeUnitError(tc) + % Verify arrow.array.Time64Array.fromMATLAB() errors if + % supplied an unsupported TimeUnit (Second or Millisecond). + import arrow.type.TimeUnit + times = seconds(1:4); + fcn = @() tc.ArrowArrayConstructorFcn(times, TimeUnit="Second"); + tc.verifyError(fcn, "arrow:validate:temporal:UnsupportedTime64TimeUnit"); + + fcn = @() tc.ArrowArrayConstructorFcn(times, TimeUnit=TimeUnit.Second); + tc.verifyError(fcn, "arrow:validate:temporal:UnsupportedTime64TimeUnit"); + + fcn = @() tc.ArrowArrayConstructorFcn(times, TimeUnit="Millisecond"); + tc.verifyError(fcn, "arrow:validate:temporal:UnsupportedTime64TimeUnit"); + + fcn = @() tc.ArrowArrayConstructorFcn(times, TimeUnit=TimeUnit.Millisecond); + tc.verifyError(fcn, "arrow:validate:temporal:UnsupportedTime64TimeUnit"); + end + + function TestLength(testCase) + % Verify the Length property. + + times = duration.empty(0, 1); + array = testCase.ArrowArrayConstructorFcn(times); + testCase.verifyEqual(array.Length, int64(0)); + + times = duration(1, 2, 3); + array = testCase.ArrowArrayConstructorFcn(times); + testCase.verifyEqual(array.Length, int64(1)); + + times = duration(1, 2, 3) + hours(0:4); + array = testCase.ArrowArrayConstructorFcn(times); + testCase.verifyEqual(array.Length, int64(5)); + end + + function TestToMATLAB(testCase, Unit) + % Verify toMATLAB() round-trips the original duration array. + times = seconds([100 200 355 400]); + array = testCase.ArrowArrayConstructorFcn(times, TimeUnit=Unit); + values = toMATLAB(array); + testCase.verifyEqual(values, times'); + end + + function TestDuration(testCase, Unit) + % Verify duration() round-trips the original duration array. + times = seconds([100 200 355 400]); + array = testCase.ArrowArrayConstructorFcn(times, TimeUnit=Unit); + values = duration(array); + testCase.verifyEqual(values, times'); + end + + function TestValid(testCase, Unit) + % Verify the Valid property returns the expected logical vector. + times = seconds([100 200 NaN 355 NaN 400]); + arrray = testCase.ArrowArrayConstructorFcn(times, TImeUnit=Unit); + testCase.verifyEqual(arrray.Valid, [true; true; false; true; false; true]); + testCase.verifyEqual(toMATLAB(arrray), times'); + testCase.verifyEqual(duration(arrray), times'); + end + + function InferNullsTrueNVPair(testCase, Unit) + % Verify arrow.array.Time64Array.fromMATLAB() behaves as + % expected when InferNulls=true is provided. + + times = seconds([1 2 NaN 4 5 NaN 7]); + array = testCase.ArrowArrayConstructorFcn(times, InferNulls=true, TimeUnit=Unit); + expectedValid = [true; true; false; true; true; false; true]; + testCase.verifyEqual(array.Valid, expectedValid); + testCase.verifyEqual(toMATLAB(array), times'); + testCase.verifyEqual(duration(array), times'); + end + + function InferNullsFalseNVPair(testCase, Unit) + % Verify arrow.array.Time64Array.fromMATLAB() behaves as + % expected when InferNulls=false is provided. + + times = seconds([1 2 NaN 4 5 NaN 7]); + array = testCase.ArrowArrayConstructorFcn(times, InferNulls=false, TimeUnit=Unit); + expectedValid = true([7 1]); + testCase.verifyEqual(array.Valid, expectedValid); + + % If NaN durations were not considered null values, then they + % are treated like int64(0) values. + expectedTime = times'; + expectedTime([3 6]) = 0; + testCase.verifyEqual(toMATLAB(array), expectedTime); + testCase.verifyEqual(duration(array), expectedTime); + end + + function TestValidNVPair(testCase, Unit) + % Verify arrow.array.Time64Array.fromMATLAB() accepts the Valid + % nv-pair, and it behaves as expected. + + times = seconds([1 2 NaN 4 5 NaN 7]); + + % Supply the Valid name-value pair as vector of indices. + array = testCase.ArrowArrayConstructorFcn(times, TimeUnit=Unit, Valid=[1 2 3 5]); + testCase.verifyEqual(array.Valid, [true; true; true; false; true; false; false]); + expectedTimes = times'; + expectedTimes(3) = 0; + expectedTimes([4 6 7]) = NaN; + testCase.verifyEqual(toMATLAB(array), expectedTimes); + + % Supply the Valid name-value pair as a logical scalar. + array = testCase.ArrowArrayConstructorFcn(times, TimeUnit=Unit, Valid=false); + testCase.verifyEqual(array.Valid, false([7 1])); + expectedTimes(:) = NaN; + testCase.verifyEqual(toMATLAB(array), expectedTimes); + end + + function EmptyDurationVector(testCase) + % Verify arrow.array.Time64Array.fromMATLAB() accepts any + % empty-shaped duration as input. + + times = duration.empty(0, 0); + array = testCase.ArrowArrayConstructorFcn(times); + testCase.verifyEqual(array.Length, int64(0)); + testCase.verifyEqual(array.Valid, logical.empty(0, 1)); + testCase.verifyEqual(toMATLAB(array), duration.empty(0, 1)); + + % Test with an N-Dimensional empty array + times = duration.empty(0, 1, 0); + array = testCase.ArrowArrayConstructorFcn(times); + testCase.verifyEqual(array.Length, int64(0)); + testCase.verifyEqual(array.Valid, logical.empty(0, 1)); + testCase.verifyEqual(toMATLAB(array), duration.empty(0, 1)); + end + + function ErrorIfNonVector(testCase) + % Verify arrow.array.Time64Array.fromMATLAB() throws an error + % if the input provided is not a vector. + + times = duration(200, 45, 34) + hours(0:11); + times = reshape(times, 2, 6); + fcn = @() testCase.ArrowArrayConstructorFcn(times); + testCase.verifyError(fcn, "arrow:array:InvalidShape"); + + times = reshape(times, 3, 2, 2); + fcn = @() testCase.ArrowArrayConstructorFcn(times); + testCase.verifyError(fcn, "arrow:array:InvalidShape"); + end + + function ErrorIfNonDuration(testCase) + % Verify arrow.array.Time64Array.fromMATLAB() throws an error + % if not given a duration as input. + + dates = datetime(2023, 4, 6); + fcn = @() testCase.ArrowArrayConstructorFcn(dates); + testCase.verifyError(fcn, "arrow:array:InvalidType"); + + numbers = [1; 2; 3; 4]; + fcn = @() testCase.ArrowArrayConstructorFcn(numbers); + testCase.verifyError(fcn, "arrow:array:InvalidType"); + end + + function MicrosecondPrecision(testCase) + % Verify microsecond precision when TimeUnit="Microsecond" + original = milliseconds(1.0033); + array = testCase.ArrowArrayConstructorFcn(original, TimeUnit="Microsecond"); + expected = milliseconds(1.003); + testCase.verifyEqual(duration(array), expected); + end + + function NanosecondPrecision(testCase) + % Verify nanosecond precision when TimeUnit="Nanosecond" + original = milliseconds(1.0030031); + array = testCase.ArrowArrayConstructorFcn(original, TimeUnit="Nanosecond"); + expected = milliseconds(1.0030030); + testCase.verifyEqual(duration(array), expected); + end + end + + methods + function verifyTime64Type(testCase, actual, expectedTimeUnit) + testCase.verifyInstanceOf(actual, "arrow.type.Time64Type"); + testCase.verifyEqual(actual.ID, arrow.type.ID.Time64); + testCase.verifyEqual(actual.TimeUnit, expectedTimeUnit); + end + end +end \ No newline at end of file diff --git a/matlab/test/arrow/type/traits/tTime64Traits.m b/matlab/test/arrow/type/traits/tTime64Traits.m new file mode 100644 index 0000000000000..bc4e205b96c5d --- /dev/null +++ b/matlab/test/arrow/type/traits/tTime64Traits.m @@ -0,0 +1,32 @@ +%TTIME64TRAITS Unit tests for arrow.type.traits.Time64Traits + +% 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. + +classdef tTime64Traits < hTypeTraits + + properties + TraitsConstructor = @arrow.type.traits.Time64Traits + ArrayConstructor = @arrow.array.Time64Array + ArrayClassName = "arrow.array.Time64Array" + ArrayProxyClassName = "arrow.array.proxy.Time64Array" + TypeConstructor = @arrow.type.Time64Type + TypeClassName = "arrow.type.Time64Type" + TypeProxyClassName = "arrow.type.proxy.Time64Type" + MatlabConstructor = @duration + MatlabClassName = "duration" + end + +end \ No newline at end of file diff --git a/matlab/test/arrow/type/traits/ttraits.m b/matlab/test/arrow/type/traits/ttraits.m index 3bfde78dbb615..771562152a5ef 100644 --- a/matlab/test/arrow/type/traits/ttraits.m +++ b/matlab/test/arrow/type/traits/ttraits.m @@ -163,6 +163,18 @@ function TestTime32(testCase) testCase.verifyEqual(actualTraits, expectedTraits); end + function TestTime64(testCase) + import arrow.type.traits.* + import arrow.type.* + + type = ID.Time64; + expectedTraits = Time64Traits(); + + actualTraits = traits(type); + + testCase.verifyEqual(actualTraits, expectedTraits); + end + function TestMatlabUInt8(testCase) import arrow.type.traits.* @@ -306,6 +318,17 @@ function TestMatlabDatetime(testCase) testCase.verifyEqual(actualTraits, expectedTraits); end + function TestMatlabDuration(testCase) + import arrow.type.traits.* + + type = "duration"; + expectedTraits = Time64Traits(); + + actualTraits = traits(type); + + testCase.verifyEqual(actualTraits, expectedTraits); + end + function TestErrorIfUnsupportedMatlabClass(testCase) import arrow.type.traits.* diff --git a/matlab/test/tfeather.m b/matlab/test/tfeather.m index 72d2438cee83f..b24b6b4af1def 100755 --- a/matlab/test/tfeather.m +++ b/matlab/test/tfeather.m @@ -28,23 +28,21 @@ function setupTempWorkingDirectory(testCase) methods(Test) function NumericDatatypesNoNulls(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes import arrow.internal.test.io.feather.roundtrip filename = fullfile(pwd, 'temp.feather'); - - actualTable = createTableWithSupportedTypes; + + actualTable = createFeatherCompatibleTable(); expectedTable = roundtrip(filename, actualTable); testCase.verifyEqual(actualTable, expectedTable); end function NumericDatatypesWithNaNRow(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes import arrow.internal.test.io.feather.roundtrip filename = fullfile(pwd, 'temp.feather'); - t = createTableWithSupportedTypes; + t = createFeatherCompatibleTable(); t = removevars(t, ["logical", "string", "datetime"]); variableNames = {'uint8', ... @@ -73,12 +71,11 @@ function NumericDatatypesWithNaNRow(testCase) end function NumericDatatypesWithNaNColumns(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes import arrow.internal.test.io.feather.roundtrip filename = fullfile(pwd, 'temp.feather'); - actualTable = createTableWithSupportedTypes; + actualTable = createFeatherCompatibleTable(); actualTable.double = [NaN; NaN; NaN]; actualTable.int64 = [NaN; NaN; NaN]; @@ -87,12 +84,11 @@ function NumericDatatypesWithNaNColumns(testCase) end function NumericDatatypesWithExpInfSciNotation(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes import arrow.internal.test.io.feather.roundtrip filename = fullfile(pwd, 'temp.feather'); - actualTable = createTableWithSupportedTypes; + actualTable = createFeatherCompatibleTable(); actualTable.single(2) = 1.0418e+06; actualTable.double(1) = Inf; @@ -105,26 +101,24 @@ function NumericDatatypesWithExpInfSciNotation(testCase) end function IgnoreRowVarNames(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes import arrow.internal.test.io.feather.roundtrip filename = fullfile(pwd, 'temp.feather'); - actualTable = createTableWithSupportedTypes; + actualTable = createFeatherCompatibleTable(); time = {'day1', 'day2', 'day3'}; actualTable.Properties.RowNames = time; expectedTable = roundtrip(filename, actualTable); - actualTable = createTableWithSupportedTypes; + actualTable = createFeatherCompatibleTable(); testCase.verifyEqual(actualTable, expectedTable); end function NotFeatherExtension(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes import arrow.internal.test.io.feather.roundtrip filename = fullfile(pwd, 'temp.txt'); - actualTable = createTableWithSupportedTypes; + actualTable = createFeatherCompatibleTable(); expectedTable = roundtrip(filename, actualTable); testCase.verifyEqual(actualTable, expectedTable); end @@ -140,12 +134,11 @@ function EmptyTable(testCase) end function zeroByNTable(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes import arrow.internal.test.io.feather.roundtrip filename = fullfile(pwd, 'temp.feather'); - actualTable = createTableWithSupportedTypes; + actualTable = createFeatherCompatibleTable(); actualTable([1, 2], :) = []; expectedTable = roundtrip(filename, actualTable); testCase.verifyEqual(actualTable, expectedTable); @@ -162,11 +155,10 @@ function ErrorIfUnableToOpenFile(testCase) end function ErrorIfCorruptedFeatherFile(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes filename = fullfile(pwd, 'temp.feather'); - t = createTableWithSupportedTypes; + t = createFeatherCompatibleTable(); featherwrite(filename, t); fileID = fopen(filename, 'w'); @@ -177,19 +169,17 @@ function ErrorIfCorruptedFeatherFile(testCase) end function ErrorIfInvalidFilenameDatatype(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes - t = createTableWithSupportedTypes; + t = createFeatherCompatibleTable(); testCase.verifyError(@() featherwrite({table}, t), 'MATLAB:validation:UnableToConvert'); end function ErrorIfTooManyInputs(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes filename = fullfile(pwd, 'temp.feather'); - t = createTableWithSupportedTypes; + t = createFeatherCompatibleTable(); testCase.verifyError(@() featherwrite(filename, t, 'SomeValue', 'SomeOtherValue'), 'MATLAB:TooManyInputs'); testCase.verifyError(@() featherread(filename, 'SomeValue', 'SomeOtherValue'), 'MATLAB:TooManyInputs'); @@ -215,11 +205,10 @@ function ErrorIfMultiColVarExist(testCase) end function UnsupportedMATLABDatatypes(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes filename = fullfile(pwd, 'temp.feather'); - actualTable = createTableWithSupportedTypes; + actualTable = createFeatherCompatibleTable(); calendarDurationVariable = [calendarDuration(1, 7, 9); ... calendarDuration(2, 1, 1); ... calendarDuration(5, 3, 2)]; @@ -229,11 +218,9 @@ function UnsupportedMATLABDatatypes(testCase) end function NumericComplexUnsupported(testCase) - import arrow.internal.test.tabular.createTableWithSupportedTypes - filename = fullfile(pwd, 'temp.feather'); - actualTable = createTableWithSupportedTypes; + actualTable = createFeatherCompatibleTable(); actualTable.single(1) = 1.0418 + 2i; actualTable.double(2) = exp(9) + 5i; actualTable.int64(2) = 1.0418e+03; @@ -282,3 +269,11 @@ function UnicodeVariableNames(testCase) end end + +function t = createFeatherCompatibleTable() + import arrow.internal.test.tabular.createTableWithSupportedTypes + t = createTableWithSupportedTypes; + % Remove the duration variable because Feather V1 does not support + % either Time32 or Time64 types. + t = removevars(t, "duration"); +end diff --git a/matlab/tools/cmake/BuildMatlabArrowInterface.cmake b/matlab/tools/cmake/BuildMatlabArrowInterface.cmake index 306ffa7debb2a..fadb1924a13c4 100644 --- a/matlab/tools/cmake/BuildMatlabArrowInterface.cmake +++ b/matlab/tools/cmake/BuildMatlabArrowInterface.cmake @@ -46,6 +46,7 @@ set(MATLAB_ARROW_LIBMEXCLASS_CLIENT_PROXY_SOURCES "${CMAKE_SOURCE_DIR}/src/cpp/a "${CMAKE_SOURCE_DIR}/src/cpp/arrow/matlab/array/proxy/string_array.cc" "${CMAKE_SOURCE_DIR}/src/cpp/arrow/matlab/array/proxy/timestamp_array.cc" "${CMAKE_SOURCE_DIR}/src/cpp/arrow/matlab/array/proxy/time32_array.cc" + "${CMAKE_SOURCE_DIR}/src/cpp/arrow/matlab/array/proxy/time64_array.cc" "${CMAKE_SOURCE_DIR}/src/cpp/arrow/matlab/array/proxy/wrap.cc" "${CMAKE_SOURCE_DIR}/src/cpp/arrow/matlab/tabular/proxy/record_batch.cc" "${CMAKE_SOURCE_DIR}/src/cpp/arrow/matlab/tabular/proxy/schema.cc"