3 use std
::time
::Duration
;
4 use std
::error
::Error
as StdError
;
7 /// Error parsing human-friendly duration
8 #[derive(Debug, PartialEq, Clone, Copy)]
10 /// Invalid character during parsing
12 /// More specifically anything that is not alphanumeric is prohibited
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")
19 /// Non-numeric value where number is expected
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`
25 /// The field is an byte offset of the errorneous character
27 NumberExpected(offset
: usize) {
28 display("expected number at {}", offset
)
29 description("expected number")
31 /// Unit in the number is not one of allowed units
33 /// See documentation of `parse_duration` for the list of supported
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")
42 /// The numeric value is too large
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.
48 display(self_
) -> ("{}", self_
.description())
49 description("number is too large")
51 /// The value was an empty string (or consists only whitespace)
53 display(self_
) -> ("{}", self_
.description())
54 description("value was empty")
60 /// A wrapper type that allows you to Display a Duration
62 pub struct FormattedDuration(Duration
);
64 trait OverflowOp
: Sized
{
65 fn mul(self, other
: Self) -> Result
<Self, Error
>;
66 fn add(self, other
: Self) -> Result
<Self, Error
>;
69 impl OverflowOp
for u64 {
70 fn mul(self, other
: Self) -> Result
<Self, Error
> {
71 self.checked_mul(other
).ok_or(Error
::NumberOverflow
)
73 fn add(self, other
: Self) -> Result
<Self, Error
> {
74 self.checked_add(other
).ok_or(Error
::NumberOverflow
)
85 fn off(&self) -> usize {
86 self.src
.len() - self.iter
.as_str().len()
89 fn parse_first_char(&mut self) -> Result
<Option
<u64>, Error
> {
91 for c
in self.iter
.by_ref() {
94 return Ok(Some(c
as u64 - '
0'
as u64));
96 c
if c
.is_whitespace() => continue,
98 return Err(Error
::NumberExpected(off
));
104 fn parse_unit(&mut self, n
: u64, start
: usize, end
: usize)
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
)),
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;
126 sec
= try
!(self.current
.0.add(sec
));
127 self.current
= (sec
, nsec
);
131 fn parse(mut self) -> Result
<Duration
, Error
> {
132 let mut n
= try
!(try
!(self.parse_first_char()).ok_or(Error
::Empty
));
134 let mut off
= self.off();
135 while let Some(c
) = self.iter
.next() {
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
));
142 c
if c
.is_whitespace() => {}
143 'a'
...'z'
| 'A'
...'Z'
=> {
147 return Err(Error
::InvalidCharacter(off
));
153 let mut off
= self.off();
154 while let Some(c
) = self.iter
.next() {
157 try
!(self.parse_unit(n
, start
, off
));
158 n
= c
as u64 - '
0'
as u64;
161 c
if c
.is_whitespace() => break,
162 'a'
...'z'
| 'A'
...'Z'
=> {}
164 return Err(Error
::InvalidCharacter(off
));
169 try
!(self.parse_unit(n
, start
, off
));
170 n
= match try
!(self.parse_first_char()) {
173 Duration
::new(self.current
.0, self.current
.1 as u32)),
180 /// Parse duration object `1hour 12min 5s`
182 /// The duration object is a concatenation of time spans. Where each time
183 /// span is an integer number and a suffix. Supported suffixes:
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
199 /// use std::time::Duration;
200 /// use humantime::parse_duration;
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)));
205 pub fn parse_duration(s
: &str) -> Result
<Duration
, Error
> {
213 /// Formats duration into a human-readable string
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
222 /// use std::time::Duration;
223 /// use humantime::format_duration;
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");
230 pub fn format_duration(val
: Duration
) -> FormattedDuration
{
231 FormattedDuration(val
)
234 fn item_plural(f
: &mut fmt
::Formatter
, started
: &mut bool
,
235 name
: &str, value
: u64)
242 write
!(f
, "{}{}", value
, name
)?
;
250 fn item(f
: &mut fmt
::Formatter
, started
: &mut bool
, name
: &str, value
: u32)
257 write
!(f
, "{}{}", value
, name
)?
;
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();
268 if secs
== 0 && nanos
== 0 {
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;
283 let millis
= nanos
/ 1_000_000;
284 let micros
= nanos
/ 1000 % 1000;
285 let nanosec
= nanos
% 1000;
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
)?
;
305 use std
::time
::Duration
;
307 use super::{parse_duration, format_duration}
;
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)));
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)));
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);
360 parse_duration(&format_duration(d
).to_string()).unwrap());
367 let sec
= rand
::thread_rng().gen_range(0, 253370764800);
368 let d
= Duration
::new(sec
, 0);
370 parse_duration(&format_duration(d
).to_string()).unwrap());
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
);
381 parse_duration(&format_duration(d
).to_string()).unwrap());
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
));
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
));