in datafusion/functions/src/datetime/to_char.rs [313:698]
fn test_to_char() {
let date = "2020-01-02T03:04:05"
.parse::<NaiveDateTime>()
.unwrap()
.with_nanosecond(12345)
.unwrap();
let date2 = "2026-07-08T09:10:11"
.parse::<NaiveDateTime>()
.unwrap()
.with_nanosecond(56789)
.unwrap();
let scalar_data = vec![
(
ScalarValue::Date32(Some(18506)),
ScalarValue::Utf8(Some("%Y::%m::%d".to_string())),
"2020::09::01".to_string(),
),
(
ScalarValue::Date64(Some(date.and_utc().timestamp_millis())),
ScalarValue::Utf8(Some("%Y::%m::%d".to_string())),
"2020::01::02".to_string(),
),
(
ScalarValue::Time32Second(Some(31851)),
ScalarValue::Utf8(Some("%H-%M-%S".to_string())),
"08-50-51".to_string(),
),
(
ScalarValue::Time32Millisecond(Some(18506000)),
ScalarValue::Utf8(Some("%H-%M-%S".to_string())),
"05-08-26".to_string(),
),
(
ScalarValue::Time64Microsecond(Some(12344567000)),
ScalarValue::Utf8(Some("%H-%M-%S %f".to_string())),
"03-25-44 567000000".to_string(),
),
(
ScalarValue::Time64Nanosecond(Some(12344567890000)),
ScalarValue::Utf8(Some("%H-%M-%S %f".to_string())),
"03-25-44 567890000".to_string(),
),
(
ScalarValue::TimestampSecond(Some(date.and_utc().timestamp()), None),
ScalarValue::Utf8(Some("%Y::%m::%d %S::%M::%H".to_string())),
"2020::01::02 05::04::03".to_string(),
),
(
ScalarValue::TimestampMillisecond(
Some(date.and_utc().timestamp_millis()),
None,
),
ScalarValue::Utf8(Some("%Y::%m::%d %S::%M::%H".to_string())),
"2020::01::02 05::04::03".to_string(),
),
(
ScalarValue::TimestampMicrosecond(
Some(date.and_utc().timestamp_micros()),
None,
),
ScalarValue::Utf8(Some("%Y::%m::%d %S::%M::%H %f".to_string())),
"2020::01::02 05::04::03 000012000".to_string(),
),
(
ScalarValue::TimestampNanosecond(
Some(date.and_utc().timestamp_nanos_opt().unwrap()),
None,
),
ScalarValue::Utf8(Some("%Y::%m::%d %S::%M::%H %f".to_string())),
"2020::01::02 05::04::03 000012345".to_string(),
),
];
for (value, format, expected) in scalar_data {
let arg_fields = vec![
Field::new("a", value.data_type(), false),
Field::new("a", format.data_type(), false),
];
let args = datafusion_expr::ScalarFunctionArgs {
args: vec![ColumnarValue::Scalar(value), ColumnarValue::Scalar(format)],
arg_fields: arg_fields.iter().collect(),
number_rows: 1,
return_field: &Field::new("f", DataType::Utf8, true),
};
let result = ToCharFunc::new()
.invoke_with_args(args)
.expect("that to_char parsed values without error");
if let ColumnarValue::Scalar(ScalarValue::Utf8(date)) = result {
assert_eq!(expected, date.unwrap());
} else {
panic!("Expected a scalar value")
}
}
let scalar_array_data = vec![
(
ScalarValue::Date32(Some(18506)),
StringArray::from(vec!["%Y::%m::%d".to_string()]),
"2020::09::01".to_string(),
),
(
ScalarValue::Date64(Some(date.and_utc().timestamp_millis())),
StringArray::from(vec!["%Y::%m::%d".to_string()]),
"2020::01::02".to_string(),
),
(
ScalarValue::Time32Second(Some(31851)),
StringArray::from(vec!["%H-%M-%S".to_string()]),
"08-50-51".to_string(),
),
(
ScalarValue::Time32Millisecond(Some(18506000)),
StringArray::from(vec!["%H-%M-%S".to_string()]),
"05-08-26".to_string(),
),
(
ScalarValue::Time64Microsecond(Some(12344567000)),
StringArray::from(vec!["%H-%M-%S %f".to_string()]),
"03-25-44 567000000".to_string(),
),
(
ScalarValue::Time64Nanosecond(Some(12344567890000)),
StringArray::from(vec!["%H-%M-%S %f".to_string()]),
"03-25-44 567890000".to_string(),
),
(
ScalarValue::TimestampSecond(Some(date.and_utc().timestamp()), None),
StringArray::from(vec!["%Y::%m::%d %S::%M::%H".to_string()]),
"2020::01::02 05::04::03".to_string(),
),
(
ScalarValue::TimestampMillisecond(
Some(date.and_utc().timestamp_millis()),
None,
),
StringArray::from(vec!["%Y::%m::%d %S::%M::%H".to_string()]),
"2020::01::02 05::04::03".to_string(),
),
(
ScalarValue::TimestampMicrosecond(
Some(date.and_utc().timestamp_micros()),
None,
),
StringArray::from(vec!["%Y::%m::%d %S::%M::%H %f".to_string()]),
"2020::01::02 05::04::03 000012000".to_string(),
),
(
ScalarValue::TimestampNanosecond(
Some(date.and_utc().timestamp_nanos_opt().unwrap()),
None,
),
StringArray::from(vec!["%Y::%m::%d %S::%M::%H %f".to_string()]),
"2020::01::02 05::04::03 000012345".to_string(),
),
];
for (value, format, expected) in scalar_array_data {
let batch_len = format.len();
let arg_fields = vec![
Field::new("a", value.data_type(), false),
Field::new("a", format.data_type().to_owned(), false),
];
let args = datafusion_expr::ScalarFunctionArgs {
args: vec![
ColumnarValue::Scalar(value),
ColumnarValue::Array(Arc::new(format) as ArrayRef),
],
arg_fields: arg_fields.iter().collect(),
number_rows: batch_len,
return_field: &Field::new("f", DataType::Utf8, true),
};
let result = ToCharFunc::new()
.invoke_with_args(args)
.expect("that to_char parsed values without error");
if let ColumnarValue::Scalar(ScalarValue::Utf8(date)) = result {
assert_eq!(expected, date.unwrap());
} else {
panic!("Expected a scalar value")
}
}
let array_scalar_data = vec![
(
Arc::new(Date32Array::from(vec![18506, 18507])) as ArrayRef,
ScalarValue::Utf8(Some("%Y::%m::%d".to_string())),
StringArray::from(vec!["2020::09::01", "2020::09::02"]),
),
(
Arc::new(Date64Array::from(vec![
date.and_utc().timestamp_millis(),
date2.and_utc().timestamp_millis(),
])) as ArrayRef,
ScalarValue::Utf8(Some("%Y::%m::%d".to_string())),
StringArray::from(vec!["2020::01::02", "2026::07::08"]),
),
];
let array_array_data = vec![
(
Arc::new(Date32Array::from(vec![18506, 18507])) as ArrayRef,
StringArray::from(vec!["%Y::%m::%d", "%d::%m::%Y"]),
StringArray::from(vec!["2020::09::01", "02::09::2020"]),
),
(
Arc::new(Date64Array::from(vec![
date.and_utc().timestamp_millis(),
date2.and_utc().timestamp_millis(),
])) as ArrayRef,
StringArray::from(vec!["%Y::%m::%d", "%d::%m::%Y"]),
StringArray::from(vec!["2020::01::02", "08::07::2026"]),
),
(
Arc::new(Time32MillisecondArray::from(vec![1850600, 1860700]))
as ArrayRef,
StringArray::from(vec!["%H:%M:%S", "%H::%M::%S"]),
StringArray::from(vec!["00:30:50", "00::31::00"]),
),
(
Arc::new(Time32SecondArray::from(vec![18506, 18507])) as ArrayRef,
StringArray::from(vec!["%H:%M:%S", "%H::%M::%S"]),
StringArray::from(vec!["05:08:26", "05::08::27"]),
),
(
Arc::new(Time64MicrosecondArray::from(vec![12344567000, 22244567000]))
as ArrayRef,
StringArray::from(vec!["%H:%M:%S", "%H::%M::%S"]),
StringArray::from(vec!["03:25:44", "06::10::44"]),
),
(
Arc::new(Time64NanosecondArray::from(vec![
1234456789000,
2224456789000,
])) as ArrayRef,
StringArray::from(vec!["%H:%M:%S", "%H::%M::%S"]),
StringArray::from(vec!["00:20:34", "00::37::04"]),
),
(
Arc::new(TimestampSecondArray::from(vec![
date.and_utc().timestamp(),
date2.and_utc().timestamp(),
])) as ArrayRef,
StringArray::from(vec!["%Y::%m::%d %S::%M::%H", "%d::%m::%Y %S-%M-%H"]),
StringArray::from(vec![
"2020::01::02 05::04::03",
"08::07::2026 11-10-09",
]),
),
(
Arc::new(TimestampMillisecondArray::from(vec![
date.and_utc().timestamp_millis(),
date2.and_utc().timestamp_millis(),
])) as ArrayRef,
StringArray::from(vec![
"%Y::%m::%d %S::%M::%H %f",
"%d::%m::%Y %S-%M-%H %f",
]),
StringArray::from(vec![
"2020::01::02 05::04::03 000000000",
"08::07::2026 11-10-09 000000000",
]),
),
(
Arc::new(TimestampMicrosecondArray::from(vec![
date.and_utc().timestamp_micros(),
date2.and_utc().timestamp_micros(),
])) as ArrayRef,
StringArray::from(vec![
"%Y::%m::%d %S::%M::%H %f",
"%d::%m::%Y %S-%M-%H %f",
]),
StringArray::from(vec![
"2020::01::02 05::04::03 000012000",
"08::07::2026 11-10-09 000056000",
]),
),
(
Arc::new(TimestampNanosecondArray::from(vec![
date.and_utc().timestamp_nanos_opt().unwrap(),
date2.and_utc().timestamp_nanos_opt().unwrap(),
])) as ArrayRef,
StringArray::from(vec![
"%Y::%m::%d %S::%M::%H %f",
"%d::%m::%Y %S-%M-%H %f",
]),
StringArray::from(vec![
"2020::01::02 05::04::03 000012345",
"08::07::2026 11-10-09 000056789",
]),
),
];
for (value, format, expected) in array_scalar_data {
let batch_len = value.len();
let arg_fields = vec![
Field::new("a", value.data_type().clone(), false),
Field::new("a", format.data_type(), false),
];
let args = datafusion_expr::ScalarFunctionArgs {
args: vec![
ColumnarValue::Array(value as ArrayRef),
ColumnarValue::Scalar(format),
],
arg_fields: arg_fields.iter().collect(),
number_rows: batch_len,
return_field: &Field::new("f", DataType::Utf8, true),
};
let result = ToCharFunc::new()
.invoke_with_args(args)
.expect("that to_char parsed values without error");
if let ColumnarValue::Array(result) = result {
assert_eq!(result.len(), 2);
assert_eq!(&expected as &dyn Array, result.as_ref());
} else {
panic!("Expected an array value")
}
}
for (value, format, expected) in array_array_data {
let batch_len = value.len();
let arg_fields = vec![
Field::new("a", value.data_type().clone(), false),
Field::new("a", format.data_type().clone(), false),
];
let args = datafusion_expr::ScalarFunctionArgs {
args: vec![
ColumnarValue::Array(value),
ColumnarValue::Array(Arc::new(format) as ArrayRef),
],
arg_fields: arg_fields.iter().collect(),
number_rows: batch_len,
return_field: &Field::new("f", DataType::Utf8, true),
};
let result = ToCharFunc::new()
.invoke_with_args(args)
.expect("that to_char parsed values without error");
if let ColumnarValue::Array(result) = result {
assert_eq!(result.len(), 2);
assert_eq!(&expected as &dyn Array, result.as_ref());
} else {
panic!("Expected an array value")
}
}
//
// Fallible test cases
//
// invalid number of arguments
let arg_field = Field::new("a", DataType::Int32, true);
let args = datafusion_expr::ScalarFunctionArgs {
args: vec![ColumnarValue::Scalar(ScalarValue::Int32(Some(1)))],
arg_fields: vec![&arg_field],
number_rows: 1,
return_field: &Field::new("f", DataType::Utf8, true),
};
let result = ToCharFunc::new().invoke_with_args(args);
assert_eq!(
result.err().unwrap().strip_backtrace(),
"Execution error: to_char function requires 2 arguments, got 1"
);
// invalid type
let arg_fields = vec![
Field::new("a", DataType::Utf8, true),
Field::new("a", DataType::Timestamp(TimeUnit::Nanosecond, None), true),
];
let args = datafusion_expr::ScalarFunctionArgs {
args: vec![
ColumnarValue::Scalar(ScalarValue::Int32(Some(1))),
ColumnarValue::Scalar(ScalarValue::TimestampNanosecond(Some(1), None)),
],
arg_fields: arg_fields.iter().collect(),
number_rows: 1,
return_field: &Field::new("f", DataType::Utf8, true),
};
let result = ToCharFunc::new().invoke_with_args(args);
assert_eq!(
result.err().unwrap().strip_backtrace(),
"Execution error: Format for `to_char` must be non-null Utf8, received Timestamp(Nanosecond, None)"
);
}