From 970b46e82b4509114cda8f321ad2289b2672ce23 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Mon, 1 Aug 2022 13:19:06 +1000 Subject: [PATCH 1/3] feat: Implement string cast operations for Time32 and Time64 --- arrow/src/compute/kernels/cast.rs | 439 +++++++++++++++++++++++++++++- 1 file changed, 437 insertions(+), 2 deletions(-) diff --git a/arrow/src/compute/kernels/cast.rs b/arrow/src/compute/kernels/cast.rs index ea166f921df..12bb4240bfe 100644 --- a/arrow/src/compute/kernels/cast.rs +++ b/arrow/src/compute/kernels/cast.rs @@ -35,6 +35,7 @@ //! assert_eq!(7.0, c.value(2)); //! ``` +use chrono::Timelike; use std::str; use std::sync::Arc; @@ -136,9 +137,25 @@ pub fn can_cast_types(from_type: &DataType, to_type: &DataType) -> bool { (Utf8, LargeUtf8) => true, (LargeUtf8, Utf8) => true, - (Utf8, Date32 | Date64 | Timestamp(TimeUnit::Nanosecond, None)) => true, + (Utf8, + Date32 + | Date64 + | Time32(TimeUnit::Second) + | Time32(TimeUnit::Millisecond) + | Time64(TimeUnit::Microsecond) + | Time64(TimeUnit::Nanosecond) + | Timestamp(TimeUnit::Nanosecond, None) + ) => true, (Utf8, _) => DataType::is_numeric(to_type), - (LargeUtf8, Date32 | Date64 | Timestamp(TimeUnit::Nanosecond, None)) => true, + (LargeUtf8, + Date32 + | Date64 + | Time32(TimeUnit::Second) + | Time32(TimeUnit::Millisecond) + | Time64(TimeUnit::Microsecond) + | Time64(TimeUnit::Nanosecond) + | Timestamp(TimeUnit::Nanosecond, None) + ) => true, (LargeUtf8, _) => DataType::is_numeric(to_type), (Timestamp(_, _), Utf8) | (Timestamp(_, _), LargeUtf8) => true, (Date32, Utf8) | (Date32, LargeUtf8) => true, @@ -659,6 +676,18 @@ pub fn cast_with_options( Float64 => cast_string_to_numeric::(array, cast_options), Date32 => cast_string_to_date32::(&**array, cast_options), Date64 => cast_string_to_date64::(&**array, cast_options), + Time32(TimeUnit::Second) => { + cast_string_to_time32second::(&**array, cast_options) + } + Time32(TimeUnit::Millisecond) => { + cast_string_to_time32millisecond::(&**array, cast_options) + } + Time64(TimeUnit::Microsecond) => { + cast_string_to_time64microsecond::(&**array, cast_options) + } + Time64(TimeUnit::Nanosecond) => { + cast_string_to_time64nanosecond::(&**array, cast_options) + } Timestamp(TimeUnit::Nanosecond, None) => { cast_string_to_timestamp_ns::(&**array, cast_options) } @@ -793,6 +822,18 @@ pub fn cast_with_options( Float64 => cast_string_to_numeric::(array, cast_options), Date32 => cast_string_to_date32::(&**array, cast_options), Date64 => cast_string_to_date64::(&**array, cast_options), + Time32(TimeUnit::Second) => { + cast_string_to_time32second::(&**array, cast_options) + } + Time32(TimeUnit::Millisecond) => { + cast_string_to_time32millisecond::(&**array, cast_options) + } + Time64(TimeUnit::Microsecond) => { + cast_string_to_time64microsecond::(&**array, cast_options) + } + Time64(TimeUnit::Nanosecond) => { + cast_string_to_time64nanosecond::(&**array, cast_options) + } Timestamp(TimeUnit::Nanosecond, None) => { cast_string_to_timestamp_ns::(&**array, cast_options) } @@ -1584,6 +1625,303 @@ fn cast_string_to_date64( Ok(Arc::new(array) as ArrayRef) } +fn seconds_since_midnight(time: &chrono::NaiveTime) -> i32 { + let sec = time.num_seconds_from_midnight(); + let frac = time.nanosecond(); + let adjust = if frac < 1_000_000_000 { 0 } else { 1 }; + (sec + adjust) as i32 +} + +fn milliseconds_since_midnight(time: &chrono::NaiveTime) -> i32 { + /// The number of nanoseconds per millisecond. + const NANOS_PER_MILLI: u32 = 1_000_000; + /// The number of milliseconds per second. + const MILLIS_PER_SEC: u32 = 1_000; + + let sec = time.num_seconds_from_midnight() * MILLIS_PER_SEC; + let frac = time.nanosecond(); + let (frac, adjust) = if frac < 1_000_000_000 { + (frac, 0) + } else { + (frac - 1_000_000_000, MILLIS_PER_SEC) + }; + (sec + adjust + frac / NANOS_PER_MILLI) as i32 +} + +/// Casts generic string arrays to `Time32SecondArray` +fn cast_string_to_time32second( + array: &dyn Array, + cast_options: &CastOptions, +) -> Result { + let string_array = array + .as_any() + .downcast_ref::>() + .unwrap(); + + let array = if cast_options.safe { + let iter = (0..string_array.len()).map(|i| { + if string_array.is_null(i) { + None + } else { + string_array + .value(i) + .parse::() + .map(|time| seconds_since_midnight(&time)) + .ok() + } + }); + + // Benefit: + // 20% performance improvement + // Soundness: + // The iterator is trustedLen because it comes from an `StringArray`. + unsafe { Time32SecondArray::from_trusted_len_iter(iter) } + } else { + let vec = (0..string_array.len()) + .map(|i| { + if string_array.is_null(i) { + Ok(None) + } else { + let string = string_array + .value(i); + chrono::Duration::days(3); + let result = string + .parse::() + .map(|time| seconds_since_midnight(&time) ); + + Some(result.map_err(|_| { + ArrowError::CastError( + format!("Cannot cast string '{}' to value of arrow::datatypes::types::Time32SecondType type", string), + ) + })) + .transpose() + } + }) + .collect::>>>()?; + + // Benefit: + // 20% performance improvement + // Soundness: + // The iterator is trustedLen because it comes from an `StringArray`. + unsafe { Time32SecondArray::from_trusted_len_iter(vec.iter()) } + }; + + Ok(Arc::new(array) as ArrayRef) +} + +/// Casts generic string arrays to `Time32MillisecondArray` +fn cast_string_to_time32millisecond( + array: &dyn Array, + cast_options: &CastOptions, +) -> Result { + let string_array = array + .as_any() + .downcast_ref::>() + .unwrap(); + + let array = if cast_options.safe { + let iter = (0..string_array.len()).map(|i| { + if string_array.is_null(i) { + None + } else { + string_array + .value(i) + .parse::() + .map(|time| milliseconds_since_midnight(&time)) + .ok() + } + }); + + // Benefit: + // 20% performance improvement + // Soundness: + // The iterator is trustedLen because it comes from an `StringArray`. + unsafe { Time32MillisecondArray::from_trusted_len_iter(iter) } + } else { + let vec = (0..string_array.len()) + .map(|i| { + if string_array.is_null(i) { + Ok(None) + } else { + let string = string_array + .value(i); + + let result = string + .parse::() + .map(|time| milliseconds_since_midnight(&time) ); + + Some(result.map_err(|_| { + ArrowError::CastError( + format!("Cannot cast string '{}' to value of arrow::datatypes::types::Time32MillisecondType type", string), + ) + })) + .transpose() + } + }) + .collect::>>>()?; + + // Benefit: + // 20% performance improvement + // Soundness: + // The iterator is trustedLen because it comes from an `StringArray`. + unsafe { Time32MillisecondArray::from_trusted_len_iter(vec.iter()) } + }; + + Ok(Arc::new(array) as ArrayRef) +} + +fn microseconds_since_midnight(time: &chrono::NaiveTime) -> i64 { + /// The number of nanoseconds per microsecond. + const NANOS_PER_MICRO: i64 = 1_000; + /// The number of microseconds per second. + const MICROS_PER_SEC: i64 = 1_000_000; + + let micros = (time.num_seconds_from_midnight() as i64) * MICROS_PER_SEC; + let frac = time.nanosecond(); + let (frac, adjust) = if frac < 1_000_000_000 { + (frac as i64, 0) + } else { + (frac as i64 - 1_000_000_000, MICROS_PER_SEC) + }; + micros + adjust + frac / NANOS_PER_MICRO +} + +fn nanoseconds_since_midnight(time: &chrono::NaiveTime) -> i64 { + /// The number of nanoseconds per second. + const NANOS_PER_SEC: i64 = 1_000_000_000; + + let nanos = (time.num_seconds_from_midnight() as i64) * NANOS_PER_SEC; + let frac = time.nanosecond(); + let (frac, adjust) = if frac < 1_000_000_000 { + (frac as i64, 0) + } else { + (frac as i64 - 1_000_000_000, NANOS_PER_SEC) + }; + nanos + adjust + frac +} + +/// Casts generic string arrays to `Time64MicrosecondArray` +fn cast_string_to_time64microsecond( + array: &dyn Array, + cast_options: &CastOptions, +) -> Result { + let string_array = array + .as_any() + .downcast_ref::>() + .unwrap(); + + let array = if cast_options.safe { + let iter = (0..string_array.len()).map(|i| { + if string_array.is_null(i) { + None + } else { + string_array + .value(i) + .parse::() + .map(|time| microseconds_since_midnight(&time)) + .ok() + } + }); + + // Benefit: + // 20% performance improvement + // Soundness: + // The iterator is trustedLen because it comes from an `StringArray`. + unsafe { Time64MicrosecondArray::from_trusted_len_iter(iter) } + } else { + let vec = (0..string_array.len()) + .map(|i| { + if string_array.is_null(i) { + Ok(None) + } else { + let string = string_array + .value(i); + + let result = string + .parse::() + .map(|time| microseconds_since_midnight(&time) ); + + Some(result.map_err(|_| { + ArrowError::CastError( + format!("Cannot cast string '{}' to value of arrow::datatypes::types::Time64MicrosecondType type", string), + ) + })) + .transpose() + } + }) + .collect::>>>()?; + + // Benefit: + // 20% performance improvement + // Soundness: + // The iterator is trustedLen because it comes from an `StringArray`. + unsafe { Time64MicrosecondArray::from_trusted_len_iter(vec.iter()) } + }; + + Ok(Arc::new(array) as ArrayRef) +} + +/// Casts generic string arrays to `Time64NanosecondArray` +fn cast_string_to_time64nanosecond( + array: &dyn Array, + cast_options: &CastOptions, +) -> Result { + let string_array = array + .as_any() + .downcast_ref::>() + .unwrap(); + + let array = if cast_options.safe { + let iter = (0..string_array.len()).map(|i| { + if string_array.is_null(i) { + None + } else { + string_array + .value(i) + .parse::() + .map(|time| nanoseconds_since_midnight(&time)) + .ok() + } + }); + + // Benefit: + // 20% performance improvement + // Soundness: + // The iterator is trustedLen because it comes from an `StringArray`. + unsafe { Time64NanosecondArray::from_trusted_len_iter(iter) } + } else { + let vec = (0..string_array.len()) + .map(|i| { + if string_array.is_null(i) { + Ok(None) + } else { + let string = string_array + .value(i); + + let result = string + .parse::() + .map(|time| nanoseconds_since_midnight(&time) ); + + Some(result.map_err(|_| { + ArrowError::CastError( + format!("Cannot cast string '{}' to value of arrow::datatypes::types::Time64NanosecondType type", string), + ) + })) + .transpose() + } + }) + .collect::>>>()?; + + // Benefit: + // 20% performance improvement + // Soundness: + // The iterator is trustedLen because it comes from an `StringArray`. + unsafe { Time64NanosecondArray::from_trusted_len_iter(vec.iter()) } + }; + + Ok(Arc::new(array) as ArrayRef) +} + /// Casts generic string arrays to TimeStampNanosecondArray fn cast_string_to_timestamp_ns( array: &dyn Array, @@ -2166,6 +2504,7 @@ where mod tests { use super::*; use crate::array::BasicDecimalArray; + use crate::datatypes::TimeUnit; use crate::util::decimal::Decimal128; use crate::{buffer::Buffer, util::display::array_value_to_string}; @@ -2854,6 +3193,102 @@ mod tests { } } + #[test] + fn test_cast_string_to_time32second() { + let a1 = Arc::new(StringArray::from(vec![ + Some("08:08:35.091323414"), + Some("08:08:60.091323414"), // leap second + Some("08:08:61.091323414"), // not valid + Some("Not a valid time"), + None, + ])) as ArrayRef; + let a2 = Arc::new(LargeStringArray::from(vec![ + Some("08:08:35.091323414"), + Some("08:08:60.091323414"), // leap second + Some("08:08:61.091323414"), // not valid + Some("Not a valid time"), + None, + ])) as ArrayRef; + for array in &[a1, a2] { + let b = cast(array, &DataType::Time32(TimeUnit::Second)).unwrap(); + let c = b.as_any().downcast_ref::().unwrap(); + assert_eq!(29315, c.value(0)); + assert_eq!(29340, c.value(1)); + assert!(c.is_null(2)); + assert!(c.is_null(3)); + assert!(c.is_null(4)); + } + } + + #[test] + fn test_cast_string_to_time32millisecond() { + let a1 = Arc::new(StringArray::from(vec![ + Some("08:08:35.091323414"), + Some("08:08:60.091323414"), // leap second + Some("08:08:61.091323414"), // not valid + Some("Not a valid time"), + None, + ])) as ArrayRef; + let a2 = Arc::new(LargeStringArray::from(vec![ + Some("08:08:35.091323414"), + Some("08:08:60.091323414"), // leap second + Some("08:08:61.091323414"), // not valid + Some("Not a valid time"), + None, + ])) as ArrayRef; + for array in &[a1, a2] { + let b = cast(array, &DataType::Time32(TimeUnit::Millisecond)).unwrap(); + let c = b.as_any().downcast_ref::().unwrap(); + assert_eq!(29315091, c.value(0)); + assert_eq!(29340091, c.value(1)); + assert!(c.is_null(2)); + assert!(c.is_null(3)); + assert!(c.is_null(4)); + } + } + + #[test] + fn test_cast_string_to_time64microsecond() { + let a1 = Arc::new(StringArray::from(vec![ + Some("08:08:35.091323414"), + Some("Not a valid time"), + None, + ])) as ArrayRef; + let a2 = Arc::new(LargeStringArray::from(vec![ + Some("08:08:35.091323414"), + Some("Not a valid time"), + None, + ])) as ArrayRef; + for array in &[a1, a2] { + let b = cast(array, &DataType::Time64(TimeUnit::Microsecond)).unwrap(); + let c = b.as_any().downcast_ref::().unwrap(); + assert_eq!(29315091323, c.value(0)); + assert!(c.is_null(1)); + assert!(c.is_null(2)); + } + } + + #[test] + fn test_cast_string_to_time64nanosecond() { + let a1 = Arc::new(StringArray::from(vec![ + Some("08:08:35.091323414"), + Some("Not a valid time"), + None, + ])) as ArrayRef; + let a2 = Arc::new(LargeStringArray::from(vec![ + Some("08:08:35.091323414"), + Some("Not a valid time"), + None, + ])) as ArrayRef; + for array in &[a1, a2] { + let b = cast(array, &DataType::Time64(TimeUnit::Nanosecond)).unwrap(); + let c = b.as_any().downcast_ref::().unwrap(); + assert_eq!(29315091323414, c.value(0)); + assert!(c.is_null(1)); + assert!(c.is_null(2)); + } + } + #[test] fn test_cast_string_to_date64() { let a1 = Arc::new(StringArray::from(vec![ From 490f0b35706c80847bd60df4fcf21b117e84c1e8 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Tue, 2 Aug 2022 07:38:51 +1000 Subject: [PATCH 2/3] chore: Remove unnecessary leap second handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the unnecessary conditionals to extract the leap second, as it is already handled when converting to a time unit relative to midnight 🤦🏻‍♂️ --- arrow/src/compute/kernels/cast.rs | 37 ++++++++----------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/arrow/src/compute/kernels/cast.rs b/arrow/src/compute/kernels/cast.rs index 12bb4240bfe..804d0f4c18d 100644 --- a/arrow/src/compute/kernels/cast.rs +++ b/arrow/src/compute/kernels/cast.rs @@ -1626,10 +1626,10 @@ fn cast_string_to_date64( } fn seconds_since_midnight(time: &chrono::NaiveTime) -> i32 { - let sec = time.num_seconds_from_midnight(); - let frac = time.nanosecond(); - let adjust = if frac < 1_000_000_000 { 0 } else { 1 }; - (sec + adjust) as i32 + /// The number of nanoseconds per millisecond. + const NANOS_PER_SEC: u32 = 1_000_000_000; + + (time.num_seconds_from_midnight() + time.nanosecond() / NANOS_PER_SEC) as i32 } fn milliseconds_since_midnight(time: &chrono::NaiveTime) -> i32 { @@ -1638,14 +1638,8 @@ fn milliseconds_since_midnight(time: &chrono::NaiveTime) -> i32 { /// The number of milliseconds per second. const MILLIS_PER_SEC: u32 = 1_000; - let sec = time.num_seconds_from_midnight() * MILLIS_PER_SEC; - let frac = time.nanosecond(); - let (frac, adjust) = if frac < 1_000_000_000 { - (frac, 0) - } else { - (frac - 1_000_000_000, MILLIS_PER_SEC) - }; - (sec + adjust + frac / NANOS_PER_MILLI) as i32 + (time.num_seconds_from_midnight() * MILLIS_PER_SEC + + time.nanosecond() / NANOS_PER_MILLI) as i32 } /// Casts generic string arrays to `Time32SecondArray` @@ -1776,28 +1770,15 @@ fn microseconds_since_midnight(time: &chrono::NaiveTime) -> i64 { /// The number of microseconds per second. const MICROS_PER_SEC: i64 = 1_000_000; - let micros = (time.num_seconds_from_midnight() as i64) * MICROS_PER_SEC; - let frac = time.nanosecond(); - let (frac, adjust) = if frac < 1_000_000_000 { - (frac as i64, 0) - } else { - (frac as i64 - 1_000_000_000, MICROS_PER_SEC) - }; - micros + adjust + frac / NANOS_PER_MICRO + time.num_seconds_from_midnight() as i64 * MICROS_PER_SEC + + time.nanosecond() as i64 / NANOS_PER_MICRO } fn nanoseconds_since_midnight(time: &chrono::NaiveTime) -> i64 { /// The number of nanoseconds per second. const NANOS_PER_SEC: i64 = 1_000_000_000; - let nanos = (time.num_seconds_from_midnight() as i64) * NANOS_PER_SEC; - let frac = time.nanosecond(); - let (frac, adjust) = if frac < 1_000_000_000 { - (frac as i64, 0) - } else { - (frac as i64 - 1_000_000_000, NANOS_PER_SEC) - }; - nanos + adjust + frac + time.num_seconds_from_midnight() as i64 * NANOS_PER_SEC + time.nanosecond() as i64 } /// Casts generic string arrays to `Time64MicrosecondArray` From 7340b1935479c5338a75980e69080508d0a76bf7 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Tue, 2 Aug 2022 08:16:33 +1000 Subject: [PATCH 3/3] chore: Inline trivial functions --- arrow/src/compute/kernels/cast.rs | 82 +++++++++++++++---------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/arrow/src/compute/kernels/cast.rs b/arrow/src/compute/kernels/cast.rs index 804d0f4c18d..097b864fc5d 100644 --- a/arrow/src/compute/kernels/cast.rs +++ b/arrow/src/compute/kernels/cast.rs @@ -1625,28 +1625,14 @@ fn cast_string_to_date64( Ok(Arc::new(array) as ArrayRef) } -fn seconds_since_midnight(time: &chrono::NaiveTime) -> i32 { - /// The number of nanoseconds per millisecond. - const NANOS_PER_SEC: u32 = 1_000_000_000; - - (time.num_seconds_from_midnight() + time.nanosecond() / NANOS_PER_SEC) as i32 -} - -fn milliseconds_since_midnight(time: &chrono::NaiveTime) -> i32 { - /// The number of nanoseconds per millisecond. - const NANOS_PER_MILLI: u32 = 1_000_000; - /// The number of milliseconds per second. - const MILLIS_PER_SEC: u32 = 1_000; - - (time.num_seconds_from_midnight() * MILLIS_PER_SEC - + time.nanosecond() / NANOS_PER_MILLI) as i32 -} - /// Casts generic string arrays to `Time32SecondArray` fn cast_string_to_time32second( array: &dyn Array, cast_options: &CastOptions, ) -> Result { + /// The number of nanoseconds per millisecond. + const NANOS_PER_SEC: u32 = 1_000_000_000; + let string_array = array .as_any() .downcast_ref::>() @@ -1660,7 +1646,11 @@ fn cast_string_to_time32second( string_array .value(i) .parse::() - .map(|time| seconds_since_midnight(&time)) + .map(|time| { + (time.num_seconds_from_midnight() + + time.nanosecond() / NANOS_PER_SEC) + as i32 + }) .ok() } }); @@ -1681,7 +1671,7 @@ fn cast_string_to_time32second( chrono::Duration::days(3); let result = string .parse::() - .map(|time| seconds_since_midnight(&time) ); + .map(|time| (time.num_seconds_from_midnight() + time.nanosecond() / NANOS_PER_SEC) as i32); Some(result.map_err(|_| { ArrowError::CastError( @@ -1708,6 +1698,11 @@ fn cast_string_to_time32millisecond( array: &dyn Array, cast_options: &CastOptions, ) -> Result { + /// The number of nanoseconds per millisecond. + const NANOS_PER_MILLI: u32 = 1_000_000; + /// The number of milliseconds per second. + const MILLIS_PER_SEC: u32 = 1_000; + let string_array = array .as_any() .downcast_ref::>() @@ -1721,7 +1716,11 @@ fn cast_string_to_time32millisecond( string_array .value(i) .parse::() - .map(|time| milliseconds_since_midnight(&time)) + .map(|time| { + (time.num_seconds_from_midnight() * MILLIS_PER_SEC + + time.nanosecond() / NANOS_PER_MILLI) + as i32 + }) .ok() } }); @@ -1742,7 +1741,8 @@ fn cast_string_to_time32millisecond( let result = string .parse::() - .map(|time| milliseconds_since_midnight(&time) ); + .map(|time| (time.num_seconds_from_midnight() * MILLIS_PER_SEC + + time.nanosecond() / NANOS_PER_MILLI) as i32); Some(result.map_err(|_| { ArrowError::CastError( @@ -1764,28 +1764,16 @@ fn cast_string_to_time32millisecond( Ok(Arc::new(array) as ArrayRef) } -fn microseconds_since_midnight(time: &chrono::NaiveTime) -> i64 { - /// The number of nanoseconds per microsecond. - const NANOS_PER_MICRO: i64 = 1_000; - /// The number of microseconds per second. - const MICROS_PER_SEC: i64 = 1_000_000; - - time.num_seconds_from_midnight() as i64 * MICROS_PER_SEC - + time.nanosecond() as i64 / NANOS_PER_MICRO -} - -fn nanoseconds_since_midnight(time: &chrono::NaiveTime) -> i64 { - /// The number of nanoseconds per second. - const NANOS_PER_SEC: i64 = 1_000_000_000; - - time.num_seconds_from_midnight() as i64 * NANOS_PER_SEC + time.nanosecond() as i64 -} - /// Casts generic string arrays to `Time64MicrosecondArray` fn cast_string_to_time64microsecond( array: &dyn Array, cast_options: &CastOptions, ) -> Result { + /// The number of nanoseconds per microsecond. + const NANOS_PER_MICRO: i64 = 1_000; + /// The number of microseconds per second. + const MICROS_PER_SEC: i64 = 1_000_000; + let string_array = array .as_any() .downcast_ref::>() @@ -1799,7 +1787,10 @@ fn cast_string_to_time64microsecond( string_array .value(i) .parse::() - .map(|time| microseconds_since_midnight(&time)) + .map(|time| { + time.num_seconds_from_midnight() as i64 * MICROS_PER_SEC + + time.nanosecond() as i64 / NANOS_PER_MICRO + }) .ok() } }); @@ -1820,7 +1811,8 @@ fn cast_string_to_time64microsecond( let result = string .parse::() - .map(|time| microseconds_since_midnight(&time) ); + .map(|time| time.num_seconds_from_midnight() as i64 * MICROS_PER_SEC + + time.nanosecond() as i64 / NANOS_PER_MICRO); Some(result.map_err(|_| { ArrowError::CastError( @@ -1847,6 +1839,9 @@ fn cast_string_to_time64nanosecond( array: &dyn Array, cast_options: &CastOptions, ) -> Result { + /// The number of nanoseconds per second. + const NANOS_PER_SEC: i64 = 1_000_000_000; + let string_array = array .as_any() .downcast_ref::>() @@ -1860,7 +1855,10 @@ fn cast_string_to_time64nanosecond( string_array .value(i) .parse::() - .map(|time| nanoseconds_since_midnight(&time)) + .map(|time| { + time.num_seconds_from_midnight() as i64 * NANOS_PER_SEC + + time.nanosecond() as i64 + }) .ok() } }); @@ -1881,7 +1879,7 @@ fn cast_string_to_time64nanosecond( let result = string .parse::() - .map(|time| nanoseconds_since_midnight(&time) ); + .map(|time| time.num_seconds_from_midnight() as i64 * NANOS_PER_SEC + time.nanosecond() as i64); Some(result.map_err(|_| { ArrowError::CastError(