Skip to main content

cargo/util/
time_span.rs

1use std::time::Duration;
2
3use crate::CargoResult;
4
5/// Parses a time span string.
6pub fn parse_time_span(span: &str) -> CargoResult<Duration> {
7    maybe_parse_time_span(span).ok_or_else(|| {
8        anyhow::format_err!(
9            "expected a value of the form \
10             \"N seconds/minutes/hours/days/weeks/months\", got: {span:?}"
11        )
12    })
13}
14
15/// Parses a time span string.
16///
17/// Returns None if the value is not valid. See [`parse_time_span`] if you
18/// need a variant that generates an error message.
19pub fn maybe_parse_time_span(span: &str) -> Option<Duration> {
20    let Some(right_i) = span.find(|c: char| !c.is_ascii_digit()) else {
21        return None;
22    };
23    let (left, mut right) = span.split_at(right_i);
24    if right.starts_with(' ') {
25        right = &right[1..];
26    }
27    let count: u64 = left.parse().ok()?;
28    let factor = match right {
29        "second" | "seconds" => 1,
30        "minute" | "minutes" => 60,
31        "hour" | "hours" => 60 * 60,
32        "day" | "days" => 24 * 60 * 60,
33        "week" | "weeks" => 7 * 24 * 60 * 60,
34        "month" | "months" => 2_629_746, // average is 30.436875 days
35        _ => return None,
36    };
37    Some(Duration::from_secs(factor * count))
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn time_spans() {
46        let d = |x| Some(Duration::from_secs(x));
47        assert_eq!(maybe_parse_time_span("0 seconds"), d(0));
48        assert_eq!(maybe_parse_time_span("1second"), d(1));
49        assert_eq!(maybe_parse_time_span("23 seconds"), d(23));
50        assert_eq!(maybe_parse_time_span("5 minutes"), d(60 * 5));
51        assert_eq!(maybe_parse_time_span("2 hours"), d(60 * 60 * 2));
52        assert_eq!(maybe_parse_time_span("1 day"), d(60 * 60 * 24));
53        assert_eq!(maybe_parse_time_span("2 weeks"), d(60 * 60 * 24 * 14));
54        assert_eq!(maybe_parse_time_span("6 months"), d(2_629_746 * 6));
55    }
56
57    #[test]
58    fn time_span_errors() {
59        assert_eq!(maybe_parse_time_span(""), None);
60        assert_eq!(maybe_parse_time_span("1"), None);
61        assert_eq!(maybe_parse_time_span("second"), None);
62        assert_eq!(maybe_parse_time_span("+2 seconds"), None);
63        assert_eq!(maybe_parse_time_span("day"), None);
64        assert_eq!(maybe_parse_time_span("-1 days"), None);
65        assert_eq!(maybe_parse_time_span("1.5 days"), None);
66        assert_eq!(maybe_parse_time_span("1 dayz"), None);
67        assert_eq!(maybe_parse_time_span("always"), None);
68        assert_eq!(maybe_parse_time_span("never"), None);
69        assert_eq!(maybe_parse_time_span("1 day "), None);
70        assert_eq!(maybe_parse_time_span(" 1 day"), None);
71        assert_eq!(maybe_parse_time_span("1  second"), None);
72    }
73}