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_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}