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::{SqlColumnType, SqlScalarType, strconv};
19use serde::{Deserialize, Serialize};
20
21use crate::EvalError;
22use crate::func::most_significant_unit;
23use crate::scalar::func::EagerUnaryFunc;
24
25#[sqlfunc(
26 sqlname = "date_to_text",
27 preserves_uniqueness = true,
28 inverse = to_unary!(super::CastStringToDate)
29)]
30fn cast_date_to_string(a: Date) -> String {
31 let mut buf = String::new();
32 strconv::format_date(&mut buf, a);
33 buf
34}
35
36#[derive(
37 Ord,
38 PartialOrd,
39 Clone,
40 Debug,
41 Eq,
42 PartialEq,
43 Serialize,
44 Deserialize,
45 Hash
46)]
47pub struct CastDateToTimestamp(pub Option<TimestampPrecision>);
48
49impl EagerUnaryFunc for CastDateToTimestamp {
50 type Input<'a> = Date;
51 type Output<'a> = Result<CheckedTimestamp<NaiveDateTime>, EvalError>;
52
53 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
54 let out =
55 CheckedTimestamp::from_timestamplike(NaiveDate::from(a).and_hms_opt(0, 0, 0).unwrap())?;
56 let updated = out.round_to_precision(self.0)?;
57 Ok(updated)
58 }
59
60 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
61 SqlScalarType::Timestamp { precision: self.0 }.nullable(input.nullable)
62 }
63
64 fn preserves_uniqueness(&self) -> bool {
65 true
66 }
67
68 fn inverse(&self) -> Option<crate::UnaryFunc> {
69 to_unary!(super::CastTimestampToDate)
70 }
71
72 fn is_monotone(&self) -> bool {
73 true
74 }
75}
76
77impl fmt::Display for CastDateToTimestamp {
78 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79 f.write_str("date_to_timestamp")
80 }
81}
82
83#[derive(
84 Ord,
85 PartialOrd,
86 Clone,
87 Debug,
88 Eq,
89 PartialEq,
90 Serialize,
91 Deserialize,
92 Hash
93)]
94pub struct CastDateToTimestampTz(pub Option<TimestampPrecision>);
95
96impl EagerUnaryFunc for CastDateToTimestampTz {
97 type Input<'a> = Date;
98 type Output<'a> = Result<CheckedTimestamp<DateTime<Utc>>, EvalError>;
99
100 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
101 let out =
102 CheckedTimestamp::from_timestamplike(DateTime::<Utc>::from_naive_utc_and_offset(
103 NaiveDate::from(a).and_hms_opt(0, 0, 0).unwrap(),
104 Utc,
105 ))?;
106 let updated = out.round_to_precision(self.0)?;
107 Ok(updated)
108 }
109
110 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
111 SqlScalarType::TimestampTz { precision: self.0 }.nullable(input.nullable)
112 }
113
114 fn preserves_uniqueness(&self) -> bool {
115 true
116 }
117
118 fn inverse(&self) -> Option<crate::UnaryFunc> {
119 to_unary!(super::CastTimestampTzToDate)
120 }
121
122 fn is_monotone(&self) -> bool {
123 true
124 }
125}
126
127impl fmt::Display for CastDateToTimestampTz {
128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129 f.write_str("date_to_timestamp_with_timezone")
130 }
131}
132
133pub fn extract_date_inner(units: DateTimeUnits, date: NaiveDate) -> Result<Numeric, EvalError> {
134 match units {
135 DateTimeUnits::Epoch => Ok(Numeric::from(date.extract_epoch())),
136 DateTimeUnits::Millennium => Ok(Numeric::from(date.millennium())),
137 DateTimeUnits::Century => Ok(Numeric::from(date.century())),
138 DateTimeUnits::Decade => Ok(Numeric::from(date.decade())),
139 DateTimeUnits::Year => Ok(Numeric::from(date.extract_year())),
140 DateTimeUnits::Quarter => Ok(Numeric::from(date.quarter())),
141 DateTimeUnits::Week => Ok(Numeric::from(date.iso_week_number())),
142 DateTimeUnits::Month => Ok(Numeric::from(date.month())),
143 DateTimeUnits::Day => Ok(Numeric::from(date.day())),
144 DateTimeUnits::DayOfWeek => Ok(Numeric::from(date.day_of_week())),
145 DateTimeUnits::DayOfYear => Ok(Numeric::from(date.ordinal())),
146 DateTimeUnits::IsoDayOfWeek => Ok(Numeric::from(date.iso_day_of_week())),
147 DateTimeUnits::Hour
148 | DateTimeUnits::Minute
149 | DateTimeUnits::Second
150 | DateTimeUnits::Milliseconds
151 | DateTimeUnits::Microseconds => Err(EvalError::UnsupportedUnits(
152 format!("{}", units).into(),
153 "date".into(),
154 )),
155 DateTimeUnits::Timezone
156 | DateTimeUnits::TimezoneHour
157 | DateTimeUnits::TimezoneMinute
158 | DateTimeUnits::IsoDayOfYear => Err(EvalError::Unsupported {
159 feature: format!("'{}' timestamp units", units).into(),
160 discussion_no: None,
161 }),
162 }
163}
164
165#[derive(
166 Ord,
167 PartialOrd,
168 Clone,
169 Debug,
170 Eq,
171 PartialEq,
172 Serialize,
173 Deserialize,
174 Hash
175)]
176pub struct ExtractDate(pub DateTimeUnits);
177
178impl EagerUnaryFunc for ExtractDate {
179 type Input<'a> = Date;
180 type Output<'a> = Result<Numeric, EvalError>;
181
182 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
183 extract_date_inner(self.0, a.into())
184 }
185
186 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
187 SqlScalarType::Numeric { max_scale: None }.nullable(input.nullable)
188 }
189
190 fn is_monotone(&self) -> bool {
191 most_significant_unit(self.0)
192 }
193}
194
195impl fmt::Display for ExtractDate {
196 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197 write!(f, "extract_{}_d", self.0)
198 }
199}