Coverage Report

Created: 2026-04-14 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/duration-str-0.21.0/src/lib.rs
Line
Count
Source
1
#![doc(
2
    html_logo_url = "https://raw.githubusercontent.com/baoyachi/duration-str/master/duration-str.png"
3
)]
4
//! Parse string to `Duration` .
5
//!
6
//! The String value unit support for one of:["y","mon","w","d","h","m","s", "ms", "µs", "ns"]
7
//!
8
//! - y:Year. Support string value: ["y" | "year" | "Y" | "YEAR" | "Year"]. e.g. 1y
9
//!
10
//! - mon:Month.Support string value: ["mon" | "MON" | "Month" | "month" | "MONTH"]. e.g. 1mon
11
//!
12
//! - w:Week.Support string value: ["w" | "W" | "Week" | "WEEK" | "week"]. e.g. 1w
13
//!
14
//! - d:Day.Support string value: ["d" | "D" | "Day" | "DAY" | "day"]. e.g. 1d
15
//!
16
//! - h:Hour.Support string value: ["h" | "H" | "hr" | "Hour" | "HOUR" | "hour"]. e.g. 1h
17
//!
18
//! - m:Minute.Support string value: ["m" | "M" | "Minute" | "MINUTE" | "minute" | "min" | "MIN"]. e.g. 1m
19
//!
20
//! - s:Second.Support string value: ["s" | "S" | "Second" | "SECOND" | "second" | "sec" | "SEC"]. e.g. 1s
21
//!
22
//! - ms:Millisecond.Support string value: ["ms" | "MS" | "Millisecond" | "MilliSecond" | "MILLISECOND" | "millisecond" | "mSEC" ]. e.g. 1ms
23
//!
24
//! - µs:Microsecond.Support string value: ["µs" | "µS" | "µsecond" | "us" | "uS" | "usecond" | "Microsecond" | "MicroSecond" | "MICROSECOND" | "microsecond" | "µSEC"]. e.g. 1µs
25
//!
26
//! - ns:Nanosecond.Support string value: ["ns" | "NS" | "Nanosecond" | "NanoSecond" | "NANOSECOND" | "nanosecond" | "nSEC"]. e.g. 1ns
27
//!
28
//! Also, `duration_str` support time duration simple evaluation(+,*). See examples below.
29
//!
30
//! # Example
31
//! ```rust
32
//! use duration_str::parse;
33
//! use std::time::Duration;
34
//!
35
//! let duration = parse("1d").unwrap();
36
//! assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
37
//!
38
//! let duration = parse("3m+31").unwrap(); //the default duration unit is second.
39
//! assert_eq!(duration, Duration::new(211, 0));
40
//!
41
//! let duration = parse("3m + 31").unwrap(); //the default duration unit is second.
42
//! assert_eq!(duration, Duration::new(211, 0));
43
//!
44
//! let duration = parse("3m + 13s + 29ms").unwrap();
45
//! assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
46
//!
47
//! let duration = parse("3m + 1s + 29ms +17µs").unwrap();
48
//! assert_eq!(
49
//!     duration,
50
//!     Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
51
//! );
52
//!
53
//! let duration = parse("3m 1s 29ms 17µs").unwrap();
54
//! assert_eq!(
55
//!     duration,
56
//!     Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
57
//! );
58
//!
59
//! let duration = parse("3m1s29ms17us").unwrap();
60
//! assert_eq!(
61
//!     duration,
62
//!     Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
63
//! );
64
//!
65
//! let duration = parse("1m*10").unwrap(); //the default duration unit is second.
66
//! assert_eq!(duration, Duration::new(600, 0));
67
//!
68
//! let duration = parse("1m*10ms").unwrap();
69
//! assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
70
//!
71
//! let duration = parse("1m * 1ns").unwrap();
72
//! assert_eq!(duration, Duration::new(0, 60));
73
//!
74
//! let duration = parse("1m * 1m").unwrap();
75
//! assert_eq!(duration, Duration::new(3600, 0));
76
//! let duration = parse("42µs").unwrap();
77
//! assert_eq!(duration,Duration::from_micros(42));
78
//! ```
79
//!
80
//! # deserialize to std::time::Duration
81
//!
82
#![cfg_attr(not(feature = "serde"), doc = "This requires the `serde` feature")]
83
//!
84
#![cfg_attr(not(feature = "serde"), doc = "```ignore")]
85
#![cfg_attr(feature = "serde", doc = "```rust")]
86
//! use duration_str::deserialize_duration;
87
//! use serde::*;
88
//! use std::time::Duration;
89
//!
90
//! /// Uses `deserialize_duration`.
91
//! #[derive(Debug, Deserialize)]
92
//! struct Config {
93
//!     #[serde(deserialize_with = "deserialize_duration")]
94
//!     time_ticker: Duration,
95
//! }
96
//!
97
//! fn needless_main() {
98
//!     let json = r#"{"time_ticker":"1m+30"}"#;
99
//!     let config: Config = serde_json::from_str(json).unwrap();
100
//!     assert_eq!(config.time_ticker, Duration::new(60 + 30, 0));
101
//!
102
//!     let json = r#"{"time_ticker":"1m+30s"}"#;
103
//!     let config: Config = serde_json::from_str(json).unwrap();
104
//!     assert_eq!(config.time_ticker, Duration::new(60 + 30, 0));
105
//!
106
//!     let json = r#"{"time_ticker":"3m 1s 29ms 17µs"}"#;
107
//!     let config: Config = serde_json::from_str(json).unwrap();
108
//!     assert_eq!(
109
//!         config.time_ticker,
110
//!         Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
111
//!     );
112
//!
113
//!     let json = r#"{"time_ticker":"3m1s29ms17us"}"#;
114
//!     let config: Config = serde_json::from_str(json).unwrap();
115
//!     assert_eq!(
116
//!         config.time_ticker,
117
//!         Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
118
//!     );
119
//! }
120
//! ```
121
//!
122
//! ## deserialize option duration (using the same function)
123
//!
124
#![cfg_attr(not(feature = "serde"), doc = "This requires the `serde` feature")]
125
//!
126
#![cfg_attr(not(feature = "serde"), doc = "```ignore")]
127
#![cfg_attr(feature = "serde", doc = "```rust")]
128
//! use duration_str::deserialize_duration;
129
//! use serde::*;
130
//! use std::time::Duration;
131
//!
132
//! /// Uses `deserialize_duration` for both required and optional fields.
133
//! #[derive(Debug, Deserialize, PartialEq)]
134
//! struct Config {
135
//!     #[serde(default, deserialize_with = "deserialize_duration")]
136
//!     time_ticker: Option<Duration>,
137
//!     name: String,
138
//! }
139
//!
140
//! fn needless_main() {
141
//!     // With valid duration string
142
//!     let json = r#"{"time_ticker":"1m+30","name":"foo"}"#;
143
//!     let config: Config = serde_json::from_str(json).unwrap();
144
//!     assert_eq!(config.time_ticker, Some(Duration::new(90, 0)));
145
//!
146
//!     // With null value
147
//!     let json = r#"{"time_ticker":null,"name":"foo"}"#;
148
//!     let config: Config = serde_json::from_str(json).unwrap();
149
//!     assert_eq!(config.time_ticker, None);
150
//!
151
//!     // With missing field
152
//!     let json = r#"{"name":"foo"}"#;
153
//!     let config: Config = serde_json::from_str(json).unwrap();
154
//!     assert_eq!(config.time_ticker, None);
155
//!
156
//!     // With empty string
157
//!     let json = r#"{"time_ticker":"","name":"foo"}"#;
158
//!     let config: Config = serde_json::from_str(json).unwrap();
159
//!     assert_eq!(config.time_ticker, None);
160
//! }
161
//! ```
162
//!
163
//! # deserialize to chrono::Duration
164
#![cfg_attr(
165
    not(all(feature = "chrono", feature = "serde")),
