Skip to main content

mz_expr/scalar/func/impls/
date.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use 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}