1use std::fmt;
11
12use chrono::{
13 DateTime, Duration, FixedOffset, NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike, Utc,
14};
15use mz_lowertest::MzReflect;
16use mz_ore::result::ResultExt;
17use mz_pgtz::timezone::Timezone;
18use mz_repr::adt::date::Date;
19use mz_repr::adt::datetime::DateTimeUnits;
20use mz_repr::adt::interval::Interval;
21use mz_repr::adt::numeric::{DecimalLike, Numeric};
22use mz_repr::adt::timestamp::{CheckedTimestamp, MAX_PRECISION, TimestampPrecision};
23use mz_repr::{ColumnType, ScalarType, strconv};
24use proptest_derive::Arbitrary;
25use serde::{Deserialize, Serialize};
26
27use crate::EvalError;
28use crate::scalar::func::format::DateTimeFormat;
29use crate::scalar::func::{EagerUnaryFunc, TimestampLike};
30
31sqlfunc!(
32 #[sqlname = "timestamp_to_text"]
33 #[preserves_uniqueness = true]
34 #[inverse = to_unary!(super::CastStringToTimestamp(None))]
35 fn cast_timestamp_to_string(a: CheckedTimestamp<NaiveDateTime>) -> String {
36 let mut buf = String::new();
37 strconv::format_timestamp(&mut buf, &a);
38 buf
39 }
40);
41
42sqlfunc!(
43 #[sqlname = "timestamp_with_time_zone_to_text"]
44 #[preserves_uniqueness = true]
45 #[inverse = to_unary!(super::CastStringToTimestampTz(None))]
46 fn cast_timestamp_tz_to_string(a: CheckedTimestamp<DateTime<Utc>>) -> String {
47 let mut buf = String::new();
48 strconv::format_timestamptz(&mut buf, &a);
49 buf
50 }
51);
52
53sqlfunc!(
54 #[sqlname = "timestamp_to_date"]
55 #[preserves_uniqueness = false]
56 #[inverse = to_unary!(super::CastDateToTimestamp(None))]
57 #[is_monotone = true]
58 fn cast_timestamp_to_date(a: CheckedTimestamp<NaiveDateTime>) -> Result<Date, EvalError> {
59 Ok(a.date().try_into()?)
60 }
61);
62
63sqlfunc!(
64 #[sqlname = "timestamp_with_time_zone_to_date"]
65 #[preserves_uniqueness = false]
66 #[inverse = to_unary!(super::CastDateToTimestampTz(None))]
67 #[is_monotone = true]
68 fn cast_timestamp_tz_to_date(a: CheckedTimestamp<DateTime<Utc>>) -> Result<Date, EvalError> {
69 Ok(a.naive_utc().date().try_into()?)
70 }
71);
72
73#[derive(
74 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
75)]
76pub struct CastTimestampToTimestampTz {
77 pub from: Option<TimestampPrecision>,
78 pub to: Option<TimestampPrecision>,
79}
80
81impl<'a> EagerUnaryFunc<'a> for CastTimestampToTimestampTz {
82 type Input = CheckedTimestamp<NaiveDateTime>;
83 type Output = Result<CheckedTimestamp<DateTime<Utc>>, EvalError>;
84
85 fn call(
86 &self,
87 a: CheckedTimestamp<NaiveDateTime>,
88 ) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
89 let out =
90 CheckedTimestamp::try_from(DateTime::<Utc>::from_naive_utc_and_offset(a.into(), Utc))?;
91 let updated = out.round_to_precision(self.to)?;
92 Ok(updated)
93 }
94
95 fn output_type(&self, input: ColumnType) -> ColumnType {
96 ScalarType::TimestampTz { precision: self.to }.nullable(input.nullable)
97 }
98
99 fn preserves_uniqueness(&self) -> bool {
100 let to_p = self.to.map(|p| p.into_u8()).unwrap_or(MAX_PRECISION);
101 let from_p = self.from.map(|p| p.into_u8()).unwrap_or(MAX_PRECISION);
102 to_p >= from_p
104 }
105
106 fn inverse(&self) -> Option<crate::UnaryFunc> {
107 to_unary!(super::CastTimestampTzToTimestamp {
108 from: self.from,
109 to: self.to
110 })
111 }
112
113 fn is_monotone(&self) -> bool {
114 true
115 }
116}
117
118impl fmt::Display for CastTimestampToTimestampTz {
119 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120 f.write_str("timestamp_to_timestamp_with_time_zone")
121 }
122}
123
124#[derive(
125 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
126)]
127pub struct AdjustTimestampPrecision {
128 pub from: Option<TimestampPrecision>,
129 pub to: Option<TimestampPrecision>,
130}
131
132impl<'a> EagerUnaryFunc<'a> for AdjustTimestampPrecision {
133 type Input = CheckedTimestamp<NaiveDateTime>;
134 type Output = Result<CheckedTimestamp<NaiveDateTime>, EvalError>;
135
136 fn call(
137 &self,
138 a: CheckedTimestamp<NaiveDateTime>,
139 ) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
140 mz_ore::soft_assert_no_log!(self.to != self.from);
143
144 let updated = a.round_to_precision(self.to)?;
145 Ok(updated)
146 }
147
148 fn output_type(&self, input: ColumnType) -> ColumnType {
149 ScalarType::Timestamp { precision: self.to }.nullable(input.nullable)
150 }
151
152 fn preserves_uniqueness(&self) -> bool {
153 let to_p = self.to.map(|p| p.into_u8()).unwrap_or(MAX_PRECISION);
154 let from_p = self.from.map(|p| p.into_u8()).unwrap_or(MAX_PRECISION);
155 to_p >= from_p
157 }
158
159 fn inverse(&self) -> Option<crate::UnaryFunc> {
160 None
161 }
162
163 fn is_monotone(&self) -> bool {
164 true
165 }
166}
167
168impl fmt::Display for AdjustTimestampPrecision {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 f.write_str("adjust_timestamp_precision")
171 }
172}
173
174#[derive(
175 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
176)]
177pub struct CastTimestampTzToTimestamp {
178 pub from: Option<TimestampPrecision>,
179 pub to: Option<TimestampPrecision>,
180}
181
182impl<'a> EagerUnaryFunc<'a> for CastTimestampTzToTimestamp {
183 type Input = CheckedTimestamp<DateTime<Utc>>;
184 type Output = Result<CheckedTimestamp<NaiveDateTime>, EvalError>;
185
186 fn call(
187 &self,
188 a: CheckedTimestamp<DateTime<Utc>>,
189 ) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
190 let out = CheckedTimestamp::try_from(a.naive_utc())?;
191 let updated = out.round_to_precision(self.to)?;
192 Ok(updated)
193 }
194
195 fn output_type(&self, input: ColumnType) -> ColumnType {
196 ScalarType::Timestamp { precision: self.to }.nullable(input.nullable)
197 }
198
199 fn preserves_uniqueness(&self) -> bool {
200 let to_p = self.to.map(|p| p.into_u8()).unwrap_or(MAX_PRECISION);
201 let from_p = self.from.map(|p| p.into_u8()).unwrap_or(MAX_PRECISION);
202 to_p >= from_p
204 }
205
206 fn inverse(&self) -> Option<crate::UnaryFunc> {
207 to_unary!(super::CastTimestampToTimestampTz {
208 from: self.from,
209 to: self.to
210 })
211 }
212
213 fn is_monotone(&self) -> bool {
214 true
215 }
216}
217
218impl fmt::Display for CastTimestampTzToTimestamp {
219 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
220 f.write_str("timestamp_with_time_zone_to_timestamp")
221 }
222}
223
224#[derive(
225 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
226)]
227pub struct AdjustTimestampTzPrecision {
228 pub from: Option<TimestampPrecision>,
229 pub to: Option<TimestampPrecision>,
230}
231
232impl<'a> EagerUnaryFunc<'a> for AdjustTimestampTzPrecision {
233 type Input = CheckedTimestamp<DateTime<Utc>>;
234 type Output = Result<CheckedTimestamp<DateTime<Utc>>, EvalError>;
235
236 fn call(
237 &self,
238 a: CheckedTimestamp<DateTime<Utc>>,
239 ) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
240 mz_ore::soft_assert_no_log!(self.to != self.from);
243
244 let updated = a.round_to_precision(self.to)?;
245 Ok(updated)
246 }
247
248 fn output_type(&self, input: ColumnType) -> ColumnType {
249 ScalarType::TimestampTz { precision: self.to }.nullable(input.nullable)
250 }
251
252 fn preserves_uniqueness(&self) -> bool {
253 let to_p = self.to.map(|p| p.into_u8()).unwrap_or(MAX_PRECISION);
254 let from_p = self.from.map(|p| p.into_u8()).unwrap_or(MAX_PRECISION);
255 to_p >= from_p
257 }
258
259 fn inverse(&self) -> Option<crate::UnaryFunc> {
260 None
261 }
262
263 fn is_monotone(&self) -> bool {
264 true
265 }
266}
267
268impl fmt::Display for AdjustTimestampTzPrecision {
269 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
270 f.write_str("adjust_timestamp_with_time_zone_precision")
271 }
272}
273
274sqlfunc!(
275 #[sqlname = "timestamp_to_time"]
276 #[preserves_uniqueness = false]
277 fn cast_timestamp_to_time(a: CheckedTimestamp<NaiveDateTime>) -> NaiveTime {
278 a.time()
279 }
280);
281
282sqlfunc!(
283 #[sqlname = "timestamp_with_time_zone_to_time"]
284 #[preserves_uniqueness = false]
285 fn cast_timestamp_tz_to_time(a: CheckedTimestamp<DateTime<Utc>>) -> NaiveTime {
286 a.naive_utc().time()
287 }
288);
289
290pub fn date_part_interval_inner<D>(units: DateTimeUnits, interval: Interval) -> Result<D, EvalError>
291where
292 D: DecimalLike,
293{
294 match units {
295 DateTimeUnits::Epoch => Ok(interval.as_epoch_seconds()),
296 DateTimeUnits::Millennium => Ok(D::from(interval.millennia())),
297 DateTimeUnits::Century => Ok(D::from(interval.centuries())),
298 DateTimeUnits::Decade => Ok(D::from(interval.decades())),
299 DateTimeUnits::Year => Ok(D::from(interval.years())),
300 DateTimeUnits::Quarter => Ok(D::from(interval.quarters())),
301 DateTimeUnits::Month => Ok(D::from(interval.months())),
302 DateTimeUnits::Day => Ok(D::lossy_from(interval.days())),
303 DateTimeUnits::Hour => Ok(D::lossy_from(interval.hours())),
304 DateTimeUnits::Minute => Ok(D::lossy_from(interval.minutes())),
305 DateTimeUnits::Second => Ok(interval.seconds()),
306 DateTimeUnits::Milliseconds => Ok(interval.milliseconds()),
307 DateTimeUnits::Microseconds => Ok(interval.microseconds()),
308 DateTimeUnits::Week
309 | DateTimeUnits::Timezone
310 | DateTimeUnits::TimezoneHour
311 | DateTimeUnits::TimezoneMinute
312 | DateTimeUnits::DayOfWeek
313 | DateTimeUnits::DayOfYear
314 | DateTimeUnits::IsoDayOfWeek
315 | DateTimeUnits::IsoDayOfYear => Err(EvalError::Unsupported {
316 feature: format!("'{}' timestamp units", units).into(),
317 discussion_no: None,
318 }),
319 }
320}
321
322#[derive(
323 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
324)]
325pub struct ExtractInterval(pub DateTimeUnits);
326
327impl<'a> EagerUnaryFunc<'a> for ExtractInterval {
328 type Input = Interval;
329 type Output = Result<Numeric, EvalError>;
330
331 fn call(&self, a: Interval) -> Result<Numeric, EvalError> {
332 date_part_interval_inner(self.0, a)
333 }
334
335 fn output_type(&self, input: ColumnType) -> ColumnType {
336 ScalarType::Numeric { max_scale: None }.nullable(input.nullable)
337 }
338}
339
340impl fmt::Display for ExtractInterval {
341 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
342 write!(f, "extract_{}_iv", self.0)
343 }
344}
345
346#[derive(
347 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
348)]
349pub struct DatePartInterval(pub DateTimeUnits);
350
351impl<'a> EagerUnaryFunc<'a> for DatePartInterval {
352 type Input = Interval;
353 type Output = Result<f64, EvalError>;
354
355 fn call(&self, a: Interval) -> Result<f64, EvalError> {
356 date_part_interval_inner(self.0, a)
357 }
358
359 fn output_type(&self, input: ColumnType) -> ColumnType {
360 ScalarType::Float64.nullable(input.nullable)
361 }
362}
363
364impl fmt::Display for DatePartInterval {
365 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
366 write!(f, "date_part_{}_iv", self.0)
367 }
368}
369
370pub fn date_part_timestamp_inner<T, D>(units: DateTimeUnits, ts: &T) -> Result<D, EvalError>
371where
372 T: TimestampLike,
373 D: DecimalLike,
374{
375 match units {
376 DateTimeUnits::Epoch => Ok(TimestampLike::extract_epoch(ts)),
377 DateTimeUnits::Millennium => Ok(D::from(ts.millennium())),
378 DateTimeUnits::Century => Ok(D::from(ts.century())),
379 DateTimeUnits::Decade => Ok(D::from(ts.decade())),
380 DateTimeUnits::Year => Ok(D::from(ts.year())),
381 DateTimeUnits::Quarter => Ok(D::from(ts.quarter())),
382 DateTimeUnits::Week => Ok(D::from(ts.iso_week_number())),
383 DateTimeUnits::Month => Ok(D::from(ts.month())),
384 DateTimeUnits::Day => Ok(D::from(ts.day())),
385 DateTimeUnits::DayOfWeek => Ok(D::from(ts.day_of_week())),
386 DateTimeUnits::DayOfYear => Ok(D::from(ts.ordinal())),
387 DateTimeUnits::IsoDayOfWeek => Ok(D::from(ts.iso_day_of_week())),
388 DateTimeUnits::Hour => Ok(D::from(ts.hour())),
389 DateTimeUnits::Minute => Ok(D::from(ts.minute())),
390 DateTimeUnits::Second => Ok(ts.extract_second()),
391 DateTimeUnits::Milliseconds => Ok(ts.extract_millisecond()),
392 DateTimeUnits::Microseconds => Ok(ts.extract_microsecond()),
393 DateTimeUnits::Timezone
394 | DateTimeUnits::TimezoneHour
395 | DateTimeUnits::TimezoneMinute
396 | DateTimeUnits::IsoDayOfYear => Err(EvalError::Unsupported {
397 feature: format!("'{}' timestamp units", units).into(),
398 discussion_no: None,
399 }),
400 }
401}
402
403pub(crate) fn most_significant_unit(unit: DateTimeUnits) -> bool {
406 match unit {
407 DateTimeUnits::Epoch
408 | DateTimeUnits::Millennium
409 | DateTimeUnits::Century
410 | DateTimeUnits::Decade
411 | DateTimeUnits::Year => true,
412 _ => false,
413 }
414}
415
416#[derive(
417 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
418)]
419pub struct ExtractTimestamp(pub DateTimeUnits);
420
421impl<'a> EagerUnaryFunc<'a> for ExtractTimestamp {
422 type Input = CheckedTimestamp<NaiveDateTime>;
423 type Output = Result<Numeric, EvalError>;
424
425 fn call(&self, a: CheckedTimestamp<NaiveDateTime>) -> Result<Numeric, EvalError> {
426 date_part_timestamp_inner(self.0, &*a)
427 }
428
429 fn output_type(&self, input: ColumnType) -> ColumnType {
430 ScalarType::Numeric { max_scale: None }.nullable(input.nullable)
431 }
432
433 fn is_monotone(&self) -> bool {
434 most_significant_unit(self.0)
435 }
436}
437
438impl fmt::Display for ExtractTimestamp {
439 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
440 write!(f, "extract_{}_ts", self.0)
441 }
442}
443
444#[derive(
445 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
446)]
447pub struct ExtractTimestampTz(pub DateTimeUnits);
448
449impl<'a> EagerUnaryFunc<'a> for ExtractTimestampTz {
450 type Input = CheckedTimestamp<DateTime<Utc>>;
451 type Output = Result<Numeric, EvalError>;
452
453 fn call(&self, a: CheckedTimestamp<DateTime<Utc>>) -> Result<Numeric, EvalError> {
454 date_part_timestamp_inner(self.0, &*a)
455 }
456
457 fn output_type(&self, input: ColumnType) -> ColumnType {
458 ScalarType::Numeric { max_scale: None }.nullable(input.nullable)
459 }
460
461 fn is_monotone(&self) -> bool {
462 self.0 == DateTimeUnits::Epoch
466 }
467}
468
469impl fmt::Display for ExtractTimestampTz {
470 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
471 write!(f, "extract_{}_tstz", self.0)
472 }
473}
474
475#[derive(
476 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
477)]
478pub struct DatePartTimestamp(pub DateTimeUnits);
479
480impl<'a> EagerUnaryFunc<'a> for DatePartTimestamp {
481 type Input = CheckedTimestamp<NaiveDateTime>;
482 type Output = Result<f64, EvalError>;
483
484 fn call(&self, a: CheckedTimestamp<NaiveDateTime>) -> Result<f64, EvalError> {
485 date_part_timestamp_inner(self.0, &*a)
486 }
487
488 fn output_type(&self, input: ColumnType) -> ColumnType {
489 ScalarType::Float64.nullable(input.nullable)
490 }
491}
492
493impl fmt::Display for DatePartTimestamp {
494 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
495 write!(f, "date_part_{}_ts", self.0)
496 }
497}
498
499#[derive(
500 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
501)]
502pub struct DatePartTimestampTz(pub DateTimeUnits);
503
504impl<'a> EagerUnaryFunc<'a> for DatePartTimestampTz {
505 type Input = CheckedTimestamp<DateTime<Utc>>;
506 type Output = Result<f64, EvalError>;
507
508 fn call(&self, a: CheckedTimestamp<DateTime<Utc>>) -> Result<f64, EvalError> {
509 date_part_timestamp_inner(self.0, &*a)
510 }
511
512 fn output_type(&self, input: ColumnType) -> ColumnType {
513 ScalarType::Float64.nullable(input.nullable)
514 }
515}
516
517impl fmt::Display for DatePartTimestampTz {
518 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
519 write!(f, "date_part_{}_tstz", self.0)
520 }
521}
522
523pub fn date_trunc_inner<T: TimestampLike>(units: DateTimeUnits, ts: &T) -> Result<T, EvalError> {
524 match units {
525 DateTimeUnits::Millennium => Ok(ts.truncate_millennium()),
526 DateTimeUnits::Century => Ok(ts.truncate_century()),
527 DateTimeUnits::Decade => Ok(ts.truncate_decade()),
528 DateTimeUnits::Year => Ok(ts.truncate_year()),
529 DateTimeUnits::Quarter => Ok(ts.truncate_quarter()),
530 DateTimeUnits::Week => Ok(ts.truncate_week()?),
531 DateTimeUnits::Day => Ok(ts.truncate_day()),
532 DateTimeUnits::Hour => Ok(ts.truncate_hour()),
533 DateTimeUnits::Minute => Ok(ts.truncate_minute()),
534 DateTimeUnits::Second => Ok(ts.truncate_second()),
535 DateTimeUnits::Month => Ok(ts.truncate_month()),
536 DateTimeUnits::Milliseconds => Ok(ts.truncate_milliseconds()),
537 DateTimeUnits::Microseconds => Ok(ts.truncate_microseconds()),
538 DateTimeUnits::Epoch
539 | DateTimeUnits::Timezone
540 | DateTimeUnits::TimezoneHour
541 | DateTimeUnits::TimezoneMinute
542 | DateTimeUnits::DayOfWeek
543 | DateTimeUnits::DayOfYear
544 | DateTimeUnits::IsoDayOfWeek
545 | DateTimeUnits::IsoDayOfYear => Err(EvalError::Unsupported {
546 feature: format!("'{}' timestamp units", units).into(),
547 discussion_no: None,
548 }),
549 }
550}
551
552#[derive(
553 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
554)]
555pub struct DateTruncTimestamp(pub DateTimeUnits);
556
557impl<'a> EagerUnaryFunc<'a> for DateTruncTimestamp {
558 type Input = CheckedTimestamp<NaiveDateTime>;
559 type Output = Result<CheckedTimestamp<NaiveDateTime>, EvalError>;
560
561 fn call(
562 &self,
563 a: CheckedTimestamp<NaiveDateTime>,
564 ) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
565 date_trunc_inner(self.0, &*a)?.try_into().err_into()
566 }
567
568 fn output_type(&self, input: ColumnType) -> ColumnType {
569 ScalarType::Timestamp { precision: None }.nullable(input.nullable)
570 }
571
572 fn is_monotone(&self) -> bool {
573 true
574 }
575}
576
577impl fmt::Display for DateTruncTimestamp {
578 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
579 write!(f, "date_trunc_{}_ts", self.0)
580 }
581}
582
583#[derive(
584 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
585)]
586pub struct DateTruncTimestampTz(pub DateTimeUnits);
587
588impl<'a> EagerUnaryFunc<'a> for DateTruncTimestampTz {
589 type Input = CheckedTimestamp<DateTime<Utc>>;
590 type Output = Result<CheckedTimestamp<DateTime<Utc>>, EvalError>;
591
592 fn call(
593 &self,
594 a: CheckedTimestamp<DateTime<Utc>>,
595 ) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
596 date_trunc_inner(self.0, &*a)?.try_into().err_into()
597 }
598
599 fn output_type(&self, input: ColumnType) -> ColumnType {
600 ScalarType::TimestampTz { precision: None }.nullable(input.nullable)
601 }
602
603 fn is_monotone(&self) -> bool {
604 true
605 }
606}
607
608impl fmt::Display for DateTruncTimestampTz {
609 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
610 write!(f, "date_trunc_{}_tstz", self.0)
611 }
612}
613
614pub fn timezone_timestamp(
622 tz: Timezone,
623 dt: NaiveDateTime,
624) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
625 let offset = match tz {
626 Timezone::FixedOffset(offset) => offset,
627 Timezone::Tz(tz) => match tz.offset_from_local_datetime(&dt).latest() {
628 Some(offset) => offset.fix(),
629 None => {
630 let dt = dt
631 .checked_add_signed(
632 Duration::try_hours(1).ok_or(EvalError::TimestampOutOfRange)?,
633 )
634 .ok_or(EvalError::TimestampOutOfRange)?;
635 tz.offset_from_local_datetime(&dt)
636 .latest()
637 .ok_or(EvalError::InvalidTimezoneConversion)?
638 .fix()
639 }
640 },
641 };
642 DateTime::from_naive_utc_and_offset(dt - offset, Utc)
643 .try_into()
644 .err_into()
645}
646
647pub fn timezone_timestamptz(tz: Timezone, utc: DateTime<Utc>) -> Result<NaiveDateTime, EvalError> {
650 let offset = match tz {
651 Timezone::FixedOffset(offset) => offset,
652 Timezone::Tz(tz) => tz.offset_from_utc_datetime(&utc.naive_utc()).fix(),
653 };
654 checked_add_with_leapsecond(&utc.naive_utc(), &offset).ok_or(EvalError::TimestampOutOfRange)
655}
656
657fn checked_add_with_leapsecond(lhs: &NaiveDateTime, rhs: &FixedOffset) -> Option<NaiveDateTime> {
659 let nanos = lhs.nanosecond();
661 let lhs = lhs.with_nanosecond(0).unwrap();
662 let rhs = rhs.local_minus_utc();
663 lhs.checked_add_signed(match chrono::Duration::try_seconds(i64::from(rhs)) {
664 Some(dur) => dur,
665 None => return None,
666 })
667 .map(|dt| dt.with_nanosecond(nanos).unwrap())
668}
669
670#[derive(
671 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
672)]
673pub struct TimezoneTimestamp(pub Timezone);
674
675impl<'a> EagerUnaryFunc<'a> for TimezoneTimestamp {
676 type Input = CheckedTimestamp<NaiveDateTime>;
677 type Output = Result<CheckedTimestamp<DateTime<Utc>>, EvalError>;
678
679 fn call(
680 &self,
681 a: CheckedTimestamp<NaiveDateTime>,
682 ) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
683 timezone_timestamp(self.0, a.to_naive())
684 }
685
686 fn output_type(&self, input: ColumnType) -> ColumnType {
687 ScalarType::TimestampTz { precision: None }.nullable(input.nullable)
688 }
689}
690
691impl fmt::Display for TimezoneTimestamp {
692 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
693 write!(f, "timezone_{}_ts", self.0)
694 }
695}
696
697#[derive(
698 Arbitrary, Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect,
699)]
700pub struct TimezoneTimestampTz(pub Timezone);
701
702impl<'a> EagerUnaryFunc<'a> for TimezoneTimestampTz {
703 type Input = CheckedTimestamp<DateTime<Utc>>;
704 type Output = Result<CheckedTimestamp<NaiveDateTime>, EvalError>;
705
706 fn call(
707 &self,
708 a: CheckedTimestamp<DateTime<Utc>>,
709 ) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
710 timezone_timestamptz(self.0, a.into())?
711 .try_into()
712 .err_into()
713 }
714
715 fn output_type(&self, input: ColumnType) -> ColumnType {
716 ScalarType::Timestamp { precision: None }.nullable(input.nullable)
717 }
718}
719
720impl fmt::Display for TimezoneTimestampTz {
721 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
722 write!(f, "timezone_{}_tstz", self.0)
723 }
724}
725
726#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, MzReflect)]
727pub struct ToCharTimestamp {
728 pub format_string: String,
729 pub format: DateTimeFormat,
730}
731
732impl<'a> EagerUnaryFunc<'a> for ToCharTimestamp {
733 type Input = CheckedTimestamp<NaiveDateTime>;
734 type Output = String;
735
736 fn call(&self, input: CheckedTimestamp<NaiveDateTime>) -> String {
737 self.format.render(&*input)
738 }
739
740 fn output_type(&self, input: ColumnType) -> ColumnType {
741 ScalarType::String.nullable(input.nullable)
742 }
743}
744
745impl fmt::Display for ToCharTimestamp {
746 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
747 write!(f, "tocharts[{}]", self.format_string)
748 }
749}
750
751#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, MzReflect)]
752pub struct ToCharTimestampTz {
753 pub format_string: String,
754 pub format: DateTimeFormat,
755}
756
757impl<'a> EagerUnaryFunc<'a> for ToCharTimestampTz {
758 type Input = CheckedTimestamp<DateTime<Utc>>;
759 type Output = String;
760
761 fn call(&self, input: CheckedTimestamp<DateTime<Utc>>) -> String {
762 self.format.render(&*input)
763 }
764
765 fn output_type(&self, input: ColumnType) -> ColumnType {
766 ScalarType::String.nullable(input.nullable)
767 }
768}
769
770impl fmt::Display for ToCharTimestampTz {
771 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
772 write!(f, "tochartstz[{}]", self.format_string)
773 }
774}