1use std::cmp::Ordering;
2use std::fmt::Display;
3use std::fmt::Formatter;
4use std::ops::Div;
5use std::ops::Mul;
6
7const INTERVAL_MAX_VALUE_I: i64 = 170_000_000;
8const INTERVAL_MAX_VALUE_F: f64 = 170_000_000.0;
9
10#[derive(Default, PartialEq, Clone)]
11pub struct Interval {
12 pub years: i64,
13 pub months: i64,
14 pub days: i64,
15 pub hours: i64,
16 pub minutes: i64,
17 pub seconds: f64,
18}
19
20impl Interval {
21 pub fn add(&self, other: &Interval) -> Result<Interval, String> {
22 let mut result = self.clone();
23 result.years = interval_value_or_error_i64(result.years + other.years)?;
24 result.months = interval_value_or_error_i64(result.months + other.months)?;
25 result.days = interval_value_or_error_i64(result.days + other.days)?;
26 result.hours = interval_value_or_error_i64(result.hours + other.hours)?;
27 result.minutes = interval_value_or_error_i64(result.minutes + other.minutes)?;
28 result.seconds = interval_value_or_error_f64(result.seconds + other.seconds)?;
29 Ok(result)
30 }
31
32 pub fn sub(&self, other: &Interval) -> Result<Interval, String> {
33 let mut result = self.clone();
34 result.years = interval_value_or_error_i64(result.years - other.years)?;
35 result.months = interval_value_or_error_i64(result.months - other.months)?;
36 result.days = interval_value_or_error_i64(result.days - other.days)?;
37 result.hours = interval_value_or_error_i64(result.hours - other.hours)?;
38 result.minutes = interval_value_or_error_i64(result.minutes - other.minutes)?;
39 result.seconds = interval_value_or_error_f64(result.seconds - other.seconds)?;
40 Ok(result)
41 }
42
43 pub fn mul(&self, other: i64) -> Result<Interval, String> {
44 let mut result = self.clone();
45 result.years = interval_value_or_error_i64(result.years * other)?;
46 result.months = interval_value_or_error_i64(result.months * other)?;
47 result.days = interval_value_or_error_i64(result.days * other)?;
48 result.hours = interval_value_or_error_i64(result.hours * other)?;
49 result.minutes = interval_value_or_error_i64(result.minutes * other)?;
50 result.seconds = interval_value_or_error_f64(result.seconds.mul(other as f64))?;
51 Ok(result)
52 }
53
54 pub fn div(&self, other: i64) -> Result<Interval, String> {
55 let mut result = self.clone();
56 result.years = interval_value_or_error_i64(result.years / other)?;
57 result.months = interval_value_or_error_i64(result.months / other)?;
58 result.days = interval_value_or_error_i64(result.days / other)?;
59 result.hours = interval_value_or_error_i64(result.hours / other)?;
60 result.minutes = interval_value_or_error_i64(result.minutes / other)?;
61 result.seconds = interval_value_or_error_f64(result.seconds.div(other as f64))?;
62 Ok(result)
63 }
64
65 pub fn to_seconds(&self) -> i64 {
66 let days =
67 self.years as f64 * 365.25 + self.months as f64 * (365.25 / 12.0) + self.days as f64;
68
69 let seconds = days * 24.0 * 60.0 * 60.0
70 + self.hours as f64 * 60.0 * 60.0
71 + self.minutes as f64 * 60.0
72 + self.seconds;
73
74 seconds as i64
75 }
76}
77
78impl PartialOrd for Interval {
79 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
80 let self_seconds = self.to_seconds();
81 let other_seconds = other.to_seconds();
82 self_seconds.partial_cmp(&other_seconds)
83 }
84}
85
86impl Display for Interval {
87 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
88 let mut parts = Vec::new();
89
90 if self.years != 0 {
91 parts.push(format!(
92 "{} year{}",
93 self.years,
94 if self.years > 1 { "s" } else { "" }
95 ));
96 }
97 if self.months != 0 {
98 parts.push(format!(
99 "{} month{}",
100 self.months,
101 if self.months > 1 { "s" } else { "" }
102 ));
103 }
104 if self.days != 0 {
105 parts.push(format!(
106 "{} day{}",
107 self.days,
108 if self.days > 1 { "s" } else { "" }
109 ));
110 }
111
112 let (hours, minutes, seconds) = (self.hours, self.minutes, self.seconds);
113 if hours != 0 || minutes != 0 || seconds != 0f64 {
114 parts.push(format!("{hours:02}:{minutes:02}:{seconds:02}"));
115 }
116
117 if parts.is_empty() {
118 write!(f, "0 seconds")?;
119 } else {
120 write!(f, "{}", parts.join(" "))?;
121 }
122 Ok(())
123 }
124}
125
126fn interval_value_or_error_i64(value: i64) -> Result<i64, String> {
127 if (-INTERVAL_MAX_VALUE_I..=INTERVAL_MAX_VALUE_I).contains(&value) {
128 return Ok(value);
129 }
130 Err(format!("Interval value out of range {value}"))
131}
132
133fn interval_value_or_error_f64(value: f64) -> Result<f64, String> {
134 if (-INTERVAL_MAX_VALUE_F..=INTERVAL_MAX_VALUE_F).contains(&value) {
135 return Ok(value);
136 }
137 Err("Interval value out of range".to_string())
138}