166
    doc = "This requires both the `chrono` and `serde` features"
167
)]
168
//!
169
#![cfg_attr(not(all(feature = "chrono", feature = "serde")), doc = "```ignore")]
170
#![cfg_attr(all(feature = "chrono", feature = "serde"), doc = "```rust")]
171
//! use chrono::Duration;
172
//! use duration_str::deserialize_duration_chrono;
173
//! use serde::*;
174
//!
175
//! #[derive(Debug, Deserialize)]
176
//! struct Config {
177
//!     #[serde(deserialize_with = "deserialize_duration_chrono")]
178
//!     time_ticker: Duration,
179
//! }
180
//!
181
//! fn needless_main() {
182
//!     let json = r#"{"time_ticker":"1m+30"}"#;
183
//!     let config: Config = serde_json::from_str(json).unwrap();
184
//!     assert_eq!(config.time_ticker, Duration::seconds(60 + 30));
185
//!
186
//!     let json = r#"{"time_ticker":"1m+30s"}"#;
187
//!     let config: Config = serde_json::from_str(json).unwrap();
188
//!     assert_eq!(config.time_ticker, Duration::seconds(60 + 30));
189
//!
190
//!     let json = r#"{"time_ticker":"3m 1s 29ms 17µs"}"#;
191
//!     let config: Config = serde_json::from_str(json).unwrap();
192
//!     assert_eq!(
193
//!         config.time_ticker,
194
//!         Duration::minutes(3)
195
//!             + Duration::seconds(1)
196
//!             + Duration::milliseconds(29)
197
//!             + Duration::microseconds(17)
198
//!     );
199
//!
200
//!     let json = r#"{"time_ticker":"3m1s29ms17us"}"#;
201
//!     let config: Config = serde_json::from_str(json).unwrap();
202
//!     assert_eq!(
203
//!         config.time_ticker,
204
//!         Duration::minutes(3)
205
//!             + Duration::seconds(1)
206
//!             + Duration::milliseconds(29)
207
//!             + Duration::microseconds(17)
208
//!     );
209
//! }
210
//! ```
211
212
mod error;
213
pub(crate) mod ext;
214
pub(crate) mod macros;
215
mod parser;
216
#[cfg(feature = "serde")]
217
mod serde;
218
mod unit;
219
220
pub use parser::parse;
221
#[cfg(feature = "serde")]
222
pub use serde::*;
223
use std::fmt::{Debug, Display};
224
225
use rust_decimal::prelude::ToPrimitive;
226
use rust_decimal::Decimal;
227
use std::str::FromStr;
228
use std::time::Duration;
229
230
pub use crate::error::DError;
231
use crate::unit::TimeUnit;
232
#[cfg(feature = "chrono")]
233
pub use naive_date::{
234
    after_naive_date, after_naive_date_time, before_naive_date, before_naive_date_time,
235
};
236
237
pub use ext::*;
238
239
pub type DResult<T> = Result<T, DError>;
240
241
const ONE_MICROSECOND_NANOSECOND: u64 = 1000;
242
const ONE_MILLISECOND_NANOSECOND: u64 = 1000 * ONE_MICROSECOND_NANOSECOND;
243
const ONE_SECOND_NANOSECOND: u64 = 1000 * ONE_MILLISECOND_NANOSECOND;
244
const ONE_MINUTE_NANOSECOND: u64 = 60 * ONE_SECOND_NANOSECOND;
245
const ONE_HOUR_NANOSECOND: u64 = 60 * ONE_MINUTE_NANOSECOND;
246
const ONE_DAY_NANOSECOND: u64 = 24 * ONE_HOUR_NANOSECOND;
247
const ONE_WEEK_NANOSECOND: u64 = 7 * ONE_DAY_NANOSECOND;
248
const ONE_MONTH_NANOSECOND: u64 = 30 * ONE_DAY_NANOSECOND;
249
const ONE_YEAR_NANOSECOND: u64 = 365 * ONE_DAY_NANOSECOND;
250
251
// const ONE_SECOND_DECIMAL: Decimal = 1_000_000_000.into();
252
0
fn one_second_decimal() -> Decimal {
253
0
    1_000_000_000.into()
254
0
}
255
256
const PLUS: &str = "+";
257
const STAR: &str = "*";
258
259
trait ExpectErr {
260
    type Output: Debug;
261
262
    fn expect_val() -> Self::Output;
263
264
    fn get_expect_val() -> &'static str;
265
    fn expect_err<S: AsRef<str> + Display>(s: S) -> String;
266
}
267
268
#[macro_export]
269
macro_rules! impl_expect_err {
270
    ($type:ty, $output:ty, [$($val:tt),* $(,)?]) => {
271
        impl ExpectErr for $type {
272
            type Output = $output;
273
274
0
            fn expect_val() -> Self::Output {
275
0
                [$($val),*]
276
0
            }
Unexecuted instantiation: <duration_str::unit::TimeUnit as duration_str::ExpectErr>::expect_val
Unexecuted instantiation: <duration_str::CondUnit as duration_str::ExpectErr>::expect_val
277
278
0
            fn expect_err<S: AsRef<str> + Display>(s: S) -> String {
279
0
                format!("expect one of:{:?}, but find:{}", Self::expect_val(), s)
280
0
            }
Unexecuted instantiation: <duration_str::unit::TimeUnit as duration_str::ExpectErr>::expect_err::<&str>
Unexecuted instantiation: <duration_str::CondUnit as duration_str::ExpectErr>::expect_err::<&str>
281
282
0
            fn get_expect_val() -> &'static str {
283
                static EXPECT_VAL_STR: &str = concat!(
284
                    "[",
285
                    impl_expect_err_internal!($($val),*),
286
                    "]"
287
                );
288
0
                EXPECT_VAL_STR
289
0
            }
Unexecuted instantiation: <duration_str::unit::TimeUnit as duration_str::ExpectErr>::get_expect_val
Unexecuted instantiation: <duration_str::CondUnit as duration_str::ExpectErr>::get_expect_val
290
        }
291
    };
