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