1mod error;
2mod primitive;
3mod string;
4
5pub use error::IntervalError;
6use {
7 super::Value,
8 crate::{ast::DateTimeField, result::Result},
9 chrono::{Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Timelike},
10 core::str::FromStr,
11 rust_decimal::{prelude::ToPrimitive, Decimal},
12 serde::{Deserialize, Serialize},
13 std::{cmp::Ordering, fmt::Debug},
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
17pub enum Interval {
18 Month(i32),
19 Microsecond(i64),
20}
21
22impl Ord for Interval {
23 fn cmp(&self, other: &Self) -> Ordering {
24 match (self, other) {
25 (Interval::Month(l), Interval::Month(r)) => l.cmp(r),
26 (Interval::Microsecond(l), Interval::Microsecond(r)) => l.cmp(r),
27 (Interval::Month(_), Interval::Microsecond(_)) => Ordering::Greater,
28 (Interval::Microsecond(_), Interval::Month(_)) => Ordering::Less,
29 }
30 }
31}
32
33impl PartialOrd<Interval> for Interval {
34 fn partial_cmp(&self, other: &Interval) -> Option<Ordering> {
35 Some(self.cmp(other))
36 }
37}
38
39const SECOND: i64 = 1_000_000;
40const MINUTE: i64 = 60 * SECOND;
41const HOUR: i64 = 3600 * SECOND;
42const DAY: i64 = 24 * HOUR;
43
44impl Interval {
45 pub fn unary_minus(&self) -> Self {
46 match self {
47 Interval::Month(v) => Interval::Month(-v),
48 Interval::Microsecond(v) => Interval::Microsecond(-v),
49 }
50 }
51
52 pub fn add(&self, other: &Interval) -> Result<Self> {
53 use Interval::*;
54
55 match (self, other) {
56 (Month(l), Month(r)) => Ok(Month(l + r)),
57 (Microsecond(l), Microsecond(r)) => Ok(Microsecond(l + r)),
58 _ => Err(IntervalError::AddBetweenYearToMonthAndHourToSecond.into()),
59 }
60 }
61
62 pub fn subtract(&self, other: &Interval) -> Result<Self> {
63 use Interval::*;
64
65 match (self, other) {
66 (Month(l), Month(r)) => Ok(Month(l - r)),
67 (Microsecond(l), Microsecond(r)) => Ok(Microsecond(l - r)),
68 _ => Err(IntervalError::SubtractBetweenYearToMonthAndHourToSecond.into()),
69 }
70 }
71
72 pub fn add_date(&self, date: &NaiveDate) -> Result<NaiveDateTime> {
73 self.add_timestamp(
74 &date
75 .and_hms_opt(0, 0, 0)
76 .ok_or_else(|| IntervalError::FailedToParseTime(date.to_string()))?,
77 )
78 }
79
80 pub fn subtract_from_date(&self, date: &NaiveDate) -> Result<NaiveDateTime> {
81 self.subtract_from_timestamp(
82 &date
83 .and_hms_opt(0, 0, 0)
84 .ok_or_else(|| IntervalError::FailedToParseTime(date.to_string()))?,
85 )
86 }
87
88 pub fn add_timestamp(&self, timestamp: &NaiveDateTime) -> Result<NaiveDateTime> {
89 match self {
90 Interval::Month(n) => {
91 let month = timestamp.month() as i32 + n;
92
93 let year = timestamp.year() + month / 12;
94 let month = month % 12;
95
96 timestamp
97 .with_year(year)
98 .and_then(|d| d.with_month(month as u32))
99 .ok_or_else(|| IntervalError::DateOverflow { year, month }.into())
100 }
101 Interval::Microsecond(n) => Ok(*timestamp + Duration::microseconds(*n)),
102 }
103 }
104
105 pub fn subtract_from_timestamp(&self, timestamp: &NaiveDateTime) -> Result<NaiveDateTime> {
106 match self {
107 Interval::Month(n) => {
108 let months = timestamp.year() * 12 + timestamp.month() as i32 - n;
109
110 let year = months / 12;
111 let month = months % 12;
112
113 timestamp
114 .with_year(year)
115 .and_then(|d| d.with_month(month as u32))
116 .ok_or_else(|| IntervalError::DateOverflow { year, month }.into())
117 }
118 Interval::Microsecond(n) => Ok(*timestamp - Duration::microseconds(*n)),
119 }
120 }
121
122 pub fn add_time(&self, time: &NaiveTime) -> Result<NaiveTime> {
123 match self {
124 Interval::Month(_) => Err(IntervalError::AddYearOrMonthToTime {
125 time: *time,
126 interval: *self,
127 }
128 .into()),
129 Interval::Microsecond(n) => Ok(*time + Duration::microseconds(*n)),
130 }
131 }
132
133 pub fn subtract_from_time(&self, time: &NaiveTime) -> Result<NaiveTime> {
134 match self {
135 Interval::Month(_) => Err(IntervalError::SubtractYearOrMonthToTime {
136 time: *time,
137 interval: *self,
138 }
139 .into()),
140 Interval::Microsecond(n) => Ok(*time - Duration::microseconds(*n)),
141 }
142 }
143
144 pub fn years(years: i32) -> Self {
145 Interval::Month(12 * years)
146 }
147
148 pub fn months(months: i32) -> Self {
149 Interval::Month(months)
150 }
151
152 pub fn extract(&self, field: &DateTimeField) -> Result<Value> {
153 let value = match (field, *self) {
154 (DateTimeField::Year, Interval::Month(i)) => i as i64 / 12,
155 (DateTimeField::Month, Interval::Month(i)) => i as i64,
156 (DateTimeField::Day, Interval::Microsecond(i)) => i / DAY,
157 (DateTimeField::Hour, Interval::Microsecond(i)) => i / HOUR,
158 (DateTimeField::Minute, Interval::Microsecond(i)) => i / MINUTE,
159 (DateTimeField::Second, Interval::Microsecond(i)) => i / SECOND,
160 _ => {
161 return Err(IntervalError::FailedToExtract.into());
162 }
163 };
164
165 Ok(Value::I64(value))
166 }
167
168 pub fn days(days: i32) -> Self {
169 Interval::Microsecond(days as i64 * DAY)
170 }
171
172 pub fn hours(hours: i32) -> Self {
173 Interval::Microsecond(hours as i64 * HOUR)
174 }
175
176 pub fn minutes(minutes: i32) -> Self {
177 Interval::Microsecond(minutes as i64 * MINUTE)
178 }
179
180 pub fn seconds(seconds: i64) -> Self {
181 Interval::Microsecond(seconds * SECOND)
182 }
183
184 pub fn milliseconds(milliseconds: i64) -> Self {
185 Interval::Microsecond(milliseconds * 1_000)
186 }
187
188 pub fn microseconds(microseconds: i64) -> Self {
189 Interval::Microsecond(microseconds)
190 }
191
192 pub fn try_from_str(
193 value: &str,
194 leading_field: Option<DateTimeField>,
195 last_field: Option<DateTimeField>,
196 ) -> Result<Self> {
197 use DateTimeField::*;
198
199 let value = value.trim_matches('\'');
200
201 let sign = if value.get(0..1) == Some("-") { -1 } else { 1 };
202
203 let parse_integer = |v: &str| {
204 v.parse::<i32>()
205 .map_err(|_| IntervalError::FailedToParseInteger(value.to_owned()).into())
206 };
207
208 let parse_decimal = |duration: i64| {
209 let parsed = Decimal::from_str(value)
210 .map_err(|_| IntervalError::FailedToParseDecimal(value.to_owned()))?;
211
212 (parsed * Decimal::from(duration))
213 .to_i64()
214 .ok_or_else(|| IntervalError::FailedToParseDecimal(value.to_owned()).into())
215 .map(Interval::Microsecond)
216 };
217
218 let parse_time = |v: &str| {
219 let sign = if v.get(0..1) == Some("-") { -1 } else { 1 };
220 let v = v.trim_start_matches('-');
221 let time = NaiveTime::from_str(v)
222 .map_err(|_| IntervalError::FailedToParseTime(value.to_owned()))?;
223
224 let msec = time.hour() as i64 * HOUR
225 + time.minute() as i64 * MINUTE
226 + time.second() as i64 * SECOND
227 + time.nanosecond() as i64 / 1000;
228
229 Ok(Interval::Microsecond(sign as i64 * msec))
230 };
231
232 match (leading_field, last_field) {
233 (Some(Year), None) => parse_integer(value).map(Interval::years),
234 (Some(Month), None) => parse_integer(value).map(Interval::months),
235 (Some(Day), None) => parse_decimal(DAY),
236 (Some(Hour), None) => parse_decimal(HOUR),
237 (Some(Minute), None) => parse_decimal(MINUTE),
238 (Some(Second), None) => parse_decimal(SECOND),
239 (Some(Year), Some(Month)) => {
240 let nums = value
241 .trim_start_matches('-')
242 .split('-')
243 .map(parse_integer)
244 .collect::<Result<Vec<_>>>()?;
245
246 match (nums.first(), nums.get(1)) {
247 (Some(years), Some(months)) => {
248 Ok(Interval::months(sign * (12 * years + months)))
249 }
250 _ => Err(IntervalError::FailedToParseYearToMonth(value.to_owned()).into()),
251 }
252 }
253 (Some(Day), Some(Hour)) => {
254 let nums = value
255 .trim_start_matches('-')
256 .split(' ')
257 .map(parse_integer)
258 .collect::<Result<Vec<_>>>()?;
259
260 match (nums.first(), nums.get(1)) {
261 (Some(days), Some(hours)) => Ok(Interval::hours(sign * (24 * days + hours))),
262 _ => Err(IntervalError::FailedToParseDayToHour(value.to_owned()).into()),
263 }
264 }
265 (Some(Day), Some(Minute)) => {
266 let nums = value.trim_start_matches('-').split(' ').collect::<Vec<_>>();
267
268 match (nums.first(), nums.get(1)) {
269 (Some(days), Some(time)) => {
270 let days = parse_integer(days)?;
271 let time = format!("{}:00", time);
272
273 Interval::days(days)
274 .add(&parse_time(&time)?)
275 .map(|interval| sign * interval)
276 }
277 _ => Err(IntervalError::FailedToParseDayToMinute(value.to_owned()).into()),
278 }
279 }
280 (Some(Day), Some(Second)) => {
281 let nums = value.trim_start_matches('-').split(' ').collect::<Vec<_>>();
282
283 match (nums.first(), nums.get(1)) {
284 (Some(days), Some(time)) => {
285 let days = parse_integer(days)?;
286
287 Interval::days(days)
288 .add(&parse_time(time)?)
289 .map(|interval| sign * interval)
290 }
291 _ => Err(IntervalError::FailedToParseDayToSecond(value.to_owned()).into()),
292 }
293 }
294 (Some(Hour), Some(Minute)) => parse_time(&format!("{}:00", value)),
295 (Some(Hour), Some(Second)) => parse_time(value),
296 (Some(Minute), Some(Second)) => {
297 let time = value.trim_start_matches('-');
298
299 parse_time(&format!("00:{}", time)).map(|v| sign * v)
300 }
301 (Some(from), Some(to)) => Err(IntervalError::UnsupportedRange(
302 format!("{:?}", from),
303 format!("{:?}", to),
304 )
305 .into()),
306 (None, _) => Err(IntervalError::Unreachable.into()),
307 }
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use {
314 super::{Interval, IntervalError},
315 crate::ast::DateTimeField,
316 chrono::{NaiveDate, NaiveTime},
317 };
318
319 #[test]
320 fn cmp() {
321 assert!(Interval::Month(12) > Interval::Month(1));
322 assert!(Interval::Microsecond(300) > Interval::Microsecond(1));
323 assert!(Interval::Month(1) > Interval::Microsecond(1000));
324 }
325
326 fn date(year: i32, month: u32, day: u32) -> NaiveDate {
327 NaiveDate::from_ymd_opt(year, month, day).unwrap()
328 }
329
330 fn time(hour: u32, min: u32, sec: u32) -> NaiveTime {
331 NaiveTime::from_hms_opt(hour, min, sec).unwrap()
332 }
333
334 #[test]
335 fn arithmetic() {
336 use Interval::*;
337
338 macro_rules! test {
339 ($op: ident $a: expr, $b: expr => $c: expr) => {
340 assert_eq!($a.$op(&$b), Ok($c));
341 };
342 }
343
344 assert_eq!(Month(1).unary_minus(), Month(-1));
345 assert_eq!(Microsecond(1).unary_minus(), Microsecond(-1));
346
347 assert_eq!(
349 Month(2).add_date(&date(2021, 11, 11)),
350 Ok(date(2022, 1, 11).and_hms_opt(0, 0, 0).unwrap())
351 );
352 assert_eq!(
353 Interval::hours(30).add_date(&date(2021, 11, 11)),
354 Ok(date(2021, 11, 12).and_hms_opt(6, 0, 0).unwrap())
355 );
356 assert_eq!(
357 Interval::years(999_999).add_date(&date(2021, 11, 11)),
358 Err(IntervalError::DateOverflow {
359 year: 1_002_020,
360 month: 11,
361 }
362 .into())
363 );
364 assert_eq!(
365 Month(2).subtract_from_date(&date(2021, 11, 11)),
366 Ok(date(2021, 9, 11).and_hms_opt(0, 0, 0).unwrap())
367 );
368 assert_eq!(
369 Month(14).subtract_from_date(&date(2021, 11, 11)),
370 Ok(date(2020, 9, 11).and_hms_opt(0, 0, 0).unwrap())
371 );
372 assert_eq!(
373 Interval::hours(30).subtract_from_date(&date(2021, 11, 11)),
374 Ok(date(2021, 11, 9).and_hms_opt(18, 0, 0).unwrap())
375 );
376 assert_eq!(
377 Interval::years(999_999).subtract_from_date(&date(2021, 11, 11)),
378 Err(IntervalError::DateOverflow {
379 year: -997977,
380 month: -1,
381 }
382 .into())
383 );
384
385 assert_eq!(
387 Interval::minutes(2).add_timestamp(&date(2021, 11, 11).and_hms_opt(12, 3, 1).unwrap()),
388 Ok(date(2021, 11, 11).and_hms_opt(12, 5, 1).unwrap())
389 );
390 assert_eq!(
391 Interval::hours(30).add_timestamp(&date(2021, 11, 11).and_hms_opt(0, 30, 0).unwrap()),
392 Ok(date(2021, 11, 12).and_hms_opt(6, 30, 0).unwrap())
393 );
394 assert_eq!(
395 Interval::years(999_999)
396 .add_timestamp(&date(2021, 11, 11).and_hms_opt(1, 1, 1).unwrap()),
397 Err(IntervalError::DateOverflow {
398 year: 1_002_020,
399 month: 11,
400 }
401 .into())
402 );
403 assert_eq!(
404 Month(2).subtract_from_timestamp(&date(2021, 11, 11).and_hms_opt(1, 3, 59).unwrap()),
405 Ok(date(2021, 9, 11).and_hms_opt(1, 3, 59).unwrap())
406 );
407 assert_eq!(
408 Month(14).subtract_from_timestamp(&date(2021, 11, 11).and_hms_opt(23, 1, 1).unwrap()),
409 Ok(date(2020, 9, 11).and_hms_opt(23, 1, 1).unwrap())
410 );
411 assert_eq!(
412 Interval::seconds(30)
413 .subtract_from_timestamp(&date(2021, 11, 11).and_hms_opt(0, 0, 0).unwrap()),
414 Ok(date(2021, 11, 10).and_hms_opt(23, 59, 30).unwrap())
415 );
416 assert_eq!(
417 Interval::years(999_999)
418 .subtract_from_timestamp(&date(2021, 11, 11).and_hms_opt(0, 0, 0).unwrap()),
419 Err(IntervalError::DateOverflow {
420 year: -997977,
421 month: -1,
422 }
423 .into())
424 );
425
426 assert_eq!(
428 Interval::minutes(30).add_time(&time(23, 0, 1)),
429 Ok(time(23, 30, 1))
430 );
431 assert_eq!(
432 Interval::hours(20).add_time(&time(5, 30, 0)),
433 Ok(time(1, 30, 0))
434 );
435 assert_eq!(
436 Interval::years(1).add_time(&time(23, 0, 1)),
437 Err(IntervalError::AddYearOrMonthToTime {
438 time: time(23, 0, 1),
439 interval: Interval::years(1),
440 }
441 .into())
442 );
443 assert_eq!(
444 Interval::minutes(30).subtract_from_time(&time(23, 0, 1)),
445 Ok(time(22, 30, 1))
446 );
447 assert_eq!(
448 Interval::hours(20).subtract_from_time(&time(5, 30, 0)),
449 Ok(time(9, 30, 0))
450 );
451 assert_eq!(
452 Interval::months(3).subtract_from_time(&time(23, 0, 1)),
453 Err(IntervalError::SubtractYearOrMonthToTime {
454 time: time(23, 0, 1),
455 interval: Interval::months(3),
456 }
457 .into())
458 );
459
460 test!(add Month(1), Month(2) => Month(3));
461 test!(subtract Month(1), Month(2) => Month(-1));
462
463 test!(add Microsecond(1), Microsecond(2) => Microsecond(3));
464 test!(subtract Microsecond(1), Microsecond(2) => Microsecond(-1));
465 }
466
467 #[test]
468 fn try_from_literal() {
469 macro_rules! test {
470 ($value: expr, $datetime: ident => $expected_value: expr, $duration: ident) => {
471 let interval = Interval::try_from_str($value, Some(DateTimeField::$datetime), None);
472
473 assert_eq!(interval, Ok(Interval::$duration($expected_value)));
474 };
475 ($value: expr, $from: ident to $to: ident => $expected_value: expr, $duration: ident) => {
476 let interval = Interval::try_from_str(
477 $value,
478 Some(DateTimeField::$from),
479 Some(DateTimeField::$to),
480 );
481
482 assert_eq!(interval, Ok(Interval::$duration($expected_value)));
483 };
484 }
485
486 test!("11", Year => 11, years);
487 test!("-11", Year => -11, years);
488 test!("18", Month => 18, months);
489 test!("-19", Month => -19, months);
490 test!("2", Day => 2, days);
491 test!("1.5", Day => 36, hours);
492 test!("-1.5", Day => -36, hours);
493 test!("2.5", Hour => 150, minutes);
494 test!("1", Hour => 60, minutes);
495 test!("-1", Hour => -60, minutes);
496 test!("35", Minute => 35, minutes);
497 test!("-35", Minute => -35, minutes);
498 test!("10.5", Minute => 630, seconds);
499 test!("10", Second => 10, seconds);
500 test!("-10", Second => -10, seconds);
501 test!("10.5", Second => 10_500_000, microseconds);
502 test!("-1.5", Second => -1_500_000, microseconds);
503
504 test!("10-2", Year to Month => 122, months);
505 test!("2 12", Day to Hour => 60, hours);
506 test!("1 01:30", Day to Minute => 60 * 24 + 90, minutes);
507 test!("1 01:30:40", Day to Second => (60 * 24 + 90) * 60 + 40, seconds);
508 test!("3 02:30:40.1234", Day to Second =>
509 (((3 * 24 + 2) * 60 + 30) * 60 + 40) * 1_000_000 + 123_400, microseconds);
510 test!("12:34", Hour to Minute => 12 * 60 + 34, minutes);
511 test!("12:34:56", Hour to Second => (12 * 60 + 34) * 60 + 56, seconds);
512 test!("12:34:56.1234", Hour to Second => ((12 * 60 + 34) * 60 + 56) * 1_000_000 + 123_400, microseconds);
513 test!("34:56.1234", Minute to Second => (34 * 60 + 56) * 1_000_000 + 123_400, microseconds);
514
515 test!("-1-4", Year to Month => -16, months);
516 test!("-2 10", Day to Hour => -58, hours);
517 test!("-1 00:01", Day to Minute => -(24 * 60 + 1), minutes);
518 test!("-1 00:00:01", Day to Second => -(24 * 3600 + 1), seconds);
519 test!("-1 00:00:01.1", Day to Second => -((24 * 3600 + 1) * 1000 + 100), milliseconds);
520 test!("-21:10", Hour to Minute => -(21 * 60 + 10), minutes);
521 test!("-05:12:03", Hour to Second => -(5 * 3600 + 12 * 60 + 3), seconds);
522 test!("-03:59:22.372", Hour to Second => -((3 * 3600 + 59 * 60 + 22) * 1000 + 372), milliseconds);
523 test!("-09:33", Minute to Second => -(9 * 60 + 33), seconds);
524 test!("-09:33.192", Minute to Second => -((9 * 60 + 33) * 1000 + 192), milliseconds);
525 }
526}