time_util/
math.rs

1// Copyright (C) 2020 Daniel Mueller <deso@posteo.net>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::time::Duration;
5use std::time::SystemTime;
6use std::time::UNIX_EPOCH;
7
8use math_util::round_up;
9
10
11/// The number of seconds in a day.
12const DAY_SECS: u32 = 24 * 60 * 60;
13
14
15fn next_day_duration(now: SystemTime) -> Duration {
16  // `UNIX_EPOCH` is the first time stamp that can be represented, so
17  // there is no way `SystemTime::duration_since` can ever fail with it
18  // as a parameter.
19  let duration = now.duration_since(UNIX_EPOCH).unwrap();
20  let next_day = round_up(duration.as_secs(), DAY_SECS.into());
21  let duration = Duration::from_secs(next_day);
22  duration
23}
24
25/// Calculate the time stamp representing the next day of the given time
26/// stamp.
27///
28/// Note that currently a time stamp marking precisely midnight will not
29/// advance to the next day.
30// TODO: We should fix this behavior.
31pub fn next_day(now: SystemTime) -> SystemTime {
32  let duration = next_day_duration(now);
33  UNIX_EPOCH + duration
34}
35
36pub fn days_back_from(now: SystemTime, count: u32) -> SystemTime {
37  let duration = next_day_duration(now) - Duration::from_secs(DAY_SECS.into()) * (count + 1);
38  UNIX_EPOCH + duration
39}
40
41pub fn days_back(count: u32) -> SystemTime {
42  days_back_from(SystemTime::now(), count)
43}
44
45/// Calculate a `SystemTime` representing 0:00:00 (i.e., the first
46/// second of) the next day.
47pub fn tomorrow() -> SystemTime {
48  next_day(SystemTime::now())
49}
50
51
52#[cfg(test)]
53pub mod tests {
54  use super::*;
55
56  use crate::parse::parse_system_time_from_str;
57
58
59  #[test]
60  fn calculate_next_day() {
61    let now = parse_system_time_from_str("2020-02-07T13:00:00Z").unwrap();
62    let tomorrow = parse_system_time_from_str("2020-02-08T00:00:00Z").unwrap();
63    assert_eq!(next_day(now), tomorrow);
64
65    let now = parse_system_time_from_str("2019-03-31T23:59:59Z").unwrap();
66    let tomorrow = parse_system_time_from_str("2019-04-01T00:00:00Z").unwrap();
67    assert_eq!(next_day(now), tomorrow);
68  }
69
70  #[test]
71  fn calculate_prev_days() {
72    let now = parse_system_time_from_str("2020-02-07T09:00:00Z").unwrap();
73    let yesterday = parse_system_time_from_str("2020-02-06T00:00:00Z").unwrap();
74    assert_eq!(days_back_from(now, 1), yesterday);
75
76    let now = parse_system_time_from_str("2020-02-02T23:59:59Z").unwrap();
77    let five_ago = parse_system_time_from_str("2020-01-28T00:00:00Z").unwrap();
78    assert_eq!(days_back_from(now, 5), five_ago);
79  }
80}