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