mz_expr/scalar/func/impls/
date.rs1use std::fmt;
11
12use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, Utc};
13use mz_expr_derive::sqlfunc;
14use mz_repr::adt::date::Date;
15use mz_repr::adt::datetime::DateTimeUnits;
16use mz_repr::adt::numeric::Numeric;
17use mz_repr::adt::timestamp::{CheckedTimestamp, DateLike, TimestampPrecision};
18use mz_repr::{SqlScalarType, strconv};
19use serde::{Deserialize, Serialize};
20
21use crate::EvalError;
22use crate::func::most_significant_unit;
23
24#[sqlfunc(
25 sqlname = "date_to_text",
26 preserves_uniqueness = true,
27 inverse = super::CastStringToDate
28)]
29fn cast_date_to_string(a: Date) -> String {
30 let mut buf = String::new();
31 strconv::format_date(&mut buf, a);
32 buf
33}
34
35#[derive(
36 Ord,
37 PartialOrd,
38 Clone,
39 Debug,
40 Eq,
41 PartialEq,
42 Serialize,
43 Deserialize,
44 Hash
45)]
46pub struct CastDateToTimestamp(pub Option<TimestampPrecision>);
47
48#[sqlfunc(
49 CastDateToTimestamp,
50 sqlname = "date_to_timestamp",
51 output_type_expr = SqlScalarType::Timestamp { precision: self.0 }.nullable(input_type.nullable),
52 preserves_uniqueness = true,
53 inverse = super::CastTimestampToDate,
54 is_monotone = true
55)]
56fn cast_date_to_timestamp(&self, a: Date) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
57 let out =
58 CheckedTimestamp::from_timestamplike(NaiveDate::from(a).and_hms_opt(0, 0, 0).unwrap())?;
59 let updated = out.round_to_precision(self.0)?;
60 Ok(updated)
61}
62
63#[derive(
64 Ord,
65 PartialOrd,
66 Clone,
67 Debug,
68 Eq,
69 PartialEq,
70 Serialize,
71 Deserialize,
72 Hash
73)]
74pub struct CastDateToTimestampTz(pub Option<TimestampPrecision>);
75
76#[sqlfunc(
77 CastDateToTimestampTz,
78 sqlname = "date_to_timestamp_with_timezone",
79 output_type_expr = SqlScalarType::TimestampTz { precision: self.0 }
80 .nullable(input_type.nullable),
81 preserves_uniqueness = true,
82 inverse = super::CastTimestampTzToDate,
83 is_monotone = true
84)]
85fn cast_date_to_timestamp_tz(&self, a: Date) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
86 let out = CheckedTimestamp::from_timestamplike(DateTime::<Utc>::from_naive_utc_and_offset(
87 NaiveDate::from(a).and_hms_opt(0, 0, 0).unwrap(),
88 Utc,
89 ))?;
90 let updated = out.round_to_precision(self.0)?;
91 Ok(updated)
92}
93
94pub fn extract_date_inner(units: DateTimeUnits, date: NaiveDate) -> Result<Numeric, EvalError> {
95 match units {
96 DateTimeUnits::Epoch => Ok(Numeric::from(date.extract_epoch())),
97 DateTimeUnits::Millennium => Ok(Numeric::from(date.millennium())),
98 DateTimeUnits::Century => Ok(Numeric::from(date.century())),
99 DateTimeUnits::Decade => Ok(Numeric::from(date.decade())),
100 DateTimeUnits::Year => Ok(Numeric::from(date.extract_year())),
101 DateTimeUnits::Quarter => Ok(Numeric::from(date.quarter())),
102 DateTimeUnits::Week => Ok(Numeric::from(date.iso_week_number())),
103 DateTimeUnits::Month => Ok(Numeric::from(date.month())),
104 DateTimeUnits::Day => Ok(Numeric::from(date.day())),
105 DateTimeUnits::DayOfWeek => Ok(Numeric::from(date.day_of_week())),
106 DateTimeUnits::DayOfYear => Ok(Numeric::from(date.ordinal())),
107 DateTimeUnits::IsoDayOfWeek => Ok(Numeric::from(date.iso_day_of_week())),
108 DateTimeUnits::Hour
109 | DateTimeUnits::Minute
110 | DateTimeUnits::Second
111 | DateTimeUnits::Milliseconds
112 | DateTimeUnits::Microseconds => Err(EvalError::UnsupportedUnits(
113 format!("{}", units).into(),
114 "date".into(),
115 )),
116 DateTimeUnits::Timezone
117 | DateTimeUnits::TimezoneHour
118 | DateTimeUnits::TimezoneMinute
119 | DateTimeUnits::IsoDayOfYear => Err(EvalError::Unsupported {
120 feature: format!("'{}' timestamp units", units).into(),
121 discussion_no: None,
122 }),
123 }
124}
125
126#[derive(
127 Ord,
128 PartialOrd,
129 Clone,
130 Debug,
131 Eq,
132 PartialEq,
133 Serialize,
134 Deserialize,
135 Hash
136)]
137pub struct ExtractDate(pub DateTimeUnits);
138
139#[sqlfunc(
140 ExtractDate,
141 skip_display = true,
142 is_monotone = most_significant_unit(self.0)
143)]
144fn extract_date(&self, a: Date) -> Result<Numeric, EvalError> {
145 extract_date_inner(self.0, a.into())
146}
147
148impl fmt::Display for ExtractDate {
149 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150 write!(f, "extract_{}_d", self.0)
151 }
152}