1use std::hash::Hash;
16
17use arrow::datatypes::IntervalUnit as ArrowIntervalUnit;
18use serde::{Deserialize, Serialize};
19
20#[derive(
21 Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
22)]
23pub enum IntervalUnit {
24 YearMonth,
26 DayTime,
29 #[default]
37 MonthDayNano,
38}
39
40impl From<&ArrowIntervalUnit> for IntervalUnit {
41 fn from(unit: &ArrowIntervalUnit) -> Self {
42 match unit {
43 ArrowIntervalUnit::YearMonth => IntervalUnit::YearMonth,
44 ArrowIntervalUnit::DayTime => IntervalUnit::DayTime,
45 ArrowIntervalUnit::MonthDayNano => IntervalUnit::MonthDayNano,
46 }
47 }
48}
49
50impl From<ArrowIntervalUnit> for IntervalUnit {
51 fn from(unit: ArrowIntervalUnit) -> Self {
52 (&unit).into()
53 }
54}
55
56#[derive(
58 Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
59)]
60#[repr(C)]
61pub struct IntervalYearMonth {
62 pub months: i32,
64}
65
66impl IntervalYearMonth {
67 pub fn new(months: i32) -> Self {
68 Self { months }
69 }
70
71 pub fn to_i32(&self) -> i32 {
72 self.months
73 }
74
75 pub fn from_i32(months: i32) -> Self {
76 Self { months }
77 }
78
79 pub fn negative(&self) -> Self {
80 Self::new(-self.months)
81 }
82
83 pub fn checked_negative(&self) -> Option<Self> {
84 self.months.checked_neg().map(Self::new)
85 }
86
87 pub fn to_iso8601_string(&self) -> String {
88 IntervalFormat::from(*self).to_iso8601_string()
89 }
90}
91
92impl From<IntervalYearMonth> for IntervalFormat {
93 fn from(interval: IntervalYearMonth) -> Self {
94 IntervalFormat {
95 years: interval.months / 12,
96 months: interval.months % 12,
97 ..Default::default()
98 }
99 }
100}
101
102impl From<i32> for IntervalYearMonth {
103 fn from(v: i32) -> Self {
104 Self::from_i32(v)
105 }
106}
107
108impl From<IntervalYearMonth> for i32 {
109 fn from(v: IntervalYearMonth) -> Self {
110 v.to_i32()
111 }
112}
113
114impl From<IntervalYearMonth> for serde_json::Value {
115 fn from(v: IntervalYearMonth) -> Self {
116 serde_json::Value::from(v.to_i32())
117 }
118}
119
120#[derive(
121 Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
122)]
123#[repr(C)]
124pub struct IntervalDayTime {
125 pub days: i32,
127 pub milliseconds: i32,
129}
130
131impl IntervalDayTime {
132 pub const ZERO: Self = Self::new(0, 0);
134
135 pub const MINUS_ONE: Self = Self::new(-1, -1);
137
138 pub const MAX: Self = Self::new(i32::MAX, i32::MAX);
140
141 pub const MIN: Self = Self::new(i32::MIN, i32::MIN);
143
144 pub const fn new(days: i32, milliseconds: i32) -> Self {
145 Self { days, milliseconds }
146 }
147
148 pub fn to_i64(&self) -> i64 {
149 let d = (self.days as u64 & u32::MAX as u64) << 32;
150 let m = self.milliseconds as u64 & u32::MAX as u64;
151 (d | m) as i64
152 }
153
154 pub fn from_i64(value: i64) -> Self {
155 let days = (value >> 32) as i32;
156 let milliseconds = value as i32;
157 Self { days, milliseconds }
158 }
159
160 pub fn negative(&self) -> Self {
161 Self::new(-self.days, -self.milliseconds)
162 }
163
164 pub fn checked_negative(&self) -> Option<Self> {
165 Some(Self::new(
166 self.days.checked_neg()?,
167 self.milliseconds.checked_neg()?,
168 ))
169 }
170
171 pub fn to_iso8601_string(&self) -> String {
172 IntervalFormat::from(*self).to_iso8601_string()
173 }
174
175 pub fn as_millis(&self) -> i64 {
176 self.days as i64 * MS_PER_DAY + self.milliseconds as i64
177 }
178}
179
180impl From<i64> for IntervalDayTime {
181 fn from(v: i64) -> Self {
182 Self::from_i64(v)
183 }
184}
185
186impl From<IntervalDayTime> for i64 {
187 fn from(v: IntervalDayTime) -> Self {
188 v.to_i64()
189 }
190}
191
192impl From<IntervalDayTime> for serde_json::Value {
193 fn from(v: IntervalDayTime) -> Self {
194 serde_json::Value::from(v.to_i64())
195 }
196}
197
198impl From<arrow::datatypes::IntervalDayTime> for IntervalDayTime {
199 fn from(value: arrow::datatypes::IntervalDayTime) -> Self {
200 Self {
201 days: value.days,
202 milliseconds: value.milliseconds,
203 }
204 }
205}
206
207impl From<IntervalDayTime> for arrow::datatypes::IntervalDayTime {
208 fn from(value: IntervalDayTime) -> Self {
209 Self {
210 days: value.days,
211 milliseconds: value.milliseconds,
212 }
213 }
214}
215
216pub const MS_PER_SEC: i64 = 1_000;
218pub const MS_PER_MINUTE: i64 = 60 * MS_PER_SEC;
219pub const MS_PER_HOUR: i64 = 60 * MS_PER_MINUTE;
220pub const MS_PER_DAY: i64 = 24 * MS_PER_HOUR;
221pub const NANOS_PER_MILLI: i64 = 1_000_000;
222
223impl From<IntervalDayTime> for IntervalFormat {
224 fn from(interval: IntervalDayTime) -> Self {
225 IntervalFormat {
226 days: interval.days,
227 hours: interval.milliseconds as i64 / MS_PER_HOUR,
228 minutes: (interval.milliseconds as i64 % MS_PER_HOUR) / MS_PER_MINUTE,
229 seconds: (interval.milliseconds as i64 % MS_PER_MINUTE) / MS_PER_SEC,
230 microseconds: (interval.milliseconds as i64 % MS_PER_SEC) * MS_PER_SEC,
231 ..Default::default()
232 }
233 }
234}
235
236#[derive(
237 Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
238)]
239#[repr(C)]
240pub struct IntervalMonthDayNano {
241 pub months: i32,
243 pub days: i32,
245 pub nanoseconds: i64,
247}
248
249impl IntervalMonthDayNano {
250 pub const ZERO: Self = Self::new(0, 0, 0);
252
253 pub const MINUS_ONE: Self = Self::new(-1, -1, -1);
255
256 pub const MAX: Self = Self::new(i32::MAX, i32::MAX, i64::MAX);
258
259 pub const MIN: Self = Self::new(i32::MIN, i32::MIN, i64::MIN);
261
262 pub const fn new(months: i32, days: i32, nanoseconds: i64) -> Self {
263 Self {
264 months,
265 days,
266 nanoseconds,
267 }
268 }
269
270 pub fn to_i128(&self) -> i128 {
271 let m = (self.months as u128 & u32::MAX as u128) << 96;
272 let d = (self.days as u128 & u32::MAX as u128) << 64;
273 let n = self.nanoseconds as u128 & u64::MAX as u128;
274 (m | d | n) as i128
275 }
276
277 pub fn from_i128(value: i128) -> Self {
278 let months = (value >> 96) as i32;
279 let days = (value >> 64) as i32;
280 let nanoseconds = value as i64;
281 Self {
282 months,
283 days,
284 nanoseconds,
285 }
286 }
287
288 pub fn negative(&self) -> Self {
289 Self::new(-self.months, -self.days, -self.nanoseconds)
290 }
291
292 pub fn checked_negative(&self) -> Option<Self> {
293 Some(Self::new(
294 self.months.checked_neg()?,
295 self.days.checked_neg()?,
296 self.nanoseconds.checked_neg()?,
297 ))
298 }
299
300 pub fn to_iso8601_string(&self) -> String {
301 IntervalFormat::from(*self).to_iso8601_string()
302 }
303}
304
305impl From<i128> for IntervalMonthDayNano {
306 fn from(v: i128) -> Self {
307 Self::from_i128(v)
308 }
309}
310
311impl From<IntervalMonthDayNano> for i128 {
312 fn from(v: IntervalMonthDayNano) -> Self {
313 v.to_i128()
314 }
315}
316
317impl From<IntervalMonthDayNano> for serde_json::Value {
318 fn from(v: IntervalMonthDayNano) -> Self {
319 serde_json::Value::from(v.to_i128().to_string())
320 }
321}
322
323impl From<arrow::datatypes::IntervalMonthDayNano> for IntervalMonthDayNano {
324 fn from(value: arrow::datatypes::IntervalMonthDayNano) -> Self {
325 Self {
326 months: value.months,
327 days: value.days,
328 nanoseconds: value.nanoseconds,
329 }
330 }
331}
332
333impl From<IntervalMonthDayNano> for arrow::datatypes::IntervalMonthDayNano {
334 fn from(value: IntervalMonthDayNano) -> Self {
335 Self {
336 months: value.months,
337 days: value.days,
338 nanoseconds: value.nanoseconds,
339 }
340 }
341}
342
343pub const NS_PER_SEC: i64 = 1_000_000_000;
345pub const NS_PER_MINUTE: i64 = 60 * NS_PER_SEC;
346pub const NS_PER_HOUR: i64 = 60 * NS_PER_MINUTE;
347pub const NS_PER_DAY: i64 = 24 * NS_PER_HOUR;
348
349impl From<IntervalMonthDayNano> for IntervalFormat {
350 fn from(interval: IntervalMonthDayNano) -> Self {
351 IntervalFormat {
352 years: interval.months / 12,
353 months: interval.months % 12,
354 days: interval.days,
355 hours: interval.nanoseconds / NS_PER_HOUR,
356 minutes: (interval.nanoseconds % NS_PER_HOUR) / NS_PER_MINUTE,
357 seconds: (interval.nanoseconds % NS_PER_MINUTE) / NS_PER_SEC,
358 microseconds: (interval.nanoseconds % NS_PER_SEC) / 1_000,
359 }
360 }
361}
362
363pub fn interval_year_month_to_month_day_nano(interval: IntervalYearMonth) -> IntervalMonthDayNano {
364 IntervalMonthDayNano {
365 months: interval.months,
366 days: 0,
367 nanoseconds: 0,
368 }
369}
370
371pub fn interval_day_time_to_month_day_nano(interval: IntervalDayTime) -> IntervalMonthDayNano {
372 IntervalMonthDayNano {
373 months: 0,
374 days: interval.days,
375 nanoseconds: interval.milliseconds as i64 * NANOS_PER_MILLI,
376 }
377}
378
379#[derive(Debug, Clone, Default, Copy, Serialize, Deserialize)]
382pub struct IntervalFormat {
383 pub years: i32,
384 pub months: i32,
385 pub days: i32,
386 pub hours: i64,
387 pub minutes: i64,
388 pub seconds: i64,
389 pub microseconds: i64,
390}
391
392impl IntervalFormat {
393 pub fn is_zero(&self) -> bool {
395 self.years == 0
396 && self.months == 0
397 && self.days == 0
398 && self.hours == 0
399 && self.minutes == 0
400 && self.seconds == 0
401 && self.microseconds == 0
402 }
403
404 pub fn has_year_month(&self) -> bool {
406 self.years != 0 || self.months != 0
407 }
408
409 pub fn has_day(&self) -> bool {
411 self.days != 0
412 }
413
414 pub fn has_time_part_positive(&self) -> bool {
416 self.hours > 0 || self.minutes > 0 || self.seconds > 0 || self.microseconds > 0
417 }
418
419 pub fn has_time_part(&self) -> bool {
421 self.hours != 0 || self.minutes != 0 || self.seconds != 0 || self.microseconds != 0
422 }
423
424 pub fn to_iso8601_string(&self) -> String {
428 if self.is_zero() {
429 return "PT0S".to_string();
430 }
431 let fract_str = match self.microseconds {
432 0 => String::default(),
433 _ => format!(".{:06}", self.microseconds)
434 .trim_end_matches('0')
435 .to_string(),
436 };
437 format!(
438 "P{}Y{}M{}DT{}H{}M{}{}S",
439 self.years, self.months, self.days, self.hours, self.minutes, self.seconds, fract_str
440 )
441 }
442
443 pub fn to_sql_standard_string(self) -> String {
447 if self.is_zero() {
448 "0".to_string()
449 } else if !self.has_time_part() && !self.has_day() {
450 get_year_month(self.months, self.years, true)
451 } else if !self.has_time_part() && !self.has_year_month() {
452 format!("{} 0:00:00", self.days)
453 } else if !self.has_year_month() && !self.has_day() {
454 get_time_part(
455 self.hours,
456 self.minutes,
457 self.seconds,
458 self.microseconds,
459 self.has_time_part_positive(),
460 true,
461 )
462 } else {
463 let year_month = get_year_month(self.months, self.years, false);
464 let time_interval = get_time_part(
465 self.hours,
466 self.minutes,
467 self.seconds,
468 self.microseconds,
469 self.has_time_part_positive(),
470 false,
471 );
472 format!("{} {:+} {}", year_month, self.days, time_interval)
473 }
474 }
475
476 pub fn to_postgres_string(&self) -> String {
480 if self.is_zero() {
481 return "00:00:00".to_string();
482 }
483 let mut result = String::default();
484 if self.has_year_month() {
485 if self.years != 0 {
486 result.push_str(&format!("{} year ", self.years));
487 }
488 if self.months != 0 {
489 result.push_str(&format!("{} mons ", self.months));
490 }
491 }
492 if self.has_day() {
493 result.push_str(&format!("{} days ", self.days));
494 }
495 result.push_str(&self.get_postgres_time_part());
496 result.trim().to_string()
497 }
498
499 fn get_postgres_time_part(&self) -> String {
501 let mut time_part = String::default();
502 if self.has_time_part() {
503 let sign = if !self.has_time_part_positive() {
504 "-"
505 } else {
506 ""
507 };
508 let hours = Self::padding_i64(self.hours);
509 time_part.push_str(&format!(
510 "{}{}:{}:{}",
511 sign,
512 hours,
513 Self::padding_i64(self.minutes),
514 Self::padding_i64(self.seconds),
515 ));
516 if self.microseconds != 0 {
517 time_part.push_str(&format!(".{:06}", self.microseconds.unsigned_abs()))
518 }
519 }
520 time_part
521 }
522
523 fn padding_i64(val: i64) -> String {
525 let num = if val < 0 {
526 val.unsigned_abs()
527 } else {
528 val as u64
529 };
530 format!("{:02}", num)
531 }
532}
533
534fn get_year_month(mons: i32, years: i32, is_only_year_month: bool) -> String {
536 let months = mons.unsigned_abs();
537 if years == 0 || is_only_year_month {
538 format!("{}-{}", years, months)
539 } else {
540 format!("{:+}-{}", years, months)
541 }
542}
543
544fn get_time_part(
546 hours: i64,
547 mins: i64,
548 secs: i64,
549 micros: i64,
550 is_time_part_positive: bool,
551 is_only_time: bool,
552) -> String {
553 let mut interval = String::default();
554 if is_time_part_positive && is_only_time {
555 interval.push_str(&format!("{}:{:02}:{:02}", hours, mins, secs));
556 } else {
557 let minutes = mins.unsigned_abs();
558 let seconds = secs.unsigned_abs();
559 interval.push_str(&format!("{:+}:{:02}:{:02}", hours, minutes, seconds));
560 }
561 if micros != 0 {
562 let microseconds = format!(".{:06}", micros.unsigned_abs());
563 interval.push_str(µseconds);
564 }
565 interval
566}
567
568#[cfg(test)]
569mod tests {
570 use super::*;
571
572 #[test]
573 fn test_from_year_month() {
574 let interval = IntervalYearMonth::new(1);
575 assert_eq!(interval.months, 1);
576 }
577
578 #[test]
579 fn test_from_date_time() {
580 let interval = IntervalDayTime::new(1, 2);
581 assert_eq!(interval.days, 1);
582 assert_eq!(interval.milliseconds, 2);
583 }
584
585 #[test]
586 fn test_from_month_day_nano() {
587 let interval = IntervalMonthDayNano::new(1, 2, 3);
588 assert_eq!(interval.months, 1);
589 assert_eq!(interval.days, 2);
590 assert_eq!(interval.nanoseconds, 3);
591 }
592
593 #[test]
594 fn test_interval_i128_convert() {
595 let test_interval_eq = |month, day, nano| {
596 let interval = IntervalMonthDayNano::new(month, day, nano);
597 let interval_i128 = interval.to_i128();
598 let interval2 = IntervalMonthDayNano::from_i128(interval_i128);
599 assert_eq!(interval, interval2);
600 };
601
602 test_interval_eq(1, 2, 3);
603 test_interval_eq(1, -2, 3);
604 test_interval_eq(1, -2, -3);
605 test_interval_eq(-1, -2, -3);
606 test_interval_eq(i32::MAX, i32::MAX, i64::MAX);
607 test_interval_eq(i32::MIN, i32::MAX, i64::MAX);
608 test_interval_eq(i32::MAX, i32::MIN, i64::MAX);
609 test_interval_eq(i32::MAX, i32::MAX, i64::MIN);
610 test_interval_eq(i32::MIN, i32::MIN, i64::MAX);
611 test_interval_eq(i32::MAX, i32::MIN, i64::MIN);
612 test_interval_eq(i32::MIN, i32::MAX, i64::MIN);
613 test_interval_eq(i32::MIN, i32::MIN, i64::MIN);
614
615 let interval = IntervalMonthDayNano::from_i128(1);
616 assert_eq!(interval, IntervalMonthDayNano::new(0, 0, 1));
617 assert_eq!(1, IntervalMonthDayNano::new(0, 0, 1).to_i128());
618 }
619
620 #[test]
621 fn test_interval_i64_convert() {
622 let interval = IntervalDayTime::from_i64(1);
623 assert_eq!(interval, IntervalDayTime::new(0, 1));
624 assert_eq!(1, IntervalDayTime::new(0, 1).to_i64());
625 }
626
627 #[test]
628 fn test_convert_interval_format() {
629 let interval = IntervalMonthDayNano {
630 months: 14,
631 days: 160,
632 nanoseconds: 1000000,
633 };
634 let interval_format = IntervalFormat::from(interval);
635 assert_eq!(interval_format.years, 1);
636 assert_eq!(interval_format.months, 2);
637 assert_eq!(interval_format.days, 160);
638 assert_eq!(interval_format.hours, 0);
639 assert_eq!(interval_format.minutes, 0);
640 assert_eq!(interval_format.seconds, 0);
641 assert_eq!(interval_format.microseconds, 1000);
642 }
643
644 #[test]
645 fn test_to_iso8601_string() {
646 let interval = IntervalMonthDayNano::new(0, 0, 0);
648 assert_eq!(interval.to_iso8601_string(), "PT0S");
649
650 let interval = IntervalMonthDayNano::new(1, 1, 1);
651 assert_eq!(interval.to_iso8601_string(), "P0Y1M1DT0H0M0S");
652
653 let interval = IntervalMonthDayNano::new(14, 31, 10000000000);
654 assert_eq!(interval.to_iso8601_string(), "P1Y2M31DT0H0M10S");
655
656 let interval = IntervalMonthDayNano::new(14, 31, 23210200000000);
657 assert_eq!(interval.to_iso8601_string(), "P1Y2M31DT6H26M50.2S");
658 }
659
660 #[test]
661 fn test_to_postgres_string() {
662 let interval = IntervalMonthDayNano::new(0, 0, 0);
664 assert_eq!(
665 IntervalFormat::from(interval).to_postgres_string(),
666 "00:00:00"
667 );
668
669 let interval = IntervalMonthDayNano::new(23, 100, 23210200000000);
670 assert_eq!(
671 IntervalFormat::from(interval).to_postgres_string(),
672 "1 year 11 mons 100 days 06:26:50.200000"
673 );
674 }
675
676 #[test]
677 fn test_to_sql_standard_string() {
678 let interval = IntervalMonthDayNano::new(0, 0, 0);
680 assert_eq!(IntervalFormat::from(interval).to_sql_standard_string(), "0");
681
682 let interval = IntervalMonthDayNano::new(23, 100, 23210200000000);
683 assert_eq!(
684 IntervalFormat::from(interval).to_sql_standard_string(),
685 "+1-11 +100 +6:26:50.200000"
686 );
687
688 let interval = IntervalMonthDayNano::new(0, 0, 23210200000000);
690 assert_eq!(
691 IntervalFormat::from(interval).to_sql_standard_string(),
692 "6:26:50.200000"
693 );
694 }
695
696 #[test]
697 fn test_from_arrow_interval_unit() {
698 let unit = ArrowIntervalUnit::YearMonth;
699 assert_eq!(IntervalUnit::from(unit), IntervalUnit::YearMonth);
700
701 let unit = ArrowIntervalUnit::DayTime;
702 assert_eq!(IntervalUnit::from(unit), IntervalUnit::DayTime);
703
704 let unit = ArrowIntervalUnit::MonthDayNano;
705 assert_eq!(IntervalUnit::from(unit), IntervalUnit::MonthDayNano);
706 }
707}