Table of Contents
Overview
fundu provides a parser to convert strings into a std::time::Duration. It tries to improve on
the standard methods Duration::from_secs_f64 and Duration::try_from_secs_f64 (which is stable since 1.66.0) with intermediate parsing to a float via f64::from_str
by
- Merging the separate steps of parsing float like strings to
f64and parsing off64to aDuration - Providing customizable TimeUnits which are accepted in the input string.
- Using no floating point calculations and precisely parse the input as it is. So, what you put
in you is what you get out within the range of a
std::time::Duration. - Evaluating to
Duration::MAXif the input number was larger than that maximum or the input string was positiveinfinity. - Supporting input strings of arbitrary length.
- Providing better error messages.
This library aims for low runtime costs (See Benchmarks) and being a lightweight
crate. fundu is purely built on top of the rust stdlib, and there are no additional dependencies
required. The accepted string format is almost the same like the scientific floating point format
and compatible to the f64::from_str format. In other words, if the accepted input string could
previously converted to an f64 with f64::from_str, no change is needed to accept the same format
with fundu. For a direct comparison of fundu vs the rust native methods Duration::(try_)from_secs_f64 see
Comparison. For further details see the
Documentation!
Installation
Add this to Cargo.toml
[]
= "0.3.0"
Examples
If only the default parser is required once, then the parse_duration method can be used.
use parse_duration;
use Duration;
let input = "1.0e2s";
assert_eq!;
When a customization of the accepted TimeUnits is required, then the builder
DurationParser can be used.
use DurationParser;
use Duration;
let input = "3m";
assert_eq!;
When no time units are configured, seconds is assumed.
use DurationParser;
use Duration;
let input = "1.0e2";
assert_eq!;
However, setting the default time unit to something different than seconds can be achieved with
use ;
use Duration;
assert_eq!;
Note the following will return an error because y (Years) is not in the default set of TimeUnits.
use DurationParser;
let input = "3y";
assert!;
The parser is reusable and the set of time units is fully customizable
use ;
use Duration;
let mut parser = with_time_units;
for in &
Also, fundu tries to give informative error messages
use DurationParser;
use Duration;
assert_eq!;
See also the examples folder for common recipes. Run an example with
cargo run --example $FILE_NAME_WITHOUT_FILETYPE_SUFFIX
Time units
Time units are used to calculate the final Duration. Second is the default time unit (if not
specified otherwise) and if no time unit was specified in the input string. The table below gives an
overview of the constructor methods and which time units are available. If a custom set of time
units is required, DurationParser::with_time_units can be used.
| Name | Time unit | Calculation | DurationParser::new | parse_duration |
DurationParser:: with_all_time_units |
DurationParser:: without_time_units |
|---|---|---|---|---|---|
| Nanoseconds | ns | 1e-9s | ☑ | ☑ | ☐ |
| Microseconds | Ms | 1e-6s | ☑ | ☑ | ☐ |
| Milliseconds | ms | 1e-3s | ☑ | ☑ | ☐ |
| Seconds | s | SI definition | ☑ | ☑ | ☐ |
| Minutes | m | 60s | ☑ | ☑ | ☐ |
| Hours | h | 60m | ☑ | ☑ | ☐ |
| Days | d | 24h | ☑ | ☑ | ☐ |
| Weeks | w | 7d | ☑ | ☑ | ☐ |
| Months | M | Year / 12 | ☐ | ☑ | ☐ |
| Years | y | 365.25d | ☐ | ☑ | ☐ |
Note that Months and Years are not included in the default set of time units. The current
implementation uses an approximate calculation of Months and Years in seconds and if they are
included in the final configuration, the Julian
year based calculation is used. (See table
above)
Benchmarks
To run the benchmarks on your machine, clone the repository
git clone https://github.com/Joining7943/fundu.git
cd fundu
and then run the benchmarks with
cargo bench
To get a rough idea about the parsing times, here the average parsing speed of two inputs on a comparatively slow machine (Quad core 3000Mhz, 8GB DDR3, Linux)
| Input | parser with time units | avg parsing time | ~ samples / s |
|---|---|---|---|
1 |
no | 48.716 ns |
20_527_136.874 |
1 |
yes | 52.548 ns |
19_030_219.989 |
format!("{}.{}e-1022", "1".repeat(1022), "1".repeat(1022)) |
no | 3.7219 µs |
268_679.975 |
format!("{}.{}e-1022", "1".repeat(1022), "1".repeat(1022)) |
yes | 3.7132 µs |
269_309.490 |
For comparison, fundu's precision and additional features only add a very low performance overhead (the reference function is Duration::from_secs_f64(input.parse().unwrap())):
| Input | avg parsing time | ~ samples / s |
|---|---|---|
1 |
25.630 ns |
39_016_777.214 |
format!("{}.{}e-1022", "1".repeat(1022), "1".repeat(1022)) |
1.7457 µs |
572_836.111 |
Comparison fundu vs Duration::(try_)from_secs_f64
Here's a short incomplete overview of differences and advantages of fundu over using
Duration::(try_)from_secs_f64(input.parse().unwrap())
| Input | Result fundu |
Result Duration::(try_)from_secs_f64 |
|---|---|---|
01271480964981728917.1 |
Duration::new(1271480964981728917, 1) |
Duration::new(1271480964981729024, 0) |
1.11111111111e10 |
Duration::new(11111111111, 1) |
Duration::new(11111111111, 100000381) |
1ns |
Duration::new(0, 1) |
error parsing to f64: cannot parse time units |
1000 |
When changing the default unit to MilliSecond -> Duration::new(1, 0) |
is always seconds based |
1e20 |
Duration::MAX |
panics or returns an error due to: can not convert float seconds to Duration: value is either too big or NaN |
infinity |
DURATION::MAX |
panics or returns an error due to: can not convert float seconds to Duration: value is either too big or NaN |
Having said that, fundu has a small impact on performance, so if you need to parse a massive amount of
inputs and can do without the full precision or any of its features, you may be better off using the
native methods from the rust stdlib.
Platform support
Since fundu is purely built on top of the rust stdlib without platform specific code, this
library should be compatible with all platforms. Please open an issue if you find any unsupported
platforms which rust itself supports.
See also the CI
TODO
- Improve performance for long inputs
- Improve error messages and error types
- Implement usage of more than one identifier for time units
- Provide other year calculations:
- mean Gregorian year
- Sidereal year
- Tropical year
See also Changelog
License
MIT license (LICENSE or http://opensource.org/licenses/MIT)