1 use std::fmt;
2 use std::str::Chars;
3 use std::time::Duration;
4 use std::error::Error as StdError;
5 
6 quick_error! {
7     /// Error parsing human-friendly duration
8     #[derive(Debug, PartialEq, Clone, Copy)]
9     pub enum Error {
10         /// Invalid character during parsing
11         ///
12         /// More specifically anything that is not alphanumeric is prohibited
13         ///
14         /// The field is an byte offset of the character in the string.
15         InvalidCharacter(offset: usize) {
16             display("invalid character at {}", offset)
17             description("invalid character")
18         }
19         /// Non-numeric value where number is expected
20         ///
21         /// This usually means that either time unit is broken into words,
22         /// e.g. `m sec` instead of `msec`, or just number is omitted,
23         /// for example `2 hours min` instead of `2 hours 1 min`
24         ///
25         /// The field is an byte offset of the errorneous character
26         /// in the string.
27         NumberExpected(offset: usize) {
28             display("expected number at {}", offset)
29             description("expected number")
30         }
31         /// Unit in the number is not one of allowed units
32         ///
33         /// See documentation of `parse_duration` for the list of supported
34         /// time units.
35         ///
36         /// The two fields are start and end (exclusive) of the slice from
37         /// the original string, containing errorneous value
38         UnknownUnit(start: usize, end: usize) {
39             display("unknown unit at {}-{}", start, end)
40             description("unknown unit")
41         }
42         /// The numeric value is too large
43         ///
44         /// Usually this means value is too large to be useful. If user writes
45         /// data in subsecond units, then the maximum is about 3k years. When
46         /// using seconds, or larger units, the limit is even larger.
47         NumberOverflow {
48             display(self_) -> ("{}", self_.description())
49             description("number is too large")
50         }
51         /// The value was an empty string (or consists only whitespace)
52         Empty {
53             display(self_) -> ("{}", self_.description())
54             description("value was empty")
55         }
56     }
57 
58 }
59 
60 /// A wrapper type that allows you to Display a Duration
61 #[derive(Debug, Clone)]
62 pub struct FormattedDuration(Duration);
63 
64 trait OverflowOp: Sized {
mul(self, other: Self) -> Result<Self, Error>65     fn mul(self, other: Self) -> Result<Self, Error>;
add(self, other: Self) -> Result<Self, Error>66     fn add(self, other: Self) -> Result<Self, Error>;
67 }
68 
69 impl OverflowOp for u64 {
mul(self, other: Self) -> Result<Self, Error>70     fn mul(self, other: Self) -> Result<Self, Error> {
71         self.checked_mul(other).ok_or(Error::NumberOverflow)
72     }
add(self, other: Self) -> Result<Self, Error>73     fn add(self, other: Self) -> Result<Self, Error> {
74         self.checked_add(other).ok_or(Error::NumberOverflow)
75     }
76 }
77 
78 struct Parser<'a> {
79     iter: Chars<'a>,
80     src: &'a str,
81     current: (u64, u64),
82 }
83 
84 impl<'a> Parser<'a> {
off(&self) -> usize85     fn off(&self) -> usize {
86         self.src.len() - self.iter.as_str().len()
87     }
88 
parse_first_char(&mut self) -> Result<Option<u64>, Error>89     fn parse_first_char(&mut self) -> Result<Option<u64>, Error> {
90         let off = self.off();
91         for c in self.iter.by_ref() {
92             match c {
93                 '0'...'9' => {
94                     return Ok(Some(c as u64 - '0' as u64));
95                 }
96                 c if c.is_whitespace() => continue,
97                 _ => {
98                     return Err(Error::NumberExpected(off));
99                 }
100             }
101         }
102         return Ok(None);
103     }
parse_unit(&mut self, n: u64, start: usize, end: usize) -> Result<(), Error>104     fn parse_unit(&mut self, n: u64, start: usize, end: usize)
105         -> Result<(), Error>
106     {
107         let (mut sec, nsec) = match &self.src[start..end] {
108             "nanos" | "nsec" | "ns" => (0u64, n),
109             "usec" | "us" => (0u64, try!(n.mul(1000))),
110             "millis" | "msec" | "ms" => (0u64, try!(n.mul(1000_000))),
111             "seconds" | "second" | "secs" | "sec" | "s" => (n, 0),
112             "minutes" | "minute" | "min" | "mins" | "m"
113             => (try!(n.mul(60)), 0),
114             "hours" | "hour" | "hr" | "hrs" | "h" => (try!(n.mul(3600)), 0),
115             "days" | "day" | "d" => (try!(n.mul(86400)), 0),
116             "weeks" | "week" | "w" => (try!(n.mul(86400*7)), 0),
117             "months" | "month" | "M" => (try!(n.mul(2630016)), 0), // 30.44d
118             "years" | "year" | "y" => (try!(n.mul(31557600)), 0), // 365.25d
119             _ => return Err(Error::UnknownUnit(start, end)),
120         };
121         let mut nsec = try!(self.current.1.add(nsec));
122         if nsec > 1000_000_000 {
123             sec = try!(sec.add(nsec / 1000_000_000));
124             nsec %= 1000_000_000;
125         }
126         sec = try!(self.current.0.add(sec));
127         self.current = (sec, nsec);
128         Ok(())
129     }
130 
parse(mut self) -> Result<Duration, Error>131     fn parse(mut self) -> Result<Duration, Error> {
132         let mut n = try!(try!(self.parse_first_char()).ok_or(Error::Empty));
133         'outer: loop {
134             let mut off = self.off();
135             while let Some(c) = self.iter.next() {
136                 match c {
137                     '0'...'9' => {
138                         n = try!(n.checked_mul(10)
139                             .and_then(|x| x.checked_add(c as u64 - '0' as u64))
140                             .ok_or(Error::NumberOverflow));
141                     }
142                     c if c.is_whitespace() => {}
143                     'a'...'z' | 'A'...'Z' => {
144                         break;
145                     }
146                     _ => {
147                         return Err(Error::InvalidCharacter(off));
148                     }
149                 }
150                 off = self.off();
151             }
152             let start = off;
153             let mut off = self.off();
154             while let Some(c) = self.iter.next() {
155                 match c {
156                     '0'...'9' => {
157                         try!(self.parse_unit(n, start, off));
158                         n = c as u64 - '0' as u64;
159                         continue 'outer;
160                     }
161                     c if c.is_whitespace() => break,
162                     'a'...'z' | 'A'...'Z' => {}
163                     _ => {
164                         return Err(Error::InvalidCharacter(off));
165                     }
166                 }
167                 off = self.off();
168             }
169             try!(self.parse_unit(n, start, off));
170             n = match try!(self.parse_first_char()) {
171                 Some(n) => n,
172                 None => return Ok(
173                     Duration::new(self.current.0, self.current.1 as u32)),
174             };
175         }
176     }
177 
178 }
179 
180 /// Parse duration object `1hour 12min 5s`
181 ///
182 /// The duration object is a concatenation of time spans. Where each time
183 /// span is an integer number and a suffix. Supported suffixes:
184 ///
185 /// * `nsec`, `ns` -- microseconds
186 /// * `usec`, `us` -- microseconds
187 /// * `msec`, `ms` -- milliseconds
188 /// * `seconds`, `second`, `sec`, `s`
189 /// * `minutes`, `minute`, `min`, `m`
190 /// * `hours`, `hour`, `hr`, `h`
191 /// * `days`, `day`, `d`
192 /// * `weeks`, `week`, `w`
193 /// * `months`, `month`, `M` -- defined as 30.44 days
194 /// * `years`, `year`, `y` -- defined as 365.25 days
195 ///
196 /// # Examples
197 ///
198 /// ```
199 /// use std::time::Duration;
200 /// use humantime::parse_duration;
201 ///
202 /// assert_eq!(parse_duration("2h 37min"), Ok(Duration::new(9420, 0)));
203 /// assert_eq!(parse_duration("32ms"), Ok(Duration::new(0, 32_000_000)));
204 /// ```
parse_duration(s: &str) -> Result<Duration, Error>205 pub fn parse_duration(s: &str) -> Result<Duration, Error> {
206     Parser {
207         iter: s.chars(),
208         src: s,
209         current: (0, 0),
210     }.parse()
211 }
212 
213 /// Formats duration into a human-readable string
214 ///
215 /// Note: this format is guaranteed to have same value when using
216 /// parse_duration, but we can change some details of the exact composition
217 /// of the value.
218 ///
219 /// # Examples
220 ///
221 /// ```
222 /// use std::time::Duration;
223 /// use humantime::format_duration;
224 ///
225 /// let val1 = Duration::new(9420, 0);
226 /// assert_eq!(format_duration(val1).to_string(), "2h 37m");
227 /// let val2 = Duration::new(0, 32_000_000);
228 /// assert_eq!(format_duration(val2).to_string(), "32ms");
229 /// ```
format_duration(val: Duration) -> FormattedDuration230 pub fn format_duration(val: Duration) -> FormattedDuration {
231     FormattedDuration(val)
232 }
233 
item_plural(f: &mut fmt::Formatter, started: &mut bool, name: &str, value: u64) -> fmt::Result234 fn item_plural(f: &mut fmt::Formatter, started: &mut bool,
235     name: &str, value: u64)
236     -> fmt::Result
237 {
238     if value > 0 {
239         if *started {
240             f.write_str(" ")?;
241         }
242         write!(f, "{}{}", value, name)?;
243         if value > 1 {
244             f.write_str("s")?;
245         }
246         *started = true;
247     }
248     Ok(())
249 }
item(f: &mut fmt::Formatter, started: &mut bool, name: &str, value: u32) -> fmt::Result250 fn item(f: &mut fmt::Formatter, started: &mut bool, name: &str, value: u32)
251     -> fmt::Result
252 {
253     if value > 0 {
254         if *started {
255             f.write_str(" ")?;
256         }
257         write!(f, "{}{}", value, name)?;
258         *started = true;
259     }
260     Ok(())
261 }
262 
263 impl fmt::Display for FormattedDuration {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result264     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
265         let secs = self.0.as_secs();
266         let nanos = self.0.subsec_nanos();
267 
268         if secs == 0 && nanos == 0 {
269             f.write_str("0s")?;
270             return Ok(());
271         }
272 
273         let years = secs / 31557600;  // 365.25d
274         let ydays = secs % 31557600;
275         let months = ydays / 2630016;  // 30.44d
276         let mdays = ydays % 2630016;
277         let days = mdays / 86400;
278         let day_secs = mdays % 86400;
279         let hours = day_secs / 3600;
280         let minutes = day_secs % 3600 / 60;
281         let seconds = day_secs % 60;
282 
283         let millis = nanos / 1_000_000;
284         let micros = nanos / 1000 % 1000;
285         let nanosec = nanos % 1000;
286 
287         let ref mut started = false;
288         item_plural(f, started, "year", years)?;
289         item_plural(f, started, "month", months)?;
290         item_plural(f, started, "day", days)?;
291         item(f, started, "h", hours as u32)?;
292         item(f, started, "m", minutes as u32)?;
293         item(f, started, "s", seconds as u32)?;
294         item(f, started, "ms", millis)?;
295         item(f, started, "us", micros)?;
296         item(f, started, "ns", nanosec)?;
297         Ok(())
298     }
299 }
300 
301 #[cfg(test)]
302 mod test {
303     extern crate rand;
304 
305     use std::time::Duration;
306     use self::rand::Rng;
307     use super::{parse_duration, format_duration};
308     use super::Error;
309 
310     #[test]
test_units()311     fn test_units() {
312         assert_eq!(parse_duration("17nsec"), Ok(Duration::new(0, 17)));
313         assert_eq!(parse_duration("17nanos"), Ok(Duration::new(0, 17)));
314         assert_eq!(parse_duration("33ns"), Ok(Duration::new(0, 33)));
315         assert_eq!(parse_duration("3usec"), Ok(Duration::new(0, 3000)));
316         assert_eq!(parse_duration("78us"), Ok(Duration::new(0, 78000)));
317         assert_eq!(parse_duration("31msec"), Ok(Duration::new(0, 31000000)));
318         assert_eq!(parse_duration("31millis"), Ok(Duration::new(0, 31000000)));
319         assert_eq!(parse_duration("6ms"), Ok(Duration::new(0, 6000000)));
320         assert_eq!(parse_duration("3000s"), Ok(Duration::new(3000, 0)));
321         assert_eq!(parse_duration("300sec"), Ok(Duration::new(300, 0)));
322         assert_eq!(parse_duration("300secs"), Ok(Duration::new(300, 0)));
323         assert_eq!(parse_duration("50seconds"), Ok(Duration::new(50, 0)));
324         assert_eq!(parse_duration("1second"), Ok(Duration::new(1, 0)));
325         assert_eq!(parse_duration("100m"), Ok(Duration::new(6000, 0)));
326         assert_eq!(parse_duration("12min"), Ok(Duration::new(720, 0)));
327         assert_eq!(parse_duration("12mins"), Ok(Duration::new(720, 0)));
328         assert_eq!(parse_duration("1minute"), Ok(Duration::new(60, 0)));
329         assert_eq!(parse_duration("7minutes"), Ok(Duration::new(420, 0)));
330         assert_eq!(parse_duration("2h"), Ok(Duration::new(7200, 0)));
331         assert_eq!(parse_duration("7hr"), Ok(Duration::new(25200, 0)));
332         assert_eq!(parse_duration("7hrs"), Ok(Duration::new(25200, 0)));
333         assert_eq!(parse_duration("1hour"), Ok(Duration::new(3600, 0)));
334         assert_eq!(parse_duration("24hours"), Ok(Duration::new(86400, 0)));
335         assert_eq!(parse_duration("1day"), Ok(Duration::new(86400, 0)));
336         assert_eq!(parse_duration("2days"), Ok(Duration::new(172800, 0)));
337         assert_eq!(parse_duration("365d"), Ok(Duration::new(31536000, 0)));
338         assert_eq!(parse_duration("1week"), Ok(Duration::new(604800, 0)));
339         assert_eq!(parse_duration("7weeks"), Ok(Duration::new(4233600, 0)));
340         assert_eq!(parse_duration("52w"), Ok(Duration::new(31449600, 0)));
341         assert_eq!(parse_duration("1month"), Ok(Duration::new(2630016, 0)));
342         assert_eq!(parse_duration("3months"), Ok(Duration::new(3*2630016, 0)));
343         assert_eq!(parse_duration("12M"), Ok(Duration::new(31560192, 0)));
344         assert_eq!(parse_duration("1year"), Ok(Duration::new(31557600, 0)));
345         assert_eq!(parse_duration("7years"), Ok(Duration::new(7*31557600, 0)));
346         assert_eq!(parse_duration("17y"), Ok(Duration::new(536479200, 0)));
347     }
348 
349     #[test]
test_combo()350     fn test_combo() {
351         assert_eq!(parse_duration("20 min 17 nsec "), Ok(Duration::new(1200, 17)));
352         assert_eq!(parse_duration("2h 15m"), Ok(Duration::new(8100, 0)));
353     }
354 
355     #[test]
all_86400_seconds()356     fn all_86400_seconds() {
357         for second in 0..86400 {  // scan leap year and non-leap year
358             let d = Duration::new(second, 0);
359             assert_eq!(d,
360                 parse_duration(&format_duration(d).to_string()).unwrap());
361         }
362     }
363 
364     #[test]
random_second()365     fn random_second() {
366         for _ in 0..10000 {
367             let sec = rand::thread_rng().gen_range(0, 253370764800);
368             let d = Duration::new(sec, 0);
369             assert_eq!(d,
370                 parse_duration(&format_duration(d).to_string()).unwrap());
371         }
372     }
373 
374     #[test]
random_any()375     fn random_any() {
376         for _ in 0..10000 {
377             let sec = rand::thread_rng().gen_range(0, 253370764800);
378             let nanos = rand::thread_rng().gen_range(0, 1_000_000_000);
379             let d = Duration::new(sec, nanos);
380             assert_eq!(d,
381                 parse_duration(&format_duration(d).to_string()).unwrap());
382         }
383     }
384 
385     #[test]
test_overlow()386     fn test_overlow() {
387         // Overflow on subseconds is earlier because of how we do conversion
388         // we could fix it, but I don't see any good reason for this
389         assert_eq!(parse_duration("100000000000000000000ns"),
390             Err(Error::NumberOverflow));
391         assert_eq!(parse_duration("100000000000000000us"),
392             Err(Error::NumberOverflow));
393         assert_eq!(parse_duration("100000000000000ms"),
394             Err(Error::NumberOverflow));
395 
396         assert_eq!(parse_duration("100000000000000000000s"),
397             Err(Error::NumberOverflow));
398         assert_eq!(parse_duration("10000000000000000000m"),
399             Err(Error::NumberOverflow));
400         assert_eq!(parse_duration("1000000000000000000h"),
401             Err(Error::NumberOverflow));
402         assert_eq!(parse_duration("100000000000000000d"),
403             Err(Error::NumberOverflow));
404         assert_eq!(parse_duration("10000000000000000w"),
405             Err(Error::NumberOverflow));
406         assert_eq!(parse_duration("1000000000000000M"),
407             Err(Error::NumberOverflow));
408         assert_eq!(parse_duration("10000000000000y"),
409             Err(Error::NumberOverflow));
410     }
411 }
412