mz_expr/scalar/func/impls/
time.rs1use std::fmt;
11
12use chrono::{NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike};
13use mz_expr_derive::sqlfunc;
14use mz_pgtz::timezone::Timezone;
15use mz_repr::adt::datetime::{DateTimeField, DateTimeUnits};
16use mz_repr::adt::interval::Interval;
17use mz_repr::adt::numeric::{DecimalLike, Numeric};
18use mz_repr::adt::timestamp::TimeLike;
19use mz_repr::{SqlColumnType, SqlScalarType, strconv};
20use serde::{Deserialize, Serialize};
21
22use crate::EvalError;
23use crate::scalar::func::EagerUnaryFunc;
24
25#[sqlfunc(
26 sqlname = "time_to_text",
27 preserves_uniqueness = true,
28 inverse = to_unary!(super::CastStringToTime)
29)]
30fn cast_time_to_string(a: NaiveTime) -> String {
31 let mut buf = String::new();
32 strconv::format_time(&mut buf, a);
33 buf
34}
35
36#[sqlfunc(
37 sqlname = "time_to_interval",
38 preserves_uniqueness = true,
39 inverse = to_unary!(super::CastIntervalToTime)
40)]
41fn cast_time_to_interval(t: NaiveTime) -> Interval {
42 let micros: i64 = Interval::convert_date_time_unit(
44 DateTimeField::Second,
45 DateTimeField::Microseconds,
46 i64::from(t.num_seconds_from_midnight()),
47 )
48 .unwrap()
49 + i64::from(t.nanosecond()) / i64::from(Interval::NANOSECOND_PER_MICROSECOND);
50
51 Interval::new(0, 0, micros)
52}
53
54pub fn date_part_time_inner<D>(units: DateTimeUnits, time: NaiveTime) -> Result<D, EvalError>
55where
56 D: DecimalLike,
57{
58 match units {
59 DateTimeUnits::Epoch => Ok(time.extract_epoch()),
60 DateTimeUnits::Hour => Ok(D::from(time.hour())),
61 DateTimeUnits::Minute => Ok(D::from(time.minute())),
62 DateTimeUnits::Second => Ok(time.extract_second()),
63 DateTimeUnits::Milliseconds => Ok(time.extract_millisecond()),
64 DateTimeUnits::Microseconds => Ok(time.extract_microsecond()),
65 DateTimeUnits::Millennium
66 | DateTimeUnits::Century
67 | DateTimeUnits::Decade
68 | DateTimeUnits::Year
69 | DateTimeUnits::Quarter
70 | DateTimeUnits::Month
71 | DateTimeUnits::Week
72 | DateTimeUnits::Day
73 | DateTimeUnits::DayOfYear
74 | DateTimeUnits::DayOfWeek
75 | DateTimeUnits::IsoDayOfYear
76 | DateTimeUnits::IsoDayOfWeek => Err(EvalError::UnsupportedUnits(
77 format!("{}", units).into(),
78 "time".into(),
79 )),
80 DateTimeUnits::Timezone | DateTimeUnits::TimezoneHour | DateTimeUnits::TimezoneMinute => {
81 Err(EvalError::Unsupported {
82 feature: format!("'{}' timestamp units", units).into(),
83 discussion_no: None,
84 })
85 }
86 }
87}
88
89#[derive(
90 Ord,
91 PartialOrd,
92 Clone,
93 Debug,
94 Eq,
95 PartialEq,
96 Serialize,
97 Deserialize,
98 Hash
99)]
100pub struct ExtractTime(pub DateTimeUnits);
101
102impl EagerUnaryFunc for ExtractTime {
103 type Input<'a> = NaiveTime;
104 type Output<'a> = Result<Numeric, EvalError>;
105
106 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
107 date_part_time_inner(self.0, a)
108 }
109
110 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
111 SqlScalarType::Numeric { max_scale: None }.nullable(input.nullable)
112 }
113}
114
115impl fmt::Display for ExtractTime {
116 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117 write!(f, "extract_{}_t", self.0)
118 }
119}
120
121#[derive(
122 Ord,
123 PartialOrd,
124 Clone,
125 Debug,
126 Eq,
127 PartialEq,
128 Serialize,
129 Deserialize,
130 Hash
131)]
132pub struct DatePartTime(pub DateTimeUnits);
133
134impl EagerUnaryFunc for DatePartTime {
135 type Input<'a> = NaiveTime;
136 type Output<'a> = Result<f64, EvalError>;
137
138 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
139 date_part_time_inner(self.0, a)
140 }
141
142 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
143 SqlScalarType::Float64.nullable(input.nullable)
144 }
145}
146
147impl fmt::Display for DatePartTime {
148 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149 write!(f, "date_part_{}_t", self.0)
150 }
151}
152
153pub fn timezone_time(tz: Timezone, t: NaiveTime, wall_time: &NaiveDateTime) -> NaiveTime {
156 let offset = match tz {
157 Timezone::FixedOffset(offset) => offset,
158 Timezone::Tz(tz) => tz.offset_from_utc_datetime(wall_time).fix(),
159 };
160 t + offset
161}
162
163#[derive(
164 Ord,
165 PartialOrd,
166 Clone,
167 Debug,
168 Eq,
169 PartialEq,
170 Serialize,
171 Deserialize,
172 Hash
173)]
174pub struct TimezoneTime {
175 pub tz: Timezone,
176 pub wall_time: NaiveDateTime,
177}
178
179impl EagerUnaryFunc for TimezoneTime {
180 type Input<'a> = NaiveTime;
181 type Output<'a> = NaiveTime;
182
183 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
184 timezone_time(self.tz, a, &self.wall_time)
185 }
186
187 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
188 SqlScalarType::Time.nullable(input.nullable)
189 }
190}
191
192impl fmt::Display for TimezoneTime {
193 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
194 write!(f, "timezone_{}_t", self.tz)
195 }
196}