1use crate::{ProtocolError, ToProtocolValue};
4use bytes::{BufMut, BytesMut};
5use std::fmt::{Display, Formatter};
6
7#[derive(Debug, Clone, Default)]
8pub struct IntervalValue {
9 pub months: i32,
10 pub days: i32,
11 pub hours: i32,
12 pub mins: i32,
13 pub secs: i32,
14 pub usecs: i32,
15}
16
17impl IntervalValue {
18 pub fn new(months: i32, days: i32, hours: i32, mins: i32, secs: i32, usecs: i32) -> Self {
19 Self {
20 months,
21 days,
22 hours,
23 mins,
24 secs,
25 usecs,
26 }
27 }
28
29 pub fn is_zeroed(&self) -> bool {
30 self.months == 0
31 && self.days == 0
32 && self.hours == 0
33 && self.mins == 0
34 && self.secs == 0
35 && self.usecs == 0
36 }
37
38 pub fn extract_years_month(&self) -> (i32, i32) {
39 let years = self.months / 12;
40 let month = self.months % 12;
41
42 (years, month)
43 }
44
45 pub fn as_iso_str(&self) -> String {
46 if self.is_zeroed() {
47 return "00:00:00".to_owned();
48 }
49
50 let mut res = "".to_owned();
51 let (years, months) = self.extract_years_month();
52
53 if years != 0 {
54 if years == 1 {
55 res.push_str(&format!("{:#?} year ", years))
56 } else {
57 res.push_str(&format!("{:#?} years ", years))
58 }
59 }
60
61 if months != 0 {
62 if months == 1 {
63 res.push_str(&format!("{:#?} mon ", months));
64 } else {
65 res.push_str(&format!("{:#?} mons ", months));
66 }
67 }
68
69 if self.days != 0 {
70 if self.days == 1 {
71 res.push_str(&format!("{:#?} day ", self.days));
72 } else {
73 res.push_str(&format!("{:#?} days ", self.days));
74 }
75 }
76
77 if self.hours != 0 || self.mins != 0 || self.secs != 0 || self.usecs != 0 {
78 if self.hours < 0 || self.mins < 0 || self.secs < 0 || self.usecs < 0 {
79 res.push('-')
80 };
81
82 res.push_str(&format!(
83 "{:02}:{:02}:{:02}",
84 self.hours.abs(),
85 self.mins.abs(),
86 self.secs.abs()
87 ));
88
89 if self.usecs != 0 {
90 res.push_str(&format!(".{:06}", self.usecs.abs()))
91 }
92 }
93
94 res.trim().to_string()
95 }
96
97 pub fn as_postgresql_str(&self) -> String {
98 let (years, months) = self.extract_years_month();
99
100 format!(
104 "{} years {} mons {} days {} hours {} mins {}{}.{} secs",
105 years,
106 months,
107 self.days,
108 self.hours,
109 self.mins,
110 if self.secs < 0 || self.usecs < 0 {
111 "-"
112 } else {
113 ""
114 },
115 self.secs.abs(),
116 if self.usecs == 0 {
117 "00".to_string()
118 } else {
119 format!("{:06}", self.usecs.abs())
120 }
121 )
122 }
123}
124
125impl Display for IntervalValue {
126 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
127 f.write_str(&self.as_postgresql_str())
130 }
131}
132
133impl ToProtocolValue for IntervalValue {
134 fn to_text(&self, buf: &mut BytesMut) -> Result<(), ProtocolError> {
136 self.to_string().to_text(buf)
137 }
138
139 fn to_binary(&self, buf: &mut BytesMut) -> Result<(), ProtocolError> {
141 let usecs = self.hours as i64 * 60 * 60 * 1_000_000
142 + self.mins as i64 * 60 * 1_000_000
143 + self.secs as i64 * 1_000_000
144 + self.usecs as i64;
145
146 buf.put_i32(16);
147 buf.put_i64(usecs);
148 buf.put_i32(self.days);
149 buf.put_i32(self.months);
150
151 Ok(())
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::ProtocolError;
159
160 #[test]
161 fn test_interval_to_iso() -> Result<(), ProtocolError> {
162 assert_eq!(
163 IntervalValue::new(1, 0, 0, 0, 0, 0).as_iso_str(),
164 "1 mon".to_string()
165 );
166 assert_eq!(
167 IntervalValue::new(14, 0, 0, 0, 0, 0).as_iso_str(),
168 "1 year 2 mons".to_string()
169 );
170 assert_eq!(
171 IntervalValue::new(0, 1, 1, 1, 1, 1).as_iso_str(),
172 "1 day 01:01:01.000001".to_string()
173 );
174 assert_eq!(
175 IntervalValue::new(0, 0, -1, -1, -1, -1).as_iso_str(),
176 "-01:01:01.000001".to_string()
177 );
178 assert_eq!(
179 IntervalValue::new(0, 0, 0, 0, 0, 0).as_iso_str(),
180 "00:00:00".to_string()
181 );
182
183 Ok(())
184 }
185
186 #[test]
187 fn test_interval_to_postgres() -> Result<(), ProtocolError> {
188 assert_eq!(
189 IntervalValue::new(0, 0, 0, 0, 0, 0).to_string(),
190 "0 years 0 mons 0 days 0 hours 0 mins 0.00 secs".to_string()
191 );
192
193 assert_eq!(
194 IntervalValue::new(0, 0, 0, 0, 1, 23).to_string(),
195 "0 years 0 mons 0 days 0 hours 0 mins 1.000023 secs".to_string()
196 );
197
198 assert_eq!(
199 IntervalValue::new(0, 0, 0, 0, -1, -23).to_string(),
200 "0 years 0 mons 0 days 0 hours 0 mins -1.000023 secs".to_string()
201 );
202
203 assert_eq!(
204 IntervalValue::new(0, 0, 0, 0, -1, 0).to_string(),
205 "0 years 0 mons 0 days 0 hours 0 mins -1.00 secs".to_string()
206 );
207
208 assert_eq!(
209 IntervalValue::new(0, 0, -14, -5, -1, 0).to_string(),
210 "0 years 0 mons 0 days -14 hours -5 mins -1.00 secs".to_string()
211 );
212
213 Ok(())
214 }
215}