This is an automated email from the ASF dual-hosted git repository.
tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
The following commit(s) were added to refs/heads/master by this push:
new 435b53ded3 Cast from numeric/timestamp to timestamp/numeric (#5123)
435b53ded3 is described below
commit 435b53ded3710216e32ad3f82ae4910f50954e06
Author: Liang-Chi Hsieh <[email protected]>
AuthorDate: Mon Nov 27 03:38:10 2023 -0800
Cast from numeric/timestamp to timestamp/numeric (#5123)
* Casting between floating and timestamp
* Fix
* For decimals
* Fix
---
arrow-cast/src/cast.rs | 104 ++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 89 insertions(+), 15 deletions(-)
diff --git a/arrow-cast/src/cast.rs b/arrow-cast/src/cast.rs
index 3d9d0ee3d9..ebfd97488b 100644
--- a/arrow-cast/src/cast.rs
+++ b/arrow-cast/src/cast.rs
@@ -161,17 +161,16 @@ pub fn can_cast_types(from_type: &DataType, to_type:
&DataType) -> bool {
(Decimal128(_, _) | Decimal256(_, _), Utf8 | LargeUtf8) => true,
// Utf8 to decimal
(Utf8 | LargeUtf8, Decimal128(_, _) | Decimal256(_, _)) => true,
- (Decimal128(_, _) | Decimal256(_, _), _) => false,
- (_, Decimal128(_, _) | Decimal256(_, _)) => false,
(Struct(_), _) => false,
(_, Struct(_)) => false,
(_, Boolean) => {
- DataType::is_numeric(from_type)
+ DataType::is_integer(from_type) ||
+ DataType::is_floating(from_type)
|| from_type == &Utf8
|| from_type == &LargeUtf8
}
(Boolean, _) => {
- DataType::is_numeric(to_type) || to_type == &Utf8 || to_type ==
&LargeUtf8
+ DataType::is_integer(to_type) || DataType::is_floating(to_type) ||
to_type == &Utf8 || to_type == &LargeUtf8
}
(Binary, LargeBinary | Utf8 | LargeUtf8 | FixedSizeBinary(_)) => true,
@@ -222,8 +221,8 @@ pub fn can_cast_types(from_type: &DataType, to_type:
&DataType) -> bool {
(Time64(_), Time32(to_unit)) => {
matches!(to_unit, Second | Millisecond)
}
- (Timestamp(_, _), _) if to_type.is_integer() => true,
- (_, Timestamp(_, _)) if from_type.is_integer() => true,
+ (Timestamp(_, _), _) if to_type.is_numeric() && to_type != &Float16 =>
true,
+ (_, Timestamp(_, _)) if from_type.is_numeric() && from_type !=
&Float16 => true,
(Date64, Timestamp(_, None)) => true,
(Date32, Timestamp(_, None)) => true,
(
@@ -849,7 +848,7 @@ pub fn cast_with_options(
cast_options,
)
}
- (Decimal128(_, scale), _) => {
+ (Decimal128(_, scale), _) if !to_type.is_temporal() => {
// cast decimal to other type
match to_type {
UInt8 => cast_decimal_to_integer::<Decimal128Type, UInt8Type>(
@@ -914,7 +913,7 @@ pub fn cast_with_options(
))),
}
}
- (Decimal256(_, scale), _) => {
+ (Decimal256(_, scale), _) if !to_type.is_temporal() => {
// cast decimal to other type
match to_type {
UInt8 => cast_decimal_to_integer::<Decimal256Type, UInt8Type>(
@@ -979,7 +978,7 @@ pub fn cast_with_options(
))),
}
}
- (_, Decimal128(precision, scale)) => {
+ (_, Decimal128(precision, scale)) if !from_type.is_temporal() => {
// cast data to decimal
match from_type {
UInt8 => cast_integer_to_decimal::<_, Decimal128Type, _>(
@@ -1068,7 +1067,7 @@ pub fn cast_with_options(
))),
}
}
- (_, Decimal256(precision, scale)) => {
+ (_, Decimal256(precision, scale)) if !from_type.is_temporal() => {
// cast data to decimal
match from_type {
UInt8 => cast_integer_to_decimal::<_, Decimal256Type, _>(
@@ -1607,24 +1606,25 @@ pub fn cast_with_options(
.unary::<_, Time64MicrosecondType>(|x| x / (NANOSECONDS /
MICROSECONDS)),
)),
- (Timestamp(TimeUnit::Second, _), _) if to_type.is_integer() => {
+ // Timestamp to integer/floating/decimals
+ (Timestamp(TimeUnit::Second, _), _) if to_type.is_numeric() => {
let array = cast_reinterpret_arrays::<TimestampSecondType,
Int64Type>(array)?;
cast_with_options(&array, to_type, cast_options)
}
- (Timestamp(TimeUnit::Millisecond, _), _) if to_type.is_integer() => {
+ (Timestamp(TimeUnit::Millisecond, _), _) if to_type.is_numeric() => {
let array = cast_reinterpret_arrays::<TimestampMillisecondType,
Int64Type>(array)?;
cast_with_options(&array, to_type, cast_options)
}
- (Timestamp(TimeUnit::Microsecond, _), _) if to_type.is_integer() => {
+ (Timestamp(TimeUnit::Microsecond, _), _) if to_type.is_numeric() => {
let array = cast_reinterpret_arrays::<TimestampMicrosecondType,
Int64Type>(array)?;
cast_with_options(&array, to_type, cast_options)
}
- (Timestamp(TimeUnit::Nanosecond, _), _) if to_type.is_integer() => {
+ (Timestamp(TimeUnit::Nanosecond, _), _) if to_type.is_numeric() => {
let array = cast_reinterpret_arrays::<TimestampNanosecondType,
Int64Type>(array)?;
cast_with_options(&array, to_type, cast_options)
}
- (_, Timestamp(unit, tz)) if from_type.is_integer() => {
+ (_, Timestamp(unit, tz)) if from_type.is_numeric() => {
let array = cast_with_options(array, &Int64, cast_options)?;
Ok(make_timestamp_array(
array.as_primitive(),
@@ -4652,6 +4652,80 @@ mod tests {
assert_eq!(&actual, &expected);
}
+ #[test]
+ fn test_cast_floating_to_timestamp() {
+ let array = Int64Array::from(vec![Some(2), Some(10), None]);
+ let expected = cast(&array,
&DataType::Timestamp(TimeUnit::Microsecond, None)).unwrap();
+
+ let array = Float32Array::from(vec![Some(2.0), Some(10.6), None]);
+ let actual = cast(&array, &DataType::Timestamp(TimeUnit::Microsecond,
None)).unwrap();
+
+ assert_eq!(&actual, &expected);
+
+ let array = Float64Array::from(vec![Some(2.1), Some(10.2), None]);
+ let actual = cast(&array, &DataType::Timestamp(TimeUnit::Microsecond,
None)).unwrap();
+
+ assert_eq!(&actual, &expected);
+ }
+
+ #[test]
+ fn test_cast_timestamp_to_floating() {
+ let array = TimestampMillisecondArray::from(vec![Some(5), Some(1),
None])
+ .with_timezone("UTC".to_string());
+ let expected = cast(&array, &DataType::Int64).unwrap();
+
+ let actual = cast(&cast(&array, &DataType::Float32).unwrap(),
&DataType::Int64).unwrap();
+ assert_eq!(&actual, &expected);
+
+ let actual = cast(&cast(&array, &DataType::Float64).unwrap(),
&DataType::Int64).unwrap();
+ assert_eq!(&actual, &expected);
+ }
+
+ #[test]
+ fn test_cast_decimal_to_timestamp() {
+ let array = Int64Array::from(vec![Some(2), Some(10), None]);
+ let expected = cast(&array,
&DataType::Timestamp(TimeUnit::Microsecond, None)).unwrap();
+
+ let array = Decimal128Array::from(vec![Some(200), Some(1000), None])
+ .with_precision_and_scale(4, 2)
+ .unwrap();
+ let actual = cast(&array, &DataType::Timestamp(TimeUnit::Microsecond,
None)).unwrap();
+
+ assert_eq!(&actual, &expected);
+
+ let array = Decimal256Array::from(vec![
+ Some(i256::from_i128(2000)),
+ Some(i256::from_i128(10000)),
+ None,
+ ])
+ .with_precision_and_scale(5, 3)
+ .unwrap();
+ let actual = cast(&array, &DataType::Timestamp(TimeUnit::Microsecond,
None)).unwrap();
+
+ assert_eq!(&actual, &expected);
+ }
+
+ #[test]
+ fn test_cast_timestamp_to_decimal() {
+ let array = TimestampMillisecondArray::from(vec![Some(5), Some(1),
None])
+ .with_timezone("UTC".to_string());
+ let expected = cast(&array, &DataType::Int64).unwrap();
+
+ let actual = cast(
+ &cast(&array, &DataType::Decimal128(5, 2)).unwrap(),
+ &DataType::Int64,
+ )
+ .unwrap();
+ assert_eq!(&actual, &expected);
+
+ let actual = cast(
+ &cast(&array, &DataType::Decimal256(10, 5)).unwrap(),
+ &DataType::Int64,
+ )
+ .unwrap();
+ assert_eq!(&actual, &expected);
+ }
+
#[test]
fn test_cast_list_i32_to_list_u16() {
let value_data = Int32Array::from(vec![0, 0, 0, -1, -2, -1, 2,
100000000]).into_data();