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