diff --git a/rust/arrow/src/util/display.rs b/rust/arrow/src/util/display.rs index 7d4b10e3402..c559658730e 100644 --- a/rust/arrow/src/util/display.rs +++ b/rust/arrow/src/util/display.rs @@ -44,6 +44,57 @@ macro_rules! make_string { }}; } +macro_rules! make_string_date { + ($array_type:ty, $column: ident, $row: ident) => {{ + let array = $column.as_any().downcast_ref::<$array_type>().unwrap(); + + let s = if array.is_null($row) { + "".to_string() + } else { + array + .value_as_date($row) + .map(|d| d.to_string()) + .unwrap_or_else(|| "ERROR CONVERTING DATE".to_string()) + }; + + Ok(s) + }}; +} + +macro_rules! make_string_time { + ($array_type:ty, $column: ident, $row: ident) => {{ + let array = $column.as_any().downcast_ref::<$array_type>().unwrap(); + + let s = if array.is_null($row) { + "".to_string() + } else { + array + .value_as_time($row) + .map(|d| d.to_string()) + .unwrap_or_else(|| "ERROR CONVERTING DATE".to_string()) + }; + + Ok(s) + }}; +} + +macro_rules! make_string_datetime { + ($array_type:ty, $column: ident, $row: ident) => {{ + let array = $column.as_any().downcast_ref::<$array_type>().unwrap(); + + let s = if array.is_null($row) { + "".to_string() + } else { + array + .value_as_datetime($row) + .map(|d| d.to_string()) + .unwrap_or_else(|| "ERROR CONVERTING DATE".to_string()) + }; + + Ok(s) + }}; +} + // It's not possible to do array.value($row).to_string() for &[u8], let's format it as hex macro_rules! make_string_hex { ($array_type:ty, $column: ident, $row: ident) => {{ @@ -104,30 +155,30 @@ pub fn array_value_to_string(column: &array::ArrayRef, row: usize) -> Result make_string!(array::Float32Array, column, row), DataType::Float64 => make_string!(array::Float64Array, column, row), DataType::Timestamp(unit, _) if *unit == TimeUnit::Second => { - make_string!(array::TimestampSecondArray, column, row) + make_string_datetime!(array::TimestampSecondArray, column, row) } DataType::Timestamp(unit, _) if *unit == TimeUnit::Millisecond => { - make_string!(array::TimestampMillisecondArray, column, row) + make_string_datetime!(array::TimestampMillisecondArray, column, row) } DataType::Timestamp(unit, _) if *unit == TimeUnit::Microsecond => { - make_string!(array::TimestampMicrosecondArray, column, row) + make_string_datetime!(array::TimestampMicrosecondArray, column, row) } DataType::Timestamp(unit, _) if *unit == TimeUnit::Nanosecond => { - make_string!(array::TimestampNanosecondArray, column, row) + make_string_datetime!(array::TimestampNanosecondArray, column, row) } - DataType::Date32(_) => make_string!(array::Date32Array, column, row), - DataType::Date64(_) => make_string!(array::Date64Array, column, row), + DataType::Date32(_) => make_string_date!(array::Date32Array, column, row), + DataType::Date64(_) => make_string_date!(array::Date64Array, column, row), DataType::Time32(unit) if *unit == TimeUnit::Second => { - make_string!(array::Time32SecondArray, column, row) + make_string_time!(array::Time32SecondArray, column, row) } DataType::Time32(unit) if *unit == TimeUnit::Millisecond => { - make_string!(array::Time32MillisecondArray, column, row) + make_string_time!(array::Time32MillisecondArray, column, row) } - DataType::Time32(unit) if *unit == TimeUnit::Microsecond => { - make_string!(array::Time64MicrosecondArray, column, row) + DataType::Time64(unit) if *unit == TimeUnit::Microsecond => { + make_string_time!(array::Time64MicrosecondArray, column, row) } DataType::Time64(unit) if *unit == TimeUnit::Nanosecond => { - make_string!(array::Time64NanosecondArray, column, row) + make_string_time!(array::Time64NanosecondArray, column, row) } DataType::List(_) => make_string_from_list!(column, row), DataType::Dictionary(index_type, _value_type) => match **index_type { diff --git a/rust/arrow/src/util/pretty.rs b/rust/arrow/src/util/pretty.rs index 4896d1a2629..9a0c95db8d4 100644 --- a/rust/arrow/src/util/pretty.rs +++ b/rust/arrow/src/util/pretty.rs @@ -71,10 +71,17 @@ fn create_table(results: &[RecordBatch]) -> Result { #[cfg(test)] mod tests { - use crate::array::{self, PrimitiveBuilder, StringBuilder, StringDictionaryBuilder}; + use crate::{ + array::{ + self, Array, Date32Array, Date64Array, PrimitiveBuilder, StringBuilder, + StringDictionaryBuilder, Time32MillisecondArray, Time32SecondArray, + Time64MicrosecondArray, Time64NanosecondArray, TimestampMicrosecondArray, + TimestampMillisecondArray, TimestampNanosecondArray, TimestampSecondArray, + }, + datatypes::{DataType, Field, Int32Type, Schema}, + }; use super::*; - use crate::datatypes::{DataType, Field, Int32Type, Schema}; use std::sync::Arc; #[test] @@ -160,4 +167,160 @@ mod tests { Ok(()) } + + /// Generate an array with type $ARRAYTYPE with a numeric value of + /// $VALUE, and compare $EXPECTED_RESULT to the output of + /// formatting that array with `pretty_format_batches` + macro_rules! check_datetime { + ($ARRAYTYPE:ident, $VALUE:expr, $EXPECTED_RESULT:expr) => { + let mut builder = $ARRAYTYPE::builder(10); + builder.append_value($VALUE).unwrap(); + builder.append_null().unwrap(); + let array = builder.finish(); + + let schema = Arc::new(Schema::new(vec![Field::new( + "f", + array.data_type().clone(), + true, + )])); + let batch = RecordBatch::try_new(schema, vec![Arc::new(array)]).unwrap(); + + let table = pretty_format_batches(&[batch]).expect("formatting batches"); + + let expected = $EXPECTED_RESULT; + let actual: Vec<&str> = table.lines().collect(); + + assert_eq!(expected, actual, "Actual result:\n\n{:#?}\n\n", actual); + }; + } + + #[test] + fn test_pretty_format_timestamp_second() { + let expected = vec![ + "+---------------------+", + "| f |", + "+---------------------+", + "| 1970-05-09 14:25:11 |", + "| |", + "+---------------------+", + ]; + check_datetime!(TimestampSecondArray, 11111111, expected); + } + + #[test] + fn test_pretty_format_timestamp_millisecond() { + let expected = vec![ + "+-------------------------+", + "| f |", + "+-------------------------+", + "| 1970-01-01 03:05:11.111 |", + "| |", + "+-------------------------+", + ]; + check_datetime!(TimestampMillisecondArray, 11111111, expected); + } + + #[test] + fn test_pretty_format_timestamp_microsecond() { + let expected = vec![ + "+----------------------------+", + "| f |", + "+----------------------------+", + "| 1970-01-01 00:00:11.111111 |", + "| |", + "+----------------------------+", + ]; + check_datetime!(TimestampMicrosecondArray, 11111111, expected); + } + + #[test] + fn test_pretty_format_timestamp_nanosecond() { + let expected = vec![ + "+-------------------------------+", + "| f |", + "+-------------------------------+", + "| 1970-01-01 00:00:00.011111111 |", + "| |", + "+-------------------------------+", + ]; + check_datetime!(TimestampNanosecondArray, 11111111, expected); + } + + #[test] + fn test_pretty_format_date_32() { + let expected = vec![ + "+------------+", + "| f |", + "+------------+", + "| 1973-05-19 |", + "| |", + "+------------+", + ]; + check_datetime!(Date32Array, 1234, expected); + } + + #[test] + fn test_pretty_format_date_64() { + let expected = vec![ + "+------------+", + "| f |", + "+------------+", + "| 2005-03-18 |", + "| |", + "+------------+", + ]; + check_datetime!(Date64Array, 1111111100000, expected); + } + + #[test] + fn test_pretty_format_time_32_second() { + let expected = vec![ + "+----------+", + "| f |", + "+----------+", + "| 00:18:31 |", + "| |", + "+----------+", + ]; + check_datetime!(Time32SecondArray, 1111, expected); + } + + #[test] + fn test_pretty_format_time_32_millisecond() { + let expected = vec![ + "+--------------+", + "| f |", + "+--------------+", + "| 03:05:11.111 |", + "| |", + "+--------------+", + ]; + check_datetime!(Time32MillisecondArray, 11111111, expected); + } + + #[test] + fn test_pretty_format_time_64_microsecond() { + let expected = vec![ + "+-----------------+", + "| f |", + "+-----------------+", + "| 00:00:11.111111 |", + "| |", + "+-----------------+", + ]; + check_datetime!(Time64MicrosecondArray, 11111111, expected); + } + + #[test] + fn test_pretty_format_time_64_nanosecond() { + let expected = vec![ + "+--------------------+", + "| f |", + "+--------------------+", + "| 00:00:00.011111111 |", + "| |", + "+--------------------+", + ]; + check_datetime!(Time64NanosecondArray, 11111111, expected); + } }