292
}
293
294
#[macro_export]
295
macro_rules! impl_expect_err_internal {
296
    // match empty
297
    () => {
298
        ""
299
    };
300
    // match single type
301
    ($first:expr) => {
302
        stringify!($first)
303
    };
304
    // match multi type
305
    ($first:expr, $($rest:expr),*) => {
306
        concat!(
307
            stringify!($first),
308
            ", ",
309
            impl_expect_err_internal!($($rest),*)
310
        )
311
    };
312
}
313
314
#[derive(Debug, Eq, PartialEq, Clone)]
315
enum CondUnit {
316
    Plus,
317
    Star,
318
}
319
320
impl FromStr for CondUnit {
321
    type Err = String;
322
323
0
    fn from_str(s: &str) -> Result<Self, Self::Err> {
324
0
        match s {
325
0
            "+" => Ok(CondUnit::Plus),
326
0
            "*" => Ok(CondUnit::Star),
327
0
            _ => Err(Self::expect_err(s)),
328
        }
329
0
    }
330
}
331
332
impl_expect_err!(CondUnit, [char; 2], ['+', '*']);
333
334
impl CondUnit {
335
0
    fn init() -> (Self, u64) {
336
0
        (CondUnit::Star, ONE_SECOND_NANOSECOND)
337
0
    }
338
339
0
    fn contain(c: char) -> bool {
340
0
        Self::expect_val().contains(&c)
341
0
    }
342
343
0
    fn change_duration(&self) -> u64 {
344
0
        match self {
345
0
            CondUnit::Plus => 0,
346
0
            CondUnit::Star => ONE_SECOND_NANOSECOND,
347
        }
348
0
    }
349
350
0
    fn calc(&self, x: u64, y: u64) -> DResult<Duration> {
351
0
        let nano_second = match self {
352
0
            CondUnit::Plus => x.checked_add(y).ok_or(DError::OverflowError)?,
353
            CondUnit::Star => {
354
0
                let x: Decimal = x.into();
355
0
                let y: Decimal = y.into();
356
0
                let ret = (x / one_second_decimal())
357
0
                    .checked_mul(y / one_second_decimal())
358
0
                    .ok_or(DError::OverflowError)?
359
0
                    .checked_mul(one_second_decimal())
360
0
                    .ok_or(DError::OverflowError)?;
361
0
                ret.to_u64().ok_or(DError::OverflowError)?
362
            }
363
        };
364
0
        Ok(Duration::from_nanos(nano_second))
365
0
    }
366
}
367
368
trait Calc<T> {
369
    fn calc(&self) -> DResult<T>;
370
}
371
372
impl Calc<(CondUnit, u64)> for Vec<(&str, CondUnit, TimeUnit)> {
373
0
    fn calc(&self) -> DResult<(CondUnit, u64)> {
374
0
        let (mut init_cond, mut init_duration) = CondUnit::init();
375
0
        for (index, (val, cond, time_unit)) in self.iter().enumerate() {
376
0
            if index == 0 {
377
0
                init_cond = cond.clone();
378
0
                init_duration = init_cond.change_duration();
379
0
            } else if &init_cond != cond {
380
0
                return Err(DError::ParseError(format!(
381
0
                    "not support '{}' with '{}' calculate",
382
0
                    init_cond, cond
383
0
                )));
384
0
            }
385
0
            match init_cond {
386
                CondUnit::Plus => {
387
0
                    init_duration = init_duration
388
0
                        .checked_add(time_unit.duration(val)?)
389
0
                        .ok_or(DError::OverflowError)?;
390
                }
391
                CondUnit::Star => {
392
0
                    let time: Decimal = time_unit.duration(val)?.into();
393
0
                    let i = time / one_second_decimal();
394
0
                    let mut init: Decimal = init_duration.into();
395
0
                    init = init.checked_mul(i).ok_or(DError::OverflowError)?;
396
0
                    init_duration = init.to_u64().ok_or(DError::OverflowError)?;
397
                }
398
            }
399
        }
400
0
        Ok((init_cond, init_duration))
401
0
    }
402
}
403
404
impl Display for CondUnit {
405
0
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
406
0
        let str = match self {
407
0
            Self::Plus => PLUS.to_string(),
408
0
            Self::Star => STAR.to_string(),
409
        };
410
0
        write!(f, "{}", str)
411
0
    }
412
}
413
414
/// convert `Into<String>` to `std::time::Duration`
415
///
416
/// # Example
417
///
418
/// ```rust
419
/// use duration_str::parse;
420
/// use std::time::Duration;
421
///
422
/// // supports units
423
/// let duration = parse("1d").unwrap();
424
/// assert_eq!(duration,Duration::new(24*60*60,0));
425
///
426
/// // supports addition
427
/// let duration = parse("3m+31").unwrap();
428
/// assert_eq!(duration,Duration::new(211,0));
429
///
430
/// // spaces are optional
431
/// let duration = parse("3m + 31").unwrap();
432
/// assert_eq!(duration,Duration::new(211,0));
433
///
434
/// // plus sign is optional
435
/// let duration = parse("3m  31").unwrap();
436
/// assert_eq!(duration,Duration::new(211,0));
437
///
438
/// // both plus and spaces are optional
439
/// let duration = parse("3m31").unwrap();
440
/// assert_eq!(duration,Duration::new(211,0));
441
///
442
/// // supports multiplication
443
/// let duration = parse("1m*10").unwrap();
444
/// assert_eq!(duration,Duration::new(600,0));
445
///
446
/// // spaces are optional
447
/// let duration = parse("1m * 10").unwrap();
448
/// assert_eq!(duration,Duration::new(600,0));
449
/// ```
450
0
pub fn parse_std(input: impl AsRef<str>) -> Result<Duration, String> {
451
0
    parse(input.as_ref())
452
0
}
453
454
/// convert `Into<String>` to `chrono::Duration`
455
///
456
/// # Example
457
///
458
/// ```rust
459
/// use duration_str::parse_chrono;
460
/// use chrono::Duration;
461
///
462
/// // supports units
463
/// let duration = parse_chrono("1d").unwrap();
464
/// assert_eq!(duration,Duration::seconds(24*60*60));
465
///
466
/// // supports addition
467
/// let duration = parse_chrono("3m+31").unwrap();
468
/// assert_eq!(duration,Duration::seconds(211));
469
///
470
/// // spaces are optional
471
/// let duration = parse_chrono("3m + 31").unwrap();
472
/// assert_eq!(duration,Duration::seconds(211));
473
///
474
/// // plus sign is optional
475
/// let duration = parse_chrono("3m  31").unwrap();
476
/// assert_eq!(duration,Duration::seconds(211));
477
///
478
/// // both plus and spaces are optional
479
/// let duration = parse_chrono("3m31").unwrap();
480
/// assert_eq!(duration,Duration::seconds(211));
481
///
482
/// // supports multiplication
483
/// let duration = parse_chrono("1m*10").unwrap();
484
/// assert_eq!(duration,Duration::seconds(600));
485
///
486
/// // spaces are optional
487
/// let duration = parse_chrono("1m * 10").unwrap();
488
/// assert_eq!(duration,Duration::seconds(600));
489
/// ```
490
#[cfg(feature = "chrono")]
491
0
pub fn parse_chrono(input: impl AsRef<str>) -> Result<chrono::Duration, String> {
492
0
    let std_duration = parse_std(input)?;
493
0
    let duration = chrono::Duration::from_std(std_duration).map_err(|e| e.to_string())?;
494
0
    Ok(duration)
495
0
}
496
497
/// convert `Into<String>` to `time::Duration`
498
///
499
/// # Example
500
///
501
/// ```rust
502
/// use duration_str::parse_time;
503
/// use time::Duration;
504
///
505
/// // supports units
506
/// let duration = parse_time("1d").unwrap();
507
/// assert_eq!(duration,Duration::seconds(24*60*60));
508
///
509
/// // supports addition
510
/// let duration = parse_time("3m+31").unwrap();
511
/// assert_eq!(duration,Duration::seconds(211));
512
///
513
/// // spaces are optional
514
/// let duration = parse_time("3m + 31").unwrap();
515
/// assert_eq!(duration,Duration::seconds(211));
516
///
517
/// // plus sign is optional
518
/// let duration = parse_time("3m  31").unwrap();
519
/// assert_eq!(duration,Duration::seconds(211));
520
///
521
/// // both plus and spaces are optional
522
/// let duration = parse_time("3m31").unwrap();
523
/// assert_eq!(duration,Duration::seconds(211));
524
///
525
/// // supports multiplication
526
/// let duration = parse_time("1m*10").unwrap();
527
/// assert_eq!(duration,Duration::seconds(600));
528
///
529
/// // spaces are optional
530
/// let duration = parse_time("1m * 10").unwrap();
531
/// assert_eq!(duration,Duration::seconds(600));
532
/// ```
533
#[cfg(feature = "time")]
534
0
pub fn parse_time(input: impl AsRef<str>) -> Result<time::Duration, String> {
535
0
    let std_duration = parse_std(input)?;
536
0
    let duration = time::Duration::try_from(std_duration).map_err(|e| e.to_string())?;
537
0
    Ok(duration)
538
0
}
539
540
#[cfg(feature = "chrono")]
541
mod naive_date {
542
    use crate::parse_chrono;
543
    use chrono::Utc;
544
545
    #[allow(dead_code)]
546
    pub enum TimeHistory {
547
        Before,
548
        After,
549
    }
550
551
    #[cfg(feature = "chrono")]
552
0
    pub fn calc_naive_date_time(
553
0
        input: impl AsRef<str>,
554
0
        history: TimeHistory,
555
0
    ) -> Result<chrono::NaiveDateTime, String> {
556
0
        let duration = parse_chrono(input)?;
557
0
        let time = match history {
558
0
            TimeHistory::Before => (Utc::now() - duration).naive_utc(),
559
0
            TimeHistory::After => (Utc::now() + duration).naive_utc(),
560
        };
561
0
        Ok(time)
562
0
    }
563
564
    macro_rules! gen_naive_date_func {
565
        ($date_time:ident,$date:ident,$history:expr) => {
566
            #[allow(dead_code)]
567
            #[cfg(feature = "chrono")]
568
0
            pub fn $date_time(input: impl AsRef<str>) -> Result<chrono::NaiveDateTime, String> {
569
0
                calc_naive_date_time(input, $history)
570
0
            }
Unexecuted instantiation: duration_str::naive_date::after_naive_date_time::<_>
Unexecuted instantiation: duration_str::naive_date::before_naive_date_time::<_>
571
572
            #[allow(dead_code)]
573
            #[cfg(feature = "chrono")]
574
0
            pub fn $date(input: impl AsRef<str>) -> Result<chrono::NaiveDate, String> {
575
0
                let date: chrono::NaiveDateTime = calc_naive_date_time(input, $history)?;
576
0
                Ok(date.date())
577
0
            }
Unexecuted instantiation: duration_str::naive_date::after_naive_date::<_>
Unexecuted instantiation: duration_str::naive_date::before_naive_date::<_>
578
        };
579
    }
580
581
    gen_naive_date_func!(
582
        before_naive_date_time,
583
        before_naive_date,
584
        TimeHistory::Before
585
    );
586
587
    gen_naive_date_func!(after_naive_date_time, after_naive_date, TimeHistory::After);
588
}