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