mz_expr/scalar/func/impls/
time.rs1use std::fmt;
11
12use chrono::{NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike};
13use mz_lowertest::MzReflect;
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
25sqlfunc!(
26 #[sqlname = "time_to_text"]
27 #[preserves_uniqueness = true]
28 #[inverse = to_unary!(super::CastStringToTime)]
29 fn cast_time_to_string(a: NaiveTime) -> String {
30 let mut buf = String::new();
31 strconv::format_time(&mut buf, a);
32 buf
33 }
34);
35
36sqlfunc!(
37 #[sqlname = "time_to_interval"]
38 #[preserves_uniqueness = true]
39 #[inverse = to_unary!(super::CastIntervalToTime)]
40 fn cast_time_to_interval<'a>(t: NaiveTime) -> Interval {
41 let micros: i64 = Interval::convert_date_time_unit(
43 DateTimeField::Second,
44 DateTimeField::Microseconds,
45 i64::from(t.num_seconds_from_midnight()),
46 )
47 .unwrap()
48 + i64::from(t.nanosecond()) / i64::from(Interval::NANOSECOND_PER_MICROSECOND);
49
50 Interval::new(0, 0, micros)
51 }
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(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
90pub struct ExtractTime(pub DateTimeUnits);
91
92impl<'a> EagerUnaryFunc<'a> for ExtractTime {
93 type Input = NaiveTime;
94 type Output = Result<Numeric, EvalError>;
95
96 fn call(&self, a: NaiveTime) -> Result<Numeric, EvalError> {
97 date_part_time_inner(self.0, a)
98 }
99
100 fn output_type(&self, input: SqlColumnType) -> SqlColumnType {
101 SqlScalarType::Numeric { max_scale: None }.nullable(input.nullable)
102 }
103}
104
105impl fmt::Display for ExtractTime {
106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107 write!(f, "extract_{}_t", self.0)
108 }
109}
110
111#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
112pub struct DatePartTime(pub DateTimeUnits);
113
114impl<'a> EagerUnaryFunc<'a> for DatePartTime {
115 type Input = NaiveTime;
116 type Output = Result<f64, EvalError>;
117
118 fn call(&self, a: NaiveTime) -> Result<f64, EvalError> {
119 date_part_time_inner(self.0, a)
120 }
121
122 fn output_type(&self, input: SqlColumnType) -> SqlColumnType {
123 SqlScalarType::Float64.nullable(input.nullable)
124 }
125}
126
127impl fmt::Display for DatePartTime {
128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129 write!(f, "date_part_{}_t", self.0)
130 }
131}
132
133pub fn timezone_time(tz: Timezone, t: NaiveTime, wall_time: &NaiveDateTime) -> NaiveTime {
136 let offset = match tz {
137 Timezone::FixedOffset(offset) => offset,
138 Timezone::Tz(tz) => tz.offset_from_utc_datetime(wall_time).fix(),
139 };
140 t + offset
141}
142
143#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
144pub struct TimezoneTime {
145 pub tz: Timezone,
146 pub wall_time: NaiveDateTime,
147}
148
149impl<'a> EagerUnaryFunc<'a> for TimezoneTime {
150 type Input = NaiveTime;
151 type Output = NaiveTime;
152
153 fn call(&self, a: NaiveTime) -> NaiveTime {
154 timezone_time(self.tz, a, &self.wall_time)
155 }
156
157 fn output_type(&self, input: SqlColumnType) -> SqlColumnType {
158 SqlScalarType::Time.nullable(input.nullable)
159 }
160}
161
162impl fmt::Display for TimezoneTime {
163 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164 write!(f, "timezone_{}_t", self.tz)
165 }
166}