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