diff --git a/datafusion/functions/src/datetime/date_trunc.rs b/datafusion/functions/src/datetime/date_trunc.rs index aca1d24c3116a..8c8a4a1c1b771 100644 --- a/datafusion/functions/src/datetime/date_trunc.rs +++ b/datafusion/functions/src/datetime/date_trunc.rs @@ -22,25 +22,28 @@ use std::str::FromStr; use std::sync::Arc; use arrow::array::temporal_conversions::{ - as_datetime_with_timezone, timestamp_ns_to_datetime, + MICROSECONDS, MILLISECONDS, NANOSECONDS, as_datetime_with_timezone, + timestamp_ns_to_datetime, }; use arrow::array::timezone::Tz; use arrow::array::types::{ - ArrowTimestampType, TimestampMicrosecondType, TimestampMillisecondType, + ArrowTimestampType, Time32MillisecondType, Time32SecondType, Time64MicrosecondType, + Time64NanosecondType, TimestampMicrosecondType, TimestampMillisecondType, TimestampNanosecondType, TimestampSecondType, }; use arrow::array::{Array, ArrayRef, PrimitiveArray}; -use arrow::datatypes::DataType::{self, Null, Timestamp, Utf8, Utf8View}; +use arrow::datatypes::DataType::{self, Time32, Time64, Timestamp}; use arrow::datatypes::TimeUnit::{self, Microsecond, Millisecond, Nanosecond, Second}; use datafusion_common::cast::as_primitive_array; +use datafusion_common::types::{NativeType, logical_date, logical_string}; use datafusion_common::{ - DataFusionError, Result, ScalarValue, exec_datafusion_err, exec_err, plan_err, + DataFusionError, Result, ScalarValue, exec_datafusion_err, exec_err, }; -use datafusion_expr::TypeSignature::Exact; use datafusion_expr::sort_properties::{ExprProperties, SortProperties}; use datafusion_expr::{ - ColumnarValue, Documentation, ScalarUDFImpl, Signature, TIMEZONE_WILDCARD, Volatility, + ColumnarValue, Documentation, ScalarUDFImpl, Signature, TypeSignature, Volatility, }; +use datafusion_expr_common::signature::{Coercion, TypeSignatureClass}; use datafusion_macros::user_doc; use chrono::{ @@ -116,16 +119,30 @@ impl DateTruncGranularity { fn is_fine_granularity_utc(&self) -> bool { self.is_fine_granularity() || matches!(self, Self::Hour | Self::Day) } + + /// Returns true if this granularity is valid for Time types + /// Time types don't have date components, so day/week/month/quarter/year are not valid + fn valid_for_time(&self) -> bool { + matches!( + self, + Self::Hour + | Self::Minute + | Self::Second + | Self::Millisecond + | Self::Microsecond + ) + } } #[user_doc( doc_section(label = "Time and Date Functions"), - description = "Truncates a timestamp value to a specified precision.", + description = "Truncates a timestamp or time value to a specified precision.", syntax_example = "date_trunc(precision, expression)", argument( name = "precision", description = r#"Time precision to truncate to. The following precisions are supported: + For Timestamp types: - year / YEAR - quarter / QUARTER - month / MONTH @@ -136,11 +153,18 @@ impl DateTruncGranularity { - second / SECOND - millisecond / MILLISECOND - microsecond / MICROSECOND + + For Time types (hour, minute, second, millisecond, microsecond only): + - hour / HOUR + - minute / MINUTE + - second / SECOND + - millisecond / MILLISECOND + - microsecond / MICROSECOND "# ), argument( name = "expression", - description = "Time expression to operate on. Can be a constant, column, or function." + description = "Timestamp or time expression to operate on. Can be a constant, column, or function." ) )] #[derive(Debug, PartialEq, Eq, Hash)] @@ -160,45 +184,21 @@ impl DateTruncFunc { Self { signature: Signature::one_of( vec![ - Exact(vec![Utf8, Timestamp(Nanosecond, None)]), - Exact(vec![Utf8View, Timestamp(Nanosecond, None)]), - Exact(vec![ - Utf8, - Timestamp(Nanosecond, Some(TIMEZONE_WILDCARD.into())), - ]), - Exact(vec![ - Utf8View, - Timestamp(Nanosecond, Some(TIMEZONE_WILDCARD.into())), - ]), - Exact(vec![Utf8, Timestamp(Microsecond, None)]), - Exact(vec![Utf8View, Timestamp(Microsecond, None)]), - Exact(vec![ - Utf8, - Timestamp(Microsecond, Some(TIMEZONE_WILDCARD.into())), + TypeSignature::Coercible(vec![ + Coercion::new_exact(TypeSignatureClass::Native(logical_string())), + Coercion::new_implicit( + TypeSignatureClass::Timestamp, + // Allow implicit cast from string and date to timestamp for backward compatibility + vec![ + TypeSignatureClass::Native(logical_string()), + TypeSignatureClass::Native(logical_date()), + ], + NativeType::Timestamp(Nanosecond, None), + ), ]), - Exact(vec![ - Utf8View, - Timestamp(Microsecond, Some(TIMEZONE_WILDCARD.into())), - ]), - Exact(vec![Utf8, Timestamp(Millisecond, None)]), - Exact(vec![Utf8View, Timestamp(Millisecond, None)]), - Exact(vec![ - Utf8, - Timestamp(Millisecond, Some(TIMEZONE_WILDCARD.into())), - ]), - Exact(vec![ - Utf8View, - Timestamp(Millisecond, Some(TIMEZONE_WILDCARD.into())), - ]), - Exact(vec![Utf8, Timestamp(Second, None)]), - Exact(vec![Utf8View, Timestamp(Second, None)]), - Exact(vec![ - Utf8, - Timestamp(Second, Some(TIMEZONE_WILDCARD.into())), - ]), - Exact(vec![ - Utf8View, - Timestamp(Second, Some(TIMEZONE_WILDCARD.into())), + TypeSignature::Coercible(vec![ + Coercion::new_exact(TypeSignatureClass::Native(logical_string())), + Coercion::new_exact(TypeSignatureClass::Time), ]), ], Volatility::Immutable, @@ -222,17 +222,10 @@ impl ScalarUDFImpl for DateTruncFunc { } fn return_type(&self, arg_types: &[DataType]) -> Result { - match &arg_types[1] { - Timestamp(Nanosecond, None) | Utf8 | DataType::Date32 | Null => { - Ok(Timestamp(Nanosecond, None)) - } - Timestamp(Nanosecond, tz_opt) => Ok(Timestamp(Nanosecond, tz_opt.clone())), - Timestamp(Microsecond, tz_opt) => Ok(Timestamp(Microsecond, tz_opt.clone())), - Timestamp(Millisecond, tz_opt) => Ok(Timestamp(Millisecond, tz_opt.clone())), - Timestamp(Second, tz_opt) => Ok(Timestamp(Second, tz_opt.clone())), - _ => plan_err!( - "The date_trunc function can only accept timestamp as the second arg." - ), + if arg_types[1].is_null() { + Ok(Timestamp(Nanosecond, None)) + } else { + Ok(arg_types[1].clone()) } } @@ -248,6 +241,9 @@ impl ScalarUDFImpl for DateTruncFunc { { v.to_lowercase() } else if let ColumnarValue::Scalar(ScalarValue::Utf8View(Some(v))) = granularity + { + v.to_lowercase() + } else if let ColumnarValue::Scalar(ScalarValue::LargeUtf8(Some(v))) = granularity { v.to_lowercase() } else { @@ -256,6 +252,15 @@ impl ScalarUDFImpl for DateTruncFunc { let granularity = DateTruncGranularity::from_str(&granularity_str)?; + // Check upfront if granularity is valid for Time types + let is_time_type = matches!(array.data_type(), Time64(_) | Time32(_)); + if is_time_type && !granularity.valid_for_time() { + return exec_err!( + "date_trunc does not support '{}' granularity for Time types. Valid values are: hour, minute, second, millisecond, microsecond", + granularity_str + ); + } + fn process_array( array: &dyn Array, granularity: DateTruncGranularity, @@ -303,6 +308,10 @@ impl ScalarUDFImpl for DateTruncFunc { } Ok(match array { + ColumnarValue::Scalar(ScalarValue::Null) => { + // NULL input returns NULL timestamp + ColumnarValue::Scalar(ScalarValue::TimestampNanosecond(None, None)) + } ColumnarValue::Scalar(ScalarValue::TimestampNanosecond(v, tz_opt)) => { process_scalar::(v, granularity, tz_opt)? } @@ -315,40 +324,77 @@ impl ScalarUDFImpl for DateTruncFunc { ColumnarValue::Scalar(ScalarValue::TimestampSecond(v, tz_opt)) => { process_scalar::(v, granularity, tz_opt)? } + ColumnarValue::Scalar(ScalarValue::Time64Nanosecond(v)) => { + let truncated = v.map(|val| truncate_time_nanos(val, granularity)); + ColumnarValue::Scalar(ScalarValue::Time64Nanosecond(truncated)) + } + ColumnarValue::Scalar(ScalarValue::Time64Microsecond(v)) => { + let truncated = v.map(|val| truncate_time_micros(val, granularity)); + ColumnarValue::Scalar(ScalarValue::Time64Microsecond(truncated)) + } + ColumnarValue::Scalar(ScalarValue::Time32Millisecond(v)) => { + let truncated = v.map(|val| truncate_time_millis(val, granularity)); + ColumnarValue::Scalar(ScalarValue::Time32Millisecond(truncated)) + } + ColumnarValue::Scalar(ScalarValue::Time32Second(v)) => { + let truncated = v.map(|val| truncate_time_secs(val, granularity)); + ColumnarValue::Scalar(ScalarValue::Time32Second(truncated)) + } ColumnarValue::Array(array) => { let array_type = array.data_type(); - if let Timestamp(unit, tz_opt) = array_type { - match unit { - Second => process_array::( - array, - granularity, - tz_opt, - )?, - Millisecond => process_array::( - array, - granularity, - tz_opt, - )?, - Microsecond => process_array::( - array, - granularity, - tz_opt, - )?, - Nanosecond => process_array::( - array, - granularity, - tz_opt, - )?, + match array_type { + Timestamp(Second, tz_opt) => { + process_array::(array, granularity, tz_opt)? + } + Timestamp(Millisecond, tz_opt) => process_array::< + TimestampMillisecondType, + >( + array, granularity, tz_opt + )?, + Timestamp(Microsecond, tz_opt) => process_array::< + TimestampMicrosecondType, + >( + array, granularity, tz_opt + )?, + Timestamp(Nanosecond, tz_opt) => process_array::< + TimestampNanosecondType, + >( + array, granularity, tz_opt + )?, + Time64(Nanosecond) => { + let arr = as_primitive_array::(array)?; + let result: PrimitiveArray = + arr.unary(|v| truncate_time_nanos(v, granularity)); + ColumnarValue::Array(Arc::new(result)) + } + Time64(Microsecond) => { + let arr = as_primitive_array::(array)?; + let result: PrimitiveArray = + arr.unary(|v| truncate_time_micros(v, granularity)); + ColumnarValue::Array(Arc::new(result)) + } + Time32(Millisecond) => { + let arr = as_primitive_array::(array)?; + let result: PrimitiveArray = + arr.unary(|v| truncate_time_millis(v, granularity)); + ColumnarValue::Array(Arc::new(result)) + } + Time32(Second) => { + let arr = as_primitive_array::(array)?; + let result: PrimitiveArray = + arr.unary(|v| truncate_time_secs(v, granularity)); + ColumnarValue::Array(Arc::new(result)) + } + _ => { + return exec_err!( + "second argument of `date_trunc` is an unsupported array type: {array_type}" + ); } - } else { - return exec_err!( - "second argument of `date_trunc` is an unsupported array type: {array_type}" - ); } } _ => { return exec_err!( - "second argument of `date_trunc` must be timestamp scalar or array" + "second argument of `date_trunc` must be timestamp, time scalar or array" ); } }) @@ -374,6 +420,76 @@ impl ScalarUDFImpl for DateTruncFunc { } } +const NANOS_PER_MICROSECOND: i64 = NANOSECONDS / MICROSECONDS; +const NANOS_PER_MILLISECOND: i64 = NANOSECONDS / MILLISECONDS; +const NANOS_PER_SECOND: i64 = NANOSECONDS; +const NANOS_PER_MINUTE: i64 = 60 * NANOS_PER_SECOND; +const NANOS_PER_HOUR: i64 = 60 * NANOS_PER_MINUTE; + +const MICROS_PER_MILLISECOND: i64 = MICROSECONDS / MILLISECONDS; +const MICROS_PER_SECOND: i64 = MICROSECONDS; +const MICROS_PER_MINUTE: i64 = 60 * MICROS_PER_SECOND; +const MICROS_PER_HOUR: i64 = 60 * MICROS_PER_MINUTE; + +const MILLIS_PER_SECOND: i32 = MILLISECONDS as i32; +const MILLIS_PER_MINUTE: i32 = 60 * MILLIS_PER_SECOND; +const MILLIS_PER_HOUR: i32 = 60 * MILLIS_PER_MINUTE; + +const SECS_PER_MINUTE: i32 = 60; +const SECS_PER_HOUR: i32 = 60 * SECS_PER_MINUTE; + +/// Truncate time in nanoseconds to the specified granularity +fn truncate_time_nanos(value: i64, granularity: DateTruncGranularity) -> i64 { + match granularity { + DateTruncGranularity::Hour => value - (value % NANOS_PER_HOUR), + DateTruncGranularity::Minute => value - (value % NANOS_PER_MINUTE), + DateTruncGranularity::Second => value - (value % NANOS_PER_SECOND), + DateTruncGranularity::Millisecond => value - (value % NANOS_PER_MILLISECOND), + DateTruncGranularity::Microsecond => value - (value % NANOS_PER_MICROSECOND), + // Other granularities are not valid for time - should be caught earlier + _ => value, + } +} + +/// Truncate time in microseconds to the specified granularity +fn truncate_time_micros(value: i64, granularity: DateTruncGranularity) -> i64 { + match granularity { + DateTruncGranularity::Hour => value - (value % MICROS_PER_HOUR), + DateTruncGranularity::Minute => value - (value % MICROS_PER_MINUTE), + DateTruncGranularity::Second => value - (value % MICROS_PER_SECOND), + DateTruncGranularity::Millisecond => value - (value % MICROS_PER_MILLISECOND), + DateTruncGranularity::Microsecond => value, // Already at microsecond precision + // Other granularities are not valid for time + _ => value, + } +} + +/// Truncate time in milliseconds to the specified granularity +fn truncate_time_millis(value: i32, granularity: DateTruncGranularity) -> i32 { + match granularity { + DateTruncGranularity::Hour => value - (value % MILLIS_PER_HOUR), + DateTruncGranularity::Minute => value - (value % MILLIS_PER_MINUTE), + DateTruncGranularity::Second => value - (value % MILLIS_PER_SECOND), + DateTruncGranularity::Millisecond => value, // Already at millisecond precision + DateTruncGranularity::Microsecond => value, // Can't truncate to finer precision + // Other granularities are not valid for time + _ => value, + } +} + +/// Truncate time in seconds to the specified granularity +fn truncate_time_secs(value: i32, granularity: DateTruncGranularity) -> i32 { + match granularity { + DateTruncGranularity::Hour => value - (value % SECS_PER_HOUR), + DateTruncGranularity::Minute => value - (value % SECS_PER_MINUTE), + DateTruncGranularity::Second => value, // Already at second precision + DateTruncGranularity::Millisecond => value, // Can't truncate to finer precision + DateTruncGranularity::Microsecond => value, // Can't truncate to finer precision + // Other granularities are not valid for time + _ => value, + } +} + fn _date_trunc_coarse( granularity: DateTruncGranularity, value: Option, diff --git a/datafusion/sqllogictest/test_files/datetime/timestamps.slt b/datafusion/sqllogictest/test_files/datetime/timestamps.slt index 5749b1c53d852..2df4c07e3a720 100644 --- a/datafusion/sqllogictest/test_files/datetime/timestamps.slt +++ b/datafusion/sqllogictest/test_files/datetime/timestamps.slt @@ -2374,6 +2374,59 @@ select arrow_typeof(date_trunc('microsecond', to_timestamp(61))) ---- Timestamp(ns) +########## +## date_trunc with Time types +########## + +# Truncate time to hour +query D +SELECT date_trunc('hour', TIME '14:30:45'); +---- +14:00:00 + +# Truncate time to minute +query D +SELECT date_trunc('minute', TIME '14:30:45'); +---- +14:30:00 + +# Truncate time to second (removes fractional seconds) +query D +SELECT date_trunc('second', TIME '14:30:45.123456789'); +---- +14:30:45 + +# Truncate time to millisecond +query D +SELECT date_trunc('millisecond', TIME '14:30:45.123456789'); +---- +14:30:45.123 + +# Truncate time to microsecond +query D +SELECT date_trunc('microsecond', TIME '14:30:45.123456789'); +---- +14:30:45.123456 + +# Return type should be Time64(ns) +query T +SELECT arrow_typeof(date_trunc('hour', TIME '14:30:45')); +---- +Time64(ns) + +# Error for granularities not valid for Time types +query error date_trunc does not support 'day' granularity for Time types +SELECT date_trunc('day', TIME '14:30:45'); + +query error date_trunc does not support 'week' granularity for Time types +SELECT date_trunc('week', TIME '14:30:45'); + +query error date_trunc does not support 'month' granularity for Time types +SELECT date_trunc('month', TIME '14:30:45'); + +query error date_trunc does not support 'year' granularity for Time types +SELECT date_trunc('year', TIME '14:30:45'); + # check date_bin query P SELECT date_bin(INTERVAL '1 day', time, '1970-01-01T00:00:00+05:00') FROM foo diff --git a/datafusion/sqllogictest/test_files/information_schema.slt b/datafusion/sqllogictest/test_files/information_schema.slt index 18f72cb9f7798..646cc3dfd5370 100644 --- a/datafusion/sqllogictest/test_files/information_schema.slt +++ b/datafusion/sqllogictest/test_files/information_schema.slt @@ -793,14 +793,11 @@ string_agg String AGGREGATE query TTTTTTTBTTTT rowsort select * from information_schema.routines where routine_name = 'date_trunc' OR routine_name = 'string_agg' OR routine_name = 'rank' ORDER BY routine_name ---- -datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(Microsecond, None) SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(Microsecond, Some("+TZ")) SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(Millisecond, None) SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(Millisecond, Some("+TZ")) SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(Nanosecond, None) SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(Nanosecond, Some("+TZ")) SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(Second, None) SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(Second, Some("+TZ")) SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) +datafusion public date_trunc datafusion public date_trunc FUNCTION true Date SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) +datafusion public date_trunc datafusion public date_trunc FUNCTION true String SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) +datafusion public date_trunc datafusion public date_trunc FUNCTION true Time(Nanosecond) SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) +datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(Nanosecond, None) SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) +datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(Nanosecond, Some("+TZ")) SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) datafusion public rank datafusion public rank FUNCTION true NULL WINDOW Returns the rank of the current row within its partition, allowing gaps between ranks. This function provides a ranking similar to `row_number`, but skips ranks for identical values. rank() datafusion public string_agg datafusion public string_agg FUNCTION true String AGGREGATE Concatenates the values of string expressions and places separator values between them. If ordering is required, strings are concatenated in the specified order. This aggregation function can only mix DISTINCT and ORDER BY if the ordering expression is exactly the same as the first argument expression. string_agg([DISTINCT] expression, delimiter [ORDER BY expression]) @@ -813,30 +810,21 @@ false query TTTITTTTBI select * from information_schema.parameters where specific_name = 'date_trunc' OR specific_name = 'string_agg' OR specific_name = 'rank' ORDER BY specific_name, rid, data_type; ---- +datafusion public date_trunc 1 OUT NULL Date NULL false 0 +datafusion public date_trunc 2 IN expression Date NULL false 0 datafusion public date_trunc 1 IN precision String NULL false 0 -datafusion public date_trunc 2 IN expression Timestamp(Microsecond, None) NULL false 0 -datafusion public date_trunc 1 OUT NULL Timestamp(Microsecond, None) NULL false 0 datafusion public date_trunc 1 IN precision String NULL false 1 -datafusion public date_trunc 2 IN expression Timestamp(Microsecond, Some("+TZ")) NULL false 1 -datafusion public date_trunc 1 OUT NULL Timestamp(Microsecond, Some("+TZ")) NULL false 1 +datafusion public date_trunc 2 IN expression String NULL false 1 +datafusion public date_trunc 1 OUT NULL String NULL false 1 datafusion public date_trunc 1 IN precision String NULL false 2 -datafusion public date_trunc 2 IN expression Timestamp(Millisecond, None) NULL false 2 -datafusion public date_trunc 1 OUT NULL Timestamp(Millisecond, None) NULL false 2 +datafusion public date_trunc 2 IN expression Time(Nanosecond) NULL false 2 +datafusion public date_trunc 1 OUT NULL Time(Nanosecond) NULL false 2 datafusion public date_trunc 1 IN precision String NULL false 3 -datafusion public date_trunc 2 IN expression Timestamp(Millisecond, Some("+TZ")) NULL false 3 -datafusion public date_trunc 1 OUT NULL Timestamp(Millisecond, Some("+TZ")) NULL false 3 +datafusion public date_trunc 2 IN expression Timestamp(Nanosecond, None) NULL false 3 +datafusion public date_trunc 1 OUT NULL Timestamp(Nanosecond, None) NULL false 3 datafusion public date_trunc 1 IN precision String NULL false 4 -datafusion public date_trunc 2 IN expression Timestamp(Nanosecond, None) NULL false 4 -datafusion public date_trunc 1 OUT NULL Timestamp(Nanosecond, None) NULL false 4 -datafusion public date_trunc 1 IN precision String NULL false 5 -datafusion public date_trunc 2 IN expression Timestamp(Nanosecond, Some("+TZ")) NULL false 5 -datafusion public date_trunc 1 OUT NULL Timestamp(Nanosecond, Some("+TZ")) NULL false 5 -datafusion public date_trunc 1 IN precision String NULL false 6 -datafusion public date_trunc 2 IN expression Timestamp(Second, None) NULL false 6 -datafusion public date_trunc 1 OUT NULL Timestamp(Second, None) NULL false 6 -datafusion public date_trunc 1 IN precision String NULL false 7 -datafusion public date_trunc 2 IN expression Timestamp(Second, Some("+TZ")) NULL false 7 -datafusion public date_trunc 1 OUT NULL Timestamp(Second, Some("+TZ")) NULL false 7 +datafusion public date_trunc 2 IN expression Timestamp(Nanosecond, Some("+TZ")) NULL false 4 +datafusion public date_trunc 1 OUT NULL Timestamp(Nanosecond, Some("+TZ")) NULL false 4 datafusion public string_agg 2 IN delimiter Null NULL false 0 datafusion public string_agg 1 IN expression String NULL false 0 datafusion public string_agg 1 OUT NULL String NULL false 0 @@ -862,14 +850,11 @@ repeat String 1 OUT 0 query TT??TTT rowsort show functions like 'date_trunc'; ---- -date_trunc Timestamp(Microsecond, None) [precision, expression] [String, Timestamp(Microsecond, None)] SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -date_trunc Timestamp(Microsecond, Some("+TZ")) [precision, expression] [String, Timestamp(Microsecond, Some("+TZ"))] SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -date_trunc Timestamp(Millisecond, None) [precision, expression] [String, Timestamp(Millisecond, None)] SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -date_trunc Timestamp(Millisecond, Some("+TZ")) [precision, expression] [String, Timestamp(Millisecond, Some("+TZ"))] SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -date_trunc Timestamp(Nanosecond, None) [precision, expression] [String, Timestamp(Nanosecond, None)] SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -date_trunc Timestamp(Nanosecond, Some("+TZ")) [precision, expression] [String, Timestamp(Nanosecond, Some("+TZ"))] SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -date_trunc Timestamp(Second, None) [precision, expression] [String, Timestamp(Second, None)] SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) -date_trunc Timestamp(Second, Some("+TZ")) [precision, expression] [String, Timestamp(Second, Some("+TZ"))] SCALAR Truncates a timestamp value to a specified precision. date_trunc(precision, expression) +date_trunc Date [precision, expression] [String, Date] SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) +date_trunc String [precision, expression] [String, String] SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) +date_trunc Time(Nanosecond) [precision, expression] [String, Time(Nanosecond)] SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) +date_trunc Timestamp(Nanosecond, None) [precision, expression] [String, Timestamp(Nanosecond, None)] SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) +date_trunc Timestamp(Nanosecond, Some("+TZ")) [precision, expression] [String, Timestamp(Nanosecond, Some("+TZ"))] SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) statement ok show functions diff --git a/docs/source/user-guide/sql/scalar_functions.md b/docs/source/user-guide/sql/scalar_functions.md index 360311552f025..4bec2ce015b7d 100644 --- a/docs/source/user-guide/sql/scalar_functions.md +++ b/docs/source/user-guide/sql/scalar_functions.md @@ -2548,7 +2548,7 @@ extract(field FROM source) ### `date_trunc` -Truncates a timestamp value to a specified precision. +Truncates a timestamp or time value to a specified precision. ```sql date_trunc(precision, expression) @@ -2558,6 +2558,8 @@ date_trunc(precision, expression) - **precision**: Time precision to truncate to. The following precisions are supported: + For Timestamp types: + - year / YEAR - quarter / QUARTER - month / MONTH @@ -2569,7 +2571,15 @@ date_trunc(precision, expression) - millisecond / MILLISECOND - microsecond / MICROSECOND -- **expression**: Time expression to operate on. Can be a constant, column, or function. + For Time types (hour, minute, second, millisecond, microsecond only): + + - hour / HOUR + - minute / MINUTE + - second / SECOND + - millisecond / MILLISECOND + - microsecond / MICROSECOND + +- **expression**: Timestamp or time expression to operate on. Can be a constant, column, or function. #### Aliases