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