Expand description
Overview
Parse a rust string into a std::time::Duration
or for negative numbers into a
time::Duration
.
fundu
is a configurable, precise and blazingly fast duration parser
- with the flexibility to customize
TimeUnit
s, the number format and other aspects - without floating point calculations. What you put in is what you get out.
- with sound limit handling. Infinity and numbers larger than
Duration::MAX
evaluate toDuration::MAX
. Numbersx
withabs(x) < 1e-18
evaluate toDuration::ZERO
. - with the option to parse negative numbers to negative durations if the
negative
feature is enabled - and with informative error messages
Features
standard
The standard
feature exposes the DurationParser
and DurationParserBuilder
structs
with time units which can be customized. However, the identifier
for each TimeUnit
is
fixed.
custom
The custom
feature provides a CustomDurationParser
and CustomDurationParserBuilder
with fully customizable identifiers for each TimeUnit
. With the CustomDurationParser
it is also possible to define completely new time units, a CustomTimeUnit
.
negative
Enable parsing negative numbers with DurationParser::parse_negative
and
CustomDurationParser::parse_negative
into negative durations represented by
time::Duration
. If not activated, negative numbers produce a
ParseError::NegativeNumber
.
Configuration and Format
This parser can be configured to accept strings with a default set of time units
DurationParser::new
, with all time units DurationParser::with_all_time_units
or
without DurationParser::without_time_units
. A custom set of time units is also possible
with DurationParser::with_time_units
. All these parsers accept strings such as
1.41
42
2e-8
,2e+8
(or likewise2.0e8
).5
or likewise0.5
3.
or likewise3.0
inf
,+inf
,infinity
,+infinity
All alphabetic characters are matched case-insensitive, so InFINity
or 2E8
are valid input
strings. Additionally, depending on the chosen set of time units one of the following time
units (the first column) are accepted.
TimeUnit | default id | is default time unit |
---|---|---|
Nanosecond | ns | yes |
Microsecond | Ms | yes |
Millisecond | ms | yes |
Second | s | yes |
Minute | m | yes |
Hour | h | yes |
Day | d | yes |
Week | w | yes |
Month | M | no |
Year | y | no |
If no time unit is given and not specified otherwise with DurationParser::default_unit
then s
(= Second
) is assumed. Some accepted strings with time units
31.2s
200000Ms
3.14e8w
- …
Per default there is no whitespace allowed between the number and the TimeUnit
, but this
behavior can be changed with setting DurationParser::allow_delimiter
.
Format specification
The time units are case-sensitive, all other alphabetic characters are case-insensitive
Duration ::= Sign? ( 'inf' | 'infinity' | ( Number TimeUnit?))
TimeUnit ::= ns | Ms | ms | s | m | h | d | w | M | y
Number ::= ( Digit+ |
Digit+ '.' Digit* |
Digit* '.' Digit+ )
Exp?
Exp ::= 'e' Sign? Digit+
Sign ::= [+-]
Digit ::= [0-9]
Special cases which are not displayed in the specification:
- The
TimeUnit
rule is based on the default identifiers as defined in the table above. They can also be completely customized with theCustomDurationParser
. - Negative values, including negative infinity are not allowed as long as the
negative
feature is not activated. For exceptions see the next point. - Numbers
x
(positive and negative) close to0
(abs(x) < 1e-18
) are treated as0
- Positive infinity and numbers exceeding
Duration::MAX
saturate atDuration::MAX
- The exponent must be in the range
-32768 <= Exp <= 32767
- If
allow_delimiter
is set then any defined delimiter is allowed between the Number and TimeUnit. This setting also allows the input string to end with this delimiter but only if no time unit was present. The parser then assumes the default time unit.
Examples
If only the default configuration is required once, the parse_duration
method can be used.
use std::time::Duration;
use fundu::parse_duration;
let input = "1.0e2s";
assert_eq!(parse_duration(input).unwrap(), Duration::new(100, 0));
When a customization of the accepted TimeUnit
s is required, then the builder
DurationParser
can be used.
use std::time::Duration;
use fundu::DurationParser;
let input = "3m";
assert_eq!(
DurationParser::with_all_time_units().parse(input).unwrap(),
Duration::new(180, 0)
);
When no time units are configured, seconds is assumed.
use std::time::Duration;
use fundu::DurationParser;
let input = "1.0e2";
assert_eq!(
DurationParser::without_time_units().parse(input).unwrap(),
Duration::new(100, 0)
);
However, the following will return an error because y
(Years) is not a default time unit:
use fundu::DurationParser;
let input = "3y";
assert!(DurationParser::new().parse(input).is_err());
The parser is reusable and the set of time units is fully customizable
use std::time::Duration;
use fundu::DurationParser;
use fundu::TimeUnit::*;
let parser = DurationParser::with_time_units(&[NanoSecond, Minute, Hour]);
for (input, expected) in &[
("9e3ns", Duration::new(0, 9000)),
("10m", Duration::new(600, 0)),
("1.1h", Duration::new(3960, 0)),
("7", Duration::new(7, 0)),
] {
assert_eq!(parser.parse(input).unwrap(), *expected);
}
Setting the default time unit (if no time unit is given in the input string) to something different than seconds is also easily possible
use std::time::Duration;
use fundu::DurationParser;
use fundu::TimeUnit::*;
assert_eq!(
DurationParser::without_time_units()
.default_unit(MilliSecond)
.parse("1000")
.unwrap(),
Duration::new(1, 0)
);
The identifiers for time units can be fully customized with any number of valid
utf-8 sequences if the custom
feature is activated:
use std::time::Duration;
use fundu::CustomDurationParser;
use fundu::TimeUnit::*;
let parser = CustomDurationParser::with_time_units(&[
(MilliSecond, &["χιλιοστό του δευτερολέπτου"]),
(Second, &["s", "secs"]),
(Hour, &["⏳"]),
]);
for (input, expected) in &[
(".3χιλιοστό του δευτερολέπτου", Duration::new(0, 300_000)),
("1e3secs", Duration::new(1000, 0)),
("1.1⏳", Duration::new(3960, 0)),
] {
assert_eq!(parser.parse(input).unwrap(), *expected);
}
Also, fundu
tries to give informative error messages
use fundu::DurationParser;
assert_eq!(
DurationParser::without_time_units()
.parse("1y")
.unwrap_err()
.to_string(),
"Time unit error: No time units allowed but found: 'y' at column 1"
);
The number format can be easily adjusted to your needs. For example to allow numbers being optional, allow some ascii whitespace between the number and the time unit and restrict the number format to whole numbers, without fractional part and an exponent:
use std::time::Duration;
use fundu::TimeUnit::*;
use fundu::{DurationParser, ParseError};
let parser = DurationParser::builder()
.custom_time_units(&[NanoSecond])
.allow_delimiter(|byte| matches!(byte, b'\t' | b'\n' | b'\r' | b' '))
.number_is_optional()
.disable_fraction()
.disable_exponent()
.build();
for (input, expected) in &[
("ns", Duration::new(0, 1)),
("1000\t\n\r ns", Duration::new(0, 1000)),
] {
assert_eq!(parser.parse(input).unwrap(), *expected);
}
for (input, expected) in &[
(
"1.0ns",
ParseError::Syntax(1, "No fraction allowed".to_string()),
),
(
"1e9ns",
ParseError::Syntax(1, "No exponent allowed".to_string()),
),
] {
assert_eq!(parser.parse(input).unwrap_err(), *expected);
}
Structs
- CustomDurationParser
custom
A parser with a customizable set ofTimeUnit
s and customizable identifiers. - Like
crate::DurationParserBuilder
forcrate::DurationParser
, this is a builder for aCustomDurationParser
. - CustomTimeUnit
custom
- DurationParser
standard
A parser with a customizable set ofTimeUnit
s with default identifiers. - DurationParserBuilder
standard
An ergonomic builder for aDurationParser
. - The multiplier of a
TimeUnit
.
Enums
- Error type emitted during the parsing
- The time units the parser can understand and needed to configure the
DurationParser
.
Constants
- DEFAULT_ALL_TIME_UNITS
custom
- The default identifier of
TimeUnit::Day
- The default identifier of
TimeUnit::Hour
- The default identifier of
TimeUnit::MicroSecond
- The default identifier of
TimeUnit::MicroSecond
- The default identifier of
TimeUnit::Minute
- The default identifier of
TimeUnit::Month
- The default identifier of
TimeUnit::NanoSecond
- The default identifier of
TimeUnit::Second
- The default identifier of
TimeUnit::Week
- The default identifier of
TimeUnit::Year
- DEFAULT_TIME_UNITS
custom
- SYSTEMD_TIME_UNITS
custom
TheIdentifiers
as defined insystemd.time
Functions
- parse_duration
standard
Parse a string into astd::time::Duration
by accepting astring
similar to floating point with the default set of time units.
Type Definitions
- An ascii delimiter defined as closure.
- Identifiers
custom
A pair consisting of aTimeUnit
and its associated identifiers