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::strconv;
20use serde::{Deserialize, Serialize};
21
22use crate::EvalError;
23
24#[sqlfunc(
26 sqlname = "time_to_text",
27 preserves_uniqueness = false,
28 inverse = 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(
39 sqlname = "time_to_interval",
40 preserves_uniqueness = false,
41 inverse = super::CastIntervalToTime
42)]
43fn cast_time_to_interval(t: NaiveTime) -> Interval {
44 let micros: i64 = Interval::convert_date_time_unit(
46 DateTimeField::Second,
47 DateTimeField::Microseconds,
48 i64::from(t.num_seconds_from_midnight()),
49 )
50 .unwrap()
51 + i64::from(t.nanosecond()) / i64::from(Interval::NANOSECOND_PER_MICROSECOND);
52
53 Interval::new(0, 0, micros)
54}
55
56pub fn date_part_time_inner<D>(units: DateTimeUnits, time: NaiveTime) -> Result<D, EvalError>
57where
58 D: DecimalLike,
59{
60 match units {
61 DateTimeUnits::Epoch => Ok(time.extract_epoch()),
62 DateTimeUnits::Hour => Ok(D::from(time.hour())),
63 DateTimeUnits::Minute => Ok(D::from(time.minute())),
64 DateTimeUnits::Second => Ok(time.extract_second()),
65 DateTimeUnits::Milliseconds => Ok(time.extract_millisecond()),
66 DateTimeUnits::Microseconds => Ok(time.extract_microsecond()),
67 DateTimeUnits::Millennium
68 | DateTimeUnits::Century
69 | DateTimeUnits::Decade
70 | DateTimeUnits::Year
71 | DateTimeUnits::Quarter
72 | DateTimeUnits::Month
73 | DateTimeUnits::Week
74 | DateTimeUnits::Day
75 | DateTimeUnits::DayOfYear
76 | DateTimeUnits::DayOfWeek
77 | DateTimeUnits::IsoDayOfYear
78 | DateTimeUnits::IsoDayOfWeek => Err(EvalError::UnsupportedUnits(
79 format!("{}", units).into(),
80 "time".into(),
81 )),
82 DateTimeUnits::Timezone | DateTimeUnits::TimezoneHour | DateTimeUnits::TimezoneMinute => {
83 Err(EvalError::Unsupported {
84 feature: format!("'{}' timestamp units", units).into(),
85 discussion_no: None,
86 })
87 }
88 }
89}
90
91#[derive(
92 Ord,
93 PartialOrd,
94 Clone,
95 Debug,
96 Eq,
97 PartialEq,
98 Serialize,
99 Deserialize,
100 Hash
101)]
102pub struct ExtractTime(pub DateTimeUnits);
103
104#[sqlfunc(ExtractTime, skip_display = true)]
105fn extract_time(&self, a: NaiveTime) -> Result<Numeric, EvalError> {
106 date_part_time_inner(self.0, a)
107}
108
109impl fmt::Display for ExtractTime {
110 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111 write!(f, "extract_{}_t", self.0)
112 }
113}
114
115#[derive(
116 Ord,
117 PartialOrd,
118 Clone,
119 Debug,
120 Eq,
121 PartialEq,
122 Serialize,
123 Deserialize,
124 Hash
125)]
126pub struct DatePartTime(pub DateTimeUnits);
127
128#[sqlfunc(DatePartTime, skip_display = true)]
129fn date_part_time(&self, a: NaiveTime) -> Result<f64, EvalError> {
130 date_part_time_inner(self.0, a)
131}
132
133impl fmt::Display for DatePartTime {
134 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135 write!(f, "date_part_{}_t", self.0)
136 }
137}
138
139pub fn timezone_time(tz: Timezone, t: NaiveTime, wall_time: &NaiveDateTime) -> NaiveTime {
142 let offset = match tz {
143 Timezone::FixedOffset(offset) => offset,
144 Timezone::Tz(tz) => tz.offset_from_utc_datetime(wall_time).fix(),
145 };
146 t + offset
147}
148
149#[derive(
150 Ord,
151 PartialOrd,
152 Clone,
153 Debug,
154 Eq,
155 PartialEq,
156 Serialize,
157 Deserialize,
158 Hash
159)]
160pub struct TimezoneTime {
161 pub tz: Timezone,
162 pub wall_time: NaiveDateTime,
163}
164
165#[sqlfunc(TimezoneTime, skip_display = true)]
166fn timezone_time(&self, a: NaiveTime) -> NaiveTime {
167 self::timezone_time(self.tz, a, &self.wall_time)
169}
170
171impl fmt::Display for TimezoneTime {
172 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
173 write!(f, "timezone_{}_t", self.tz)
174 }
175}