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