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