1use std::error::Error;
18use std::fmt::Display;
19use std::hash::Hash;
20use std::io::{Cursor, Write};
21use std::str::FromStr;
22
23use anyhow::Context;
24use byteorder::{BigEndian, ReadBytesExt};
25use bytes::BytesMut;
26use chrono::{
27 DateTime, Datelike, Days, Duration, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Weekday,
28};
29use postgres_types::{FromSql, IsNull, ToSql, Type, accepts, to_sql_checked};
30use risingwave_common_estimate_size::ZeroHeapSize;
31use thiserror::Error;
32
33use super::to_text::ToText;
34use super::{CheckedAdd, DataType, Interval};
35use crate::array::{ArrayError, ArrayResult};
36
37const UNIX_EPOCH_DAYS: i32 = 719_163;
40const LEAP_DAYS: &[i32] = &[0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
41const NORMAL_DAYS: &[i32] = &[0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
42
43macro_rules! impl_chrono_wrapper {
44 ($variant_name:ident, $chrono:ty, $pg_type:ident) => {
45 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
46 #[repr(transparent)]
47 pub struct $variant_name(pub $chrono);
48
49 impl $variant_name {
50 pub const MIN: Self = Self(<$chrono>::MIN);
51
52 pub fn new(data: $chrono) -> Self {
53 $variant_name(data)
54 }
55 }
56
57 impl Display for $variant_name {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 ToText::write(self, f)
60 }
61 }
62
63 impl From<$chrono> for $variant_name {
64 fn from(data: $chrono) -> Self {
65 $variant_name(data)
66 }
67 }
68
69 impl ZeroHeapSize for $variant_name {}
70
71 impl ToSql for $variant_name {
72 accepts!($pg_type);
73
74 to_sql_checked!();
75
76 fn to_sql(
77 &self,
78 ty: &Type,
79 out: &mut BytesMut,
80 ) -> std::result::Result<IsNull, Box<dyn Error + Sync + Send>>
81 where
82 Self: Sized,
83 {
84 self.0.to_sql(ty, out)
85 }
86 }
87
88 impl<'a> FromSql<'a> for $variant_name {
89 fn from_sql(
90 ty: &Type,
91 raw: &'a [u8],
92 ) -> std::result::Result<Self, Box<dyn std::error::Error + Sync + Send>> {
93 let instant = <$chrono>::from_sql(ty, raw)?;
94 Ok(Self::from(instant))
95 }
96
97 fn accepts(ty: &Type) -> bool {
98 matches!(*ty, Type::$pg_type)
99 }
100 }
101 };
102}
103
104impl_chrono_wrapper!(Date, NaiveDate, DATE);
105impl_chrono_wrapper!(Timestamp, NaiveDateTime, TIMESTAMP);
106impl_chrono_wrapper!(Time, NaiveTime, TIME);
107
108impl FromStr for Date {
119 type Err = InvalidParamsError;
120
121 fn from_str(s: &str) -> Result<Self> {
122 let date = speedate::Date::parse_str_rfc3339(s).map_err(|_| ErrorKind::ParseDate)?;
123 Ok(Date::new(
124 Date::from_ymd_uncheck(date.year as i32, date.month as u32, date.day as u32).0,
125 ))
126 }
127}
128
129impl FromStr for Time {
141 type Err = InvalidParamsError;
142
143 fn from_str(s: &str) -> Result<Self> {
144 let s_without_zone = s.trim_end_matches('Z');
145 let res = speedate::Time::parse_str(s_without_zone).map_err(|_| ErrorKind::ParseTime)?;
146 Ok(Time::from_hms_micro_uncheck(
147 res.hour as u32,
148 res.minute as u32,
149 res.second as u32,
150 res.microsecond,
151 ))
152 }
153}
154
155impl FromStr for Timestamp {
168 type Err = InvalidParamsError;
169
170 fn from_str(s: &str) -> Result<Self> {
171 let dt = s
172 .parse::<jiff::civil::DateTime>()
173 .map_err(|_| ErrorKind::ParseTimestamp)?;
174 Ok(
175 Date::from_ymd_uncheck(dt.year() as i32, dt.month() as u32, dt.day() as u32)
176 .and_hms_nano_uncheck(
177 dt.hour() as u32,
178 dt.minute() as u32,
179 dt.second() as u32,
180 dt.subsec_nanosecond() as u32,
181 ),
182 )
183 }
184}
185
186impl From<Timestamp> for Date {
199 fn from(ts: Timestamp) -> Self {
200 Date::new(ts.0.date())
201 }
202}
203
204impl From<Timestamp> for Time {
217 fn from(ts: Timestamp) -> Self {
218 Time::new(ts.0.time())
219 }
220}
221
222impl From<Interval> for Time {
239 fn from(interval: Interval) -> Self {
240 let usecs = interval.usecs_of_day();
241 let secs = (usecs / 1_000_000) as u32;
242 let nano = (usecs % 1_000_000 * 1000) as u32;
243 Time::from_num_seconds_from_midnight_uncheck(secs, nano)
244 }
245}
246
247#[derive(Copy, Clone, Debug, Error)]
248enum ErrorKind {
249 #[error("Invalid date: days: {days}")]
250 Date { days: i32 },
251 #[error("Invalid time: secs: {secs}, nanoseconds: {nsecs}")]
252 Time { secs: u32, nsecs: u32 },
253 #[error("Invalid time: {value} {unit} is out of range for a time of day")]
254 TimeOfDay { value: u64, unit: &'static str },
255 #[error("Invalid datetime: seconds: {secs}, nanoseconds: {nsecs}")]
256 DateTime { secs: i64, nsecs: u32 },
257 #[error("Invalid datetime: {value} {unit} is out of range")]
258 Timestamp { value: i64, unit: &'static str },
259 #[error("Can't cast string to date (expected format is YYYY-MM-DD)")]
260 ParseDate,
261 #[error(
262 "Can't cast string to time (expected format is HH:MM:SS[.D+{{up to 6 digits}}][Z] or HH:MM)"
263 )]
264 ParseTime,
265 #[error(
266 "Can't cast string to timestamp (expected format is YYYY-MM-DD HH:MM:SS[.D+{{up to 9 digits}}] or YYYY-MM-DD HH:MM or YYYY-MM-DD or ISO 8601 format)"
267 )]
268 ParseTimestamp,
269}
270
271#[derive(Debug, Error)]
272#[error(transparent)]
273pub struct InvalidParamsError(#[from] ErrorKind);
274
275impl InvalidParamsError {
276 pub fn date(days: i32) -> Self {
277 ErrorKind::Date { days }.into()
278 }
279
280 pub fn time(secs: u32, nsecs: u32) -> Self {
281 ErrorKind::Time { secs, nsecs }.into()
282 }
283
284 pub fn time_of_day(value: u64, unit: &'static str) -> Self {
285 ErrorKind::TimeOfDay { value, unit }.into()
286 }
287
288 pub fn datetime(secs: i64, nsecs: u32) -> Self {
289 ErrorKind::DateTime { secs, nsecs }.into()
290 }
291
292 pub fn timestamp(value: i64, unit: &'static str) -> Self {
293 ErrorKind::Timestamp { value, unit }.into()
294 }
295}
296
297impl From<InvalidParamsError> for ArrayError {
298 fn from(e: InvalidParamsError) -> Self {
299 ArrayError::internal(e)
300 }
301}
302
303type Result<T> = std::result::Result<T, InvalidParamsError>;
304
305impl ToText for Date {
306 fn write<W: std::fmt::Write>(&self, f: &mut W) -> std::fmt::Result {
318 let (ce, year) = self.0.year_ce();
319 let suffix = if ce { "" } else { " BC" };
320 write!(
321 f,
322 "{:04}-{:02}-{:02}{}",
323 year,
324 self.0.month(),
325 self.0.day(),
326 suffix
327 )
328 }
329
330 fn write_with_type<W: std::fmt::Write>(&self, ty: &DataType, f: &mut W) -> std::fmt::Result {
331 match ty {
332 super::DataType::Date => self.write(f),
333 _ => unreachable!(),
334 }
335 }
336}
337
338impl ToText for Time {
339 fn write<W: std::fmt::Write>(&self, f: &mut W) -> std::fmt::Result {
340 write!(f, "{}", self.0)
341 }
342
343 fn write_with_type<W: std::fmt::Write>(&self, ty: &DataType, f: &mut W) -> std::fmt::Result {
344 match ty {
345 super::DataType::Time => self.write(f),
346 _ => unreachable!(),
347 }
348 }
349}
350
351impl ToText for Timestamp {
352 fn write<W: std::fmt::Write>(&self, f: &mut W) -> std::fmt::Result {
353 let (ce, year) = self.0.year_ce();
354 let suffix = if ce { "" } else { " BC" };
355 write!(
356 f,
357 "{:04}-{:02}-{:02} {}{}",
358 year,
359 self.0.month(),
360 self.0.day(),
361 self.0.time(),
362 suffix
363 )
364 }
365
366 fn write_with_type<W: std::fmt::Write>(&self, ty: &DataType, f: &mut W) -> std::fmt::Result {
367 match ty {
368 super::DataType::Timestamp => self.write(f),
369 _ => unreachable!(),
370 }
371 }
372}
373
374impl Date {
375 pub fn with_days_since_ce(days: i32) -> Result<Self> {
376 Ok(Date::new(
377 NaiveDate::from_num_days_from_ce_opt(days)
378 .ok_or_else(|| InvalidParamsError::date(days))?,
379 ))
380 }
381
382 pub fn with_days_since_unix_epoch(days: i32) -> Result<Self> {
383 Ok(Date::new(
384 NaiveDate::from_num_days_from_ce_opt(days)
385 .ok_or_else(|| InvalidParamsError::date(days))?
386 .checked_add_days(Days::new(UNIX_EPOCH_DAYS as u64))
387 .ok_or_else(|| InvalidParamsError::date(days))?,
388 ))
389 }
390
391 pub fn get_nums_days_unix_epoch(&self) -> i32 {
392 self.0
393 .checked_sub_days(Days::new(UNIX_EPOCH_DAYS as u64))
394 .unwrap()
395 .num_days_from_ce()
396 }
397
398 pub fn from_protobuf(cur: &mut Cursor<&[u8]>) -> ArrayResult<Date> {
399 let days = cur
400 .read_i32::<BigEndian>()
401 .context("failed to read i32 from Date buffer")?;
402
403 Ok(Date::with_days_since_ce(days)?)
404 }
405
406 pub fn to_protobuf<T: Write>(self, output: &mut T) -> ArrayResult<usize> {
407 output
408 .write(&(self.0.num_days_from_ce()).to_be_bytes())
409 .map_err(Into::into)
410 }
411
412 pub fn from_ymd_uncheck(year: i32, month: u32, day: u32) -> Self {
413 Self::new(NaiveDate::from_ymd_opt(year, month, day).unwrap())
414 }
415
416 pub fn from_num_days_from_ce_uncheck(days: i32) -> Self {
417 Self::with_days_since_ce(days).unwrap()
418 }
419
420 pub fn and_hms_uncheck(self, hour: u32, min: u32, sec: u32) -> Timestamp {
421 self.and_hms_micro_uncheck(hour, min, sec, 0)
422 }
423
424 pub fn and_hms_micro_uncheck(self, hour: u32, min: u32, sec: u32, micro: u32) -> Timestamp {
425 Timestamp::new(
426 self.0
427 .and_time(Time::from_hms_micro_uncheck(hour, min, sec, micro).0),
428 )
429 }
430
431 pub fn and_hms_nano_uncheck(self, hour: u32, min: u32, sec: u32, nano: u32) -> Timestamp {
432 Timestamp::new(
433 self.0
434 .and_time(Time::from_hms_nano_uncheck(hour, min, sec, nano).0),
435 )
436 }
437}
438
439const NANOS_PER_DAY: u64 = 86_400 * 1_000_000_000;
441const MICROS_PER_DAY: u64 = 86_400 * 1_000_000;
442
443impl Time {
444 pub fn with_secs_nano(secs: u32, nano: u32) -> Result<Self> {
445 Ok(Time::new(
446 NaiveTime::from_num_seconds_from_midnight_opt(secs, nano)
447 .ok_or_else(|| InvalidParamsError::time(secs, nano))?,
448 ))
449 }
450
451 pub fn from_protobuf(cur: &mut Cursor<&[u8]>) -> ArrayResult<Time> {
452 let nano = cur
453 .read_u64::<BigEndian>()
454 .context("failed to read u64 from Time buffer")?;
455
456 Ok(Time::with_nano(nano)?)
457 }
458
459 pub fn to_protobuf<T: Write>(self, output: &mut T) -> ArrayResult<usize> {
460 output
461 .write(&self.nanos_of_day().to_be_bytes())
462 .map_err(Into::into)
463 }
464
465 pub fn with_nano(nano: u64) -> Result<Self> {
466 if nano >= NANOS_PER_DAY {
468 return Err(InvalidParamsError::time_of_day(nano, "nanoseconds"));
469 }
470 Self::with_secs_nano((nano / 1_000_000_000) as u32, (nano % 1_000_000_000) as u32)
471 }
472
473 pub fn with_micro(micro: u64) -> Result<Self> {
474 if micro >= MICROS_PER_DAY {
475 return Err(InvalidParamsError::time_of_day(micro, "microseconds"));
476 }
477 Self::with_secs_nano(
478 (micro / 1_000_000) as u32,
479 ((micro % 1_000_000) * 1_000) as u32,
480 )
481 }
482
483 pub fn nanos_of_day(self) -> u64 {
485 self.0.num_seconds_from_midnight() as u64 * 1_000_000_000 + self.0.nanosecond() as u64
486 }
487
488 pub fn micros_of_day(self) -> u64 {
490 self.0.num_seconds_from_midnight() as u64 * 1_000_000 + self.0.nanosecond() as u64 / 1_000
491 }
492
493 pub fn with_milli(milli: u32) -> Result<Self> {
494 let secs = milli / 1_000;
495 let nano = (milli % 1_000) * 1_000_000;
496 Self::with_secs_nano(secs, nano)
497 }
498
499 pub fn from_hms_uncheck(hour: u32, min: u32, sec: u32) -> Self {
500 Self::from_hms_nano_uncheck(hour, min, sec, 0)
501 }
502
503 pub fn from_hms_micro_uncheck(hour: u32, min: u32, sec: u32, micro: u32) -> Self {
504 Self::new(NaiveTime::from_hms_micro_opt(hour, min, sec, micro).unwrap())
505 }
506
507 pub fn from_hms_nano_uncheck(hour: u32, min: u32, sec: u32, nano: u32) -> Self {
508 Self::new(NaiveTime::from_hms_nano_opt(hour, min, sec, nano).unwrap())
509 }
510
511 pub fn from_num_seconds_from_midnight_uncheck(secs: u32, nano: u32) -> Self {
512 Self::new(NaiveTime::from_num_seconds_from_midnight_opt(secs, nano).unwrap())
513 }
514}
515
516enum FirstI64 {
523 V0 { usecs: i64 },
524 V1 { secs: i64 },
525}
526impl FirstI64 {
527 pub fn to_protobuf(&self) -> i64 {
528 match self {
529 FirstI64::V0 { usecs } => *usecs,
530 FirstI64::V1 { secs } => secs ^ (0b01 << 62),
531 }
532 }
533
534 pub fn from_protobuf(cur: &mut Cursor<&[u8]>) -> ArrayResult<FirstI64> {
535 let value = cur
536 .read_i64::<BigEndian>()
537 .context("failed to read i64 from Time buffer")?;
538 if Self::is_v1_format_state(value) {
539 let secs = value ^ (0b01 << 62);
540 Ok(FirstI64::V1 { secs })
541 } else {
542 Ok(FirstI64::V0 { usecs: value })
543 }
544 }
545
546 fn is_v1_format_state(value: i64) -> bool {
547 let state = (value >> 62) & 0b11;
548 state == 0b10 || state == 0b01
549 }
550}
551
552impl Timestamp {
553 pub fn with_secs_nsecs(secs: i64, nsecs: u32) -> Result<Self> {
554 DateTime::from_timestamp(secs, nsecs)
555 .map(|t| Timestamp(t.naive_utc()))
556 .ok_or_else(|| InvalidParamsError::datetime(secs, nsecs))
557 }
558
559 pub fn from_protobuf(cur: &mut Cursor<&[u8]>) -> ArrayResult<Timestamp> {
560 match FirstI64::from_protobuf(cur)? {
561 FirstI64::V0 { usecs } => Ok(Timestamp::with_micros(usecs)?),
562 FirstI64::V1 { secs } => {
563 let nsecs = cur
564 .read_u32::<BigEndian>()
565 .context("failed to read u32 from Time buffer")?;
566 Ok(Timestamp::with_secs_nsecs(secs, nsecs)?)
567 }
568 }
569 }
570
571 pub fn to_protobuf<T: Write>(self, output: &mut T) -> ArrayResult<usize> {
575 let timestamp_size = output
576 .write(
577 &(FirstI64::V1 {
578 secs: self.0.and_utc().timestamp(),
579 }
580 .to_protobuf())
581 .to_be_bytes(),
582 )
583 .map_err(Into::<ArrayError>::into)?;
584 let timestamp_subsec_nanos_size = output
585 .write(&(self.0.and_utc().timestamp_subsec_nanos()).to_be_bytes())
586 .map_err(Into::<ArrayError>::into)?;
587 Ok(timestamp_subsec_nanos_size + timestamp_size)
588 }
589
590 pub fn get_timestamp_nanos(&self) -> i64 {
591 self.0.and_utc().timestamp_nanos_opt().unwrap()
592 }
593
594 pub fn with_millis(timestamp_millis: i64) -> Result<Self> {
595 DateTime::from_timestamp_millis(timestamp_millis)
596 .map(|t| Timestamp(t.naive_utc()))
597 .ok_or_else(|| InvalidParamsError::timestamp(timestamp_millis, "milliseconds"))
598 }
599
600 pub fn with_micros(timestamp_micros: i64) -> Result<Self> {
601 DateTime::from_timestamp_micros(timestamp_micros)
602 .map(|t| Timestamp(t.naive_utc()))
603 .ok_or_else(|| InvalidParamsError::timestamp(timestamp_micros, "microseconds"))
604 }
605
606 pub fn with_nanos(timestamp_nanos: i64) -> Self {
608 Timestamp(DateTime::from_timestamp_nanos(timestamp_nanos).naive_utc())
609 }
610
611 pub fn from_timestamp_uncheck(secs: i64, nsecs: u32) -> Self {
612 Self::new(DateTime::from_timestamp(secs, nsecs).unwrap().naive_utc())
613 }
614
615 pub fn truncate_micros(self) -> Self {
627 Self::new(
628 self.0
629 .with_nanosecond(self.0.nanosecond() / 1000 * 1000)
630 .unwrap(),
631 )
632 }
633
634 pub fn truncate_millis(self) -> Self {
646 Self::new(
647 self.0
648 .with_nanosecond(self.0.nanosecond() / 1_000_000 * 1_000_000)
649 .unwrap(),
650 )
651 }
652
653 pub fn truncate_second(self) -> Self {
665 Self::new(self.0.with_nanosecond(0).unwrap())
666 }
667
668 pub fn truncate_minute(self) -> Self {
680 Date::new(self.0.date()).and_hms_uncheck(self.0.hour(), self.0.minute(), 0)
681 }
682
683 pub fn truncate_hour(self) -> Self {
695 Date::new(self.0.date()).and_hms_uncheck(self.0.hour(), 0, 0)
696 }
697
698 pub fn truncate_day(self) -> Self {
710 Date::new(self.0.date()).into()
711 }
712
713 pub fn truncate_week(self) -> Self {
725 Date::new(self.0.date().week(Weekday::Mon).first_day()).into()
726 }
727
728 pub fn truncate_month(self) -> Self {
740 Date::new(self.0.date().with_day(1).unwrap()).into()
741 }
742
743 pub fn truncate_quarter(self) -> Self {
755 Date::from_ymd_uncheck(self.0.year(), self.0.month0() / 3 * 3 + 1, 1).into()
756 }
757
758 pub fn truncate_year(self) -> Self {
770 Date::from_ymd_uncheck(self.0.year(), 1, 1).into()
771 }
772
773 pub fn truncate_decade(self) -> Self {
785 Date::from_ymd_uncheck(self.0.year() / 10 * 10, 1, 1).into()
786 }
787
788 pub fn truncate_century(self) -> Self {
800 Date::from_ymd_uncheck((self.0.year() - 1) / 100 * 100 + 1, 1, 1).into()
801 }
802
803 pub fn truncate_millennium(self) -> Self {
815 Date::from_ymd_uncheck((self.0.year() - 1) / 1000 * 1000 + 1, 1, 1).into()
816 }
817}
818
819impl From<Date> for Timestamp {
820 fn from(date: Date) -> Self {
821 date.and_hms_uncheck(0, 0, 0)
822 }
823}
824
825fn get_mouth_days(year: i32, month: usize) -> i32 {
827 if is_leap_year(year) {
828 LEAP_DAYS[month]
829 } else {
830 NORMAL_DAYS[month]
831 }
832}
833
834fn is_leap_year(year: i32) -> bool {
835 year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
836}
837
838impl CheckedAdd<Interval> for Timestamp {
839 type Output = Timestamp;
840
841 fn checked_add(self, rhs: Interval) -> Option<Timestamp> {
842 let mut date = self.0.date();
843 if rhs.months() != 0 {
844 let mut day = date.day() as i32;
846 let mut month = date.month() as i32;
847 let mut year = date.year();
848 let interval_months = rhs.months();
850 let year_diff = interval_months / 12;
851 year += year_diff;
852
853 let month_diff = interval_months - year_diff * 12;
857 month += month_diff;
859 if month > 12 {
861 year += 1;
862 month -= 12;
863 } else if month <= 0 {
864 year -= 1;
865 month += 12;
866 }
867
868 day = day.min(get_mouth_days(year, month as usize));
871 date = NaiveDate::from_ymd_opt(year, month as u32, day as u32)?;
872 }
873 let mut datetime = NaiveDateTime::new(date, self.0.time());
874 datetime = datetime.checked_add_signed(Duration::days(rhs.days().into()))?;
875 datetime = datetime.checked_add_signed(Duration::microseconds(rhs.usecs()))?;
876
877 Some(Timestamp::new(datetime))
878 }
879}
880
881#[cfg(test)]
882mod tests {
883 use super::*;
884
885 #[test]
886 fn parse() {
887 assert_eq!(
888 Timestamp::from_str("2022-08-03T10:34:02").unwrap(),
889 Timestamp::from_str("2022-08-03 10:34:02").unwrap()
890 );
891 let ts = Timestamp::from_str("0001-11-15 07:35:40.999999").unwrap();
892 assert_eq!(ts.0.and_utc().timestamp_micros(), -62108094259000001);
893
894 let ts = Timestamp::from_str("1969-12-31 23:59:59.999999").unwrap();
895 assert_eq!(ts.0.and_utc().timestamp_micros(), -1);
896
897 Date::from_str("1999-01-08AA").unwrap_err();
899 Time::from_str("AA04:05:06").unwrap_err();
900 Timestamp::from_str("1999-01-08 04:05:06AA").unwrap_err();
901 }
902
903 #[test]
904 fn time_of_day_bounds() {
905 assert_eq!(
906 Time::with_micro(86_399_999_999).unwrap(),
907 Time::from_hms_micro_uncheck(23, 59, 59, 999_999)
908 );
909 assert_eq!(
910 Time::with_nano(86_399_999_999_999).unwrap(),
911 Time::from_hms_nano_uncheck(23, 59, 59, 999_999_999)
912 );
913
914 Time::with_micro(86_400_000_000).unwrap_err();
915 Time::with_nano(86_400_000_000_000).unwrap_err();
916
917 Time::with_micro(4_294_967_296_000_000).unwrap_err();
919 Time::with_nano(4_294_967_296_000_000_000).unwrap_err();
920 }
921}