1use std::iter::repeat_n;
19use std::str::FromStr;
20use std::sync::Arc;
21
22use arrow::array::timezone::Tz;
23use arrow::array::{Array, ArrayRef, Float64Array, Int32Array, Int64Array};
24use arrow::compute::kernels::cast_utils::IntervalUnit;
25use arrow::compute::{DatePart, binary, date_part};
26use arrow::datatypes::DataType::{
27 Date32, Date64, Duration, Interval, Time32, Time64, Timestamp,
28};
29use arrow::datatypes::TimeUnit::{Microsecond, Millisecond, Nanosecond, Second};
30use arrow::datatypes::{
31 ArrowTimestampType, DataType, Date32Type, Date64Type, Field, FieldRef,
32 IntervalUnit as ArrowIntervalUnit, TimeUnit, TimestampMicrosecondType,
33 TimestampMillisecondType, TimestampNanosecondType, TimestampSecondType,
34};
35use chrono::{Datelike, NaiveDate};
36use datafusion_common::types::{NativeType, logical_date};
37
38use datafusion_common::{
39 Result, ScalarValue,
40 cast::{
41 as_date32_array, as_date64_array, as_int32_array, as_interval_dt_array,
42 as_interval_mdn_array, as_interval_ym_array, as_time32_millisecond_array,
43 as_time32_second_array, as_time64_microsecond_array, as_time64_nanosecond_array,
44 as_timestamp_microsecond_array, as_timestamp_millisecond_array,
45 as_timestamp_nanosecond_array, as_timestamp_second_array,
46 },
47 exec_err, internal_err, not_impl_err,
48 types::logical_string,
49 utils::take_function_args,
50};
51use datafusion_expr::preimage::PreimageResult;
52use datafusion_expr::simplify::SimplifyContext;
53use datafusion_expr::{
54 ColumnarValue, Documentation, Expr, ReturnFieldArgs, ScalarFunctionArgs,
55 ScalarUDFImpl, Signature, TypeSignature, Volatility, interval_arithmetic,
56};
57use datafusion_expr_common::signature::{Coercion, TypeSignatureClass};
58use datafusion_macros::user_doc;
59
60#[user_doc(
61 doc_section(label = "Time and Date Functions"),
62 description = "Returns the specified part of the date as an integer.",
63 syntax_example = "date_part(part, expression)",
64 alternative_syntax = "extract(field FROM source)",
65 argument(
66 name = "part",
67 description = r#"Part of the date to return. The following date parts are supported:
68
69 - year
70 - isoyear (ISO 8601 week-numbering year)
71 - quarter (emits value in inclusive range [1, 4] based on which quartile of the year the date is in)
72 - month
73 - week (week of the year)
74 - day (day of the month)
75 - hour
76 - minute
77 - second
78 - millisecond
79 - microsecond
80 - nanosecond
81 - dow (day of the week where Sunday is 0)
82 - doy (day of the year)
83 - epoch (seconds since Unix epoch for timestamps/dates, total seconds for intervals)
84 - isodow (ISO 8601 day of the week where Monday is 1 and Sunday is 7)
85"#
86 ),
87 argument(
88 name = "expression",
89 description = "Time expression to operate on. Can be a constant, column, or function."
90 ),
91 sql_example = r#"```sql
92> SELECT date_part('year', '2024-05-01T00:00:00');
93+-----------------------------------------------------+
94| date_part(Utf8("year"),Utf8("2024-05-01T00:00:00")) |
95+-----------------------------------------------------+
96| 2024 |
97+-----------------------------------------------------+
98> SELECT extract(day FROM timestamp '2024-05-01T00:00:00');
99+----------------------------------------------------+
100| date_part(Utf8("DAY"),Utf8("2024-05-01T00:00:00")) |
101+----------------------------------------------------+
102| 1 |
103+----------------------------------------------------+
104```"#
105)]
106#[derive(Debug, PartialEq, Eq, Hash)]
107pub struct DatePartFunc {
108 signature: Signature,
109 aliases: Vec<String>,
110}
111
112impl Default for DatePartFunc {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118impl DatePartFunc {
119 pub fn new() -> Self {
120 Self {
121 signature: Signature::one_of(
122 vec![
123 TypeSignature::Coercible(vec![
124 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
125 Coercion::new_implicit(
126 TypeSignatureClass::Timestamp,
127 vec![TypeSignatureClass::Native(logical_string())],
129 NativeType::Timestamp(Nanosecond, None),
130 ),
131 ]),
132 TypeSignature::Coercible(vec![
133 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
134 Coercion::new_exact(TypeSignatureClass::Native(logical_date())),
135 ]),
136 TypeSignature::Coercible(vec![
137 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
138 Coercion::new_exact(TypeSignatureClass::Time),
139 ]),
140 TypeSignature::Coercible(vec![
141 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
142 Coercion::new_exact(TypeSignatureClass::Interval),
143 ]),
144 TypeSignature::Coercible(vec![
145 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
146 Coercion::new_exact(TypeSignatureClass::Duration),
147 ]),
148 ],
149 Volatility::Immutable,
150 ),
151 aliases: vec![String::from("datepart")],
152 }
153 }
154}
155
156impl ScalarUDFImpl for DatePartFunc {
157 fn name(&self) -> &str {
158 "date_part"
159 }
160
161 fn signature(&self) -> &Signature {
162 &self.signature
163 }
164
165 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
166 internal_err!("return_field_from_args should be called instead")
167 }
168
169 fn return_field_from_args(&self, args: ReturnFieldArgs) -> Result<FieldRef> {
170 let [field, _] = take_function_args(self.name(), args.scalar_arguments)?;
171 let nullable = args.arg_fields[1].is_nullable();
172
173 field
174 .and_then(|sv| {
175 sv.try_as_str()
176 .flatten()
177 .filter(|s| !s.is_empty())
178 .map(|part| {
179 if is_epoch(part) {
180 Field::new(self.name(), DataType::Float64, nullable)
181 } else if is_nanosecond(part) {
182 Field::new(self.name(), DataType::Int64, nullable)
184 } else {
185 Field::new(self.name(), DataType::Int32, nullable)
186 }
187 })
188 })
189 .map(Arc::new)
190 .map_or_else(
191 || exec_err!("{} requires non-empty constant string", self.name()),
192 Ok,
193 )
194 }
195
196 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
197 let args = args.args;
198 let [part, array] = take_function_args(self.name(), args)?;
199
200 let part = if let ColumnarValue::Scalar(ScalarValue::Utf8(Some(v))) = part {
201 v
202 } else if let ColumnarValue::Scalar(ScalarValue::Utf8View(Some(v))) = part {
203 v
204 } else {
205 return exec_err!(
206 "First argument of `DATE_PART` must be non-null scalar Utf8"
207 );
208 };
209
210 let is_scalar = matches!(array, ColumnarValue::Scalar(_));
211
212 let array = match array {
213 ColumnarValue::Array(array) => Arc::clone(&array),
214 ColumnarValue::Scalar(scalar) => scalar.to_array()?,
215 };
216
217 let part_trim = part_normalization(&part);
218
219 let arr = if let Ok(interval_unit) = IntervalUnit::from_str(part_trim) {
222 match interval_unit {
223 IntervalUnit::Year => date_part(array.as_ref(), DatePart::Year)?,
224 IntervalUnit::Month => date_part(array.as_ref(), DatePart::Month)?,
225 IntervalUnit::Week => date_part(array.as_ref(), DatePart::Week)?,
226 IntervalUnit::Day => date_part(array.as_ref(), DatePart::Day)?,
227 IntervalUnit::Hour => date_part(array.as_ref(), DatePart::Hour)?,
228 IntervalUnit::Minute => date_part(array.as_ref(), DatePart::Minute)?,
229 IntervalUnit::Second => seconds_as_i32(array.as_ref(), Second)?,
230 IntervalUnit::Millisecond => seconds_as_i32(array.as_ref(), Millisecond)?,
231 IntervalUnit::Microsecond => seconds_as_i32(array.as_ref(), Microsecond)?,
232 IntervalUnit::Nanosecond => seconds_ns(array.as_ref())?,
233 _ => return exec_err!("Date part '{part}' not supported"),
235 }
236 } else {
237 match part_trim.to_lowercase().as_str() {
239 "isoyear" => date_part(array.as_ref(), DatePart::YearISO)?,
240 "qtr" | "quarter" => date_part(array.as_ref(), DatePart::Quarter)?,
241 "doy" => date_part(array.as_ref(), DatePart::DayOfYear)?,
242 "dow" => date_part(array.as_ref(), DatePart::DayOfWeekSunday0)?,
243 "isodow" => {
244 date_part(array.as_ref(), DatePart::DayOfWeekMonday1)?
246 }
247 "epoch" => epoch(array.as_ref())?,
248 _ => return exec_err!("Date part '{part}' not supported"),
249 }
250 };
251
252 Ok(if is_scalar {
253 ColumnarValue::Scalar(ScalarValue::try_from_array(arr.as_ref(), 0)?)
254 } else {
255 ColumnarValue::Array(arr)
256 })
257 }
258
259 fn preimage(
264 &self,
265 args: &[Expr],
266 lit_expr: &Expr,
267 info: &SimplifyContext,
268 ) -> Result<PreimageResult> {
269 let [part, col_expr] = take_function_args(self.name(), args)?;
270
271 let interval_unit = part
273 .as_literal()
274 .and_then(|sv| sv.try_as_str().flatten())
275 .map(part_normalization)
276 .and_then(|s| IntervalUnit::from_str(s).ok());
277
278 match interval_unit {
280 Some(IntervalUnit::Year) => (),
281 _ => return Ok(PreimageResult::None),
282 }
283
284 let Some(argument_literal) = lit_expr.as_literal() else {
286 return Ok(PreimageResult::None);
287 };
288
289 let year = match argument_literal {
291 ScalarValue::Int32(Some(y)) => *y,
292 _ => return Ok(PreimageResult::None),
293 };
294
295 let target_type = match info.get_data_type(col_expr)? {
297 Date32 | Date64 | Timestamp(_, _) => &info.get_data_type(col_expr)?,
298 _ => return Ok(PreimageResult::None),
299 };
300
301 let Some(start_time) = NaiveDate::from_ymd_opt(year, 1, 1) else {
303 return Ok(PreimageResult::None);
304 };
305 let Some(end_time) = start_time.with_year(year + 1) else {
306 return Ok(PreimageResult::None);
307 };
308
309 let (Some(lower), Some(upper)) = (
311 date_to_scalar(start_time, target_type),
312 date_to_scalar(end_time, target_type),
313 ) else {
314 return Ok(PreimageResult::None);
315 };
316 let interval = Box::new(interval_arithmetic::Interval::try_new(lower, upper)?);
317
318 Ok(PreimageResult::Range {
319 expr: col_expr.clone(),
320 interval,
321 })
322 }
323
324 fn aliases(&self) -> &[String] {
325 &self.aliases
326 }
327
328 fn documentation(&self) -> Option<&Documentation> {
329 self.doc()
330 }
331}
332
333fn is_epoch(part: &str) -> bool {
334 let part = part_normalization(part);
335 matches!(part.to_lowercase().as_str(), "epoch")
336}
337
338fn is_nanosecond(part: &str) -> bool {
339 IntervalUnit::from_str(part_normalization(part))
340 .map(|p| matches!(p, IntervalUnit::Nanosecond))
341 .unwrap_or(false)
342}
343
344fn date_to_scalar(date: NaiveDate, target_type: &DataType) -> Option<ScalarValue> {
345 Some(match target_type {
346 Date32 => ScalarValue::Date32(Some(Date32Type::from_naive_date(date))),
347 Date64 => ScalarValue::Date64(Some(Date64Type::from_naive_date(date))),
348
349 Timestamp(unit, tz_opt) => {
350 let naive_midnight = date.and_hms_opt(0, 0, 0)?;
351 let tz: Option<Tz> = tz_opt.clone().and_then(|s| s.parse().ok());
352
353 match unit {
354 Second => ScalarValue::TimestampSecond(
355 TimestampSecondType::from_naive_datetime(naive_midnight, tz.as_ref()),
356 tz_opt.clone(),
357 ),
358 Millisecond => ScalarValue::TimestampMillisecond(
359 TimestampMillisecondType::from_naive_datetime(
360 naive_midnight,
361 tz.as_ref(),
362 ),
363 tz_opt.clone(),
364 ),
365 Microsecond => ScalarValue::TimestampMicrosecond(
366 TimestampMicrosecondType::from_naive_datetime(
367 naive_midnight,
368 tz.as_ref(),
369 ),
370 tz_opt.clone(),
371 ),
372 Nanosecond => ScalarValue::TimestampNanosecond(
373 TimestampNanosecondType::from_naive_datetime(
374 naive_midnight,
375 tz.as_ref(),
376 ),
377 tz_opt.clone(),
378 ),
379 }
380 }
381 _ => return None,
382 })
383}
384
385fn part_normalization(part: &str) -> &str {
387 part.strip_prefix(|c| c == '\'' || c == '\"')
388 .and_then(|s| s.strip_suffix(|c| c == '\'' || c == '\"'))
389 .unwrap_or(part)
390}
391
392fn seconds_as_i32(array: &dyn Array, unit: TimeUnit) -> Result<ArrayRef> {
396 if unit == Nanosecond {
399 return not_impl_err!("Date part {unit:?} not supported");
400 }
401
402 if unit == Second {
404 return Ok(date_part(array, DatePart::Second)?);
405 }
406
407 if array.data_type() == &Date32 || array.data_type() == &Date64 {
409 return Ok(Arc::new(Int32Array::from_iter_values_with_nulls(
410 repeat_n(0, array.len()),
411 array.nulls().cloned(),
412 )));
413 }
414
415 let conversion_factor = match unit {
416 Second => 1_000_000_000,
417 Millisecond => 1_000_000,
418 Microsecond => 1_000,
419 Nanosecond => 1,
420 };
421
422 let second_factor = match unit {
423 Second => 1,
424 Millisecond => 1_000,
425 Microsecond => 1_000_000,
426 Nanosecond => 1_000_000_000,
427 };
428
429 let secs = date_part(array, DatePart::Second)?;
430 let secs = as_int32_array(secs.as_ref())?;
432 let subsecs = date_part(array, DatePart::Nanosecond)?;
433 let subsecs = as_int32_array(subsecs.as_ref())?;
434
435 if subsecs.null_count() == 0 {
437 let r: Int32Array = binary(secs, subsecs, |secs, subsecs| {
438 secs * second_factor + (subsecs % 1_000_000_000) / conversion_factor
439 })?;
440 Ok(Arc::new(r))
441 } else {
442 let r: Int32Array = secs
445 .iter()
446 .zip(subsecs)
447 .map(|(secs, subsecs)| {
448 secs.map(|secs| {
449 let subsecs = subsecs.unwrap_or(0);
450 secs * second_factor + (subsecs % 1_000_000_000) / conversion_factor
451 })
452 })
453 .collect();
454 Ok(Arc::new(r))
455 }
456}
457
458fn seconds(array: &dyn Array, unit: TimeUnit) -> Result<ArrayRef> {
464 let sf = match unit {
465 Second => 1_f64,
466 Millisecond => 1_000_f64,
467 Microsecond => 1_000_000_f64,
468 Nanosecond => 1_000_000_000_f64,
469 };
470 let secs = date_part(array, DatePart::Second)?;
471 let secs = as_int32_array(secs.as_ref())?;
473 let subsecs = date_part(array, DatePart::Nanosecond)?;
474 let subsecs = as_int32_array(subsecs.as_ref())?;
475
476 if subsecs.null_count() == 0 {
478 let r: Float64Array = binary(secs, subsecs, |secs, subsecs| {
479 (secs as f64 + ((subsecs % 1_000_000_000) as f64 / 1_000_000_000_f64)) * sf
480 })?;
481 Ok(Arc::new(r))
482 } else {
483 let r: Float64Array = secs
486 .iter()
487 .zip(subsecs)
488 .map(|(secs, subsecs)| {
489 secs.map(|secs| {
490 let subsecs = subsecs.unwrap_or(0);
491 (secs as f64 + ((subsecs % 1_000_000_000) as f64 / 1_000_000_000_f64))
492 * sf
493 })
494 })
495 .collect();
496 Ok(Arc::new(r))
497 }
498}
499
500fn epoch(array: &dyn Array) -> Result<ArrayRef> {
501 const SECONDS_IN_A_DAY: f64 = 86400_f64;
502 const DAYS_PER_MONTH: f64 = 30_f64;
507
508 let f: Float64Array = match array.data_type() {
509 Timestamp(Second, _) => as_timestamp_second_array(array)?.unary(|x| x as f64),
510 Timestamp(Millisecond, _) => {
511 as_timestamp_millisecond_array(array)?.unary(|x| x as f64 / 1_000_f64)
512 }
513 Timestamp(Microsecond, _) => {
514 as_timestamp_microsecond_array(array)?.unary(|x| x as f64 / 1_000_000_f64)
515 }
516 Timestamp(Nanosecond, _) => {
517 as_timestamp_nanosecond_array(array)?.unary(|x| x as f64 / 1_000_000_000_f64)
518 }
519 Date32 => as_date32_array(array)?.unary(|x| x as f64 * SECONDS_IN_A_DAY),
520 Date64 => as_date64_array(array)?.unary(|x| x as f64 / 1_000_f64),
521 Time32(Second) => as_time32_second_array(array)?.unary(|x| x as f64),
522 Time32(Millisecond) => {
523 as_time32_millisecond_array(array)?.unary(|x| x as f64 / 1_000_f64)
524 }
525 Time64(Microsecond) => {
526 as_time64_microsecond_array(array)?.unary(|x| x as f64 / 1_000_000_f64)
527 }
528 Time64(Nanosecond) => {
529 as_time64_nanosecond_array(array)?.unary(|x| x as f64 / 1_000_000_000_f64)
530 }
531 Interval(ArrowIntervalUnit::YearMonth) => as_interval_ym_array(array)?
532 .unary(|x| x as f64 * DAYS_PER_MONTH * SECONDS_IN_A_DAY),
533 Interval(ArrowIntervalUnit::DayTime) => as_interval_dt_array(array)?.unary(|x| {
534 x.days as f64 * SECONDS_IN_A_DAY + x.milliseconds as f64 / 1_000_f64
535 }),
536 Interval(ArrowIntervalUnit::MonthDayNano) => {
537 as_interval_mdn_array(array)?.unary(|x| {
538 x.months as f64 * DAYS_PER_MONTH * SECONDS_IN_A_DAY
539 + x.days as f64 * SECONDS_IN_A_DAY
540 + x.nanoseconds as f64 / 1_000_000_000_f64
541 })
542 }
543 Duration(_) => return seconds(array, Second),
544 d => return exec_err!("Cannot convert {d:?} to epoch"),
545 };
546 Ok(Arc::new(f))
547}
548
549fn seconds_ns(array: &dyn Array) -> Result<ArrayRef> {
556 if array.data_type() == &Date32 || array.data_type() == &Date64 {
558 return Ok(Arc::new(Int64Array::from_iter_values_with_nulls(
559 repeat_n(0, array.len()),
560 array.nulls().cloned(),
561 )));
562 }
563
564 let secs = date_part(array, DatePart::Second)?;
565 let secs = as_int32_array(secs.as_ref())?;
567 let subsecs = date_part(array, DatePart::Nanosecond)?;
568 let subsecs = as_int32_array(subsecs.as_ref())?;
569
570 if subsecs.null_count() == 0 {
572 let r: Int64Array = binary(secs, subsecs, |secs, subsecs| {
573 (secs as i64) * 1_000_000_000 + (subsecs as i64)
574 })?;
575 Ok(Arc::new(r))
576 } else {
577 let r: Int64Array = secs
580 .iter()
581 .zip(subsecs)
582 .map(|(secs, subsecs)| {
583 secs.map(|secs| {
584 let subsecs = subsecs.unwrap_or(0);
585 (secs as i64) * 1_000_000_000 + (subsecs as i64)
586 })
587 })
588 .collect();
589 Ok(Arc::new(r))
590 }
591}