]> git.proxmox.com Git - rustc.git/blob - src/vendor/humantime/src/duration.rs
New upstream version 1.26.0+dfsg1
[rustc.git] / src / vendor / humantime / src / duration.rs
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)]
62 pub struct FormattedDuration(Duration);
63
64 trait OverflowOp: Sized {
65 fn mul(self, other: Self) -> Result<Self, Error>;
66 fn add(self, other: Self) -> Result<Self, Error>;
67 }
68
69 impl OverflowOp for u64 {
70 fn mul(self, other: Self) -> Result<Self, Error> {
71 self.checked_mul(other).ok_or(Error::NumberOverflow)
72 }
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> {
85 fn off(&self) -> usize {
86 self.src.len() - self.iter.as_str().len()
87 }
88
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 }
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
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 /// ```
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 /// ```
230 pub fn format_duration(val: Duration) -> FormattedDuration {
231 FormattedDuration(val)
232 }
233
234 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 }
250 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 {
264 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]
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]
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]
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]
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]
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]
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 }