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