You've already forked parse_datetime
mirror of
https://github.com/uutils/parse_datetime.git
synced 2026-06-10 16:13:15 -07:00
add the code from rust/coreutils
This commit is contained in:
Generated
+65
@@ -0,0 +1,65 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime_to_duration"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.160"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "humantime_to_duration"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
regex = "1.8.1"
|
||||
time = "0.3.20"
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use regex::Regex;
|
||||
use time::Duration;
|
||||
|
||||
/// Parses a relative time string and returns a `Duration` representing the
|
||||
/// relative time.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `s` - A string slice representing the relative time.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use time::Duration;
|
||||
/// let duration = humantime_to_duration::from_str("+3 days");
|
||||
/// assert_eq!(duration, Some(Duration::days(3)));
|
||||
/// ```
|
||||
///
|
||||
/// # Supported formats
|
||||
///
|
||||
/// The function supports the following formats for relative time:
|
||||
///
|
||||
/// * `num` `unit` (e.g., "-1 hour", "+3 days")
|
||||
/// * `unit` (e.g., "hour", "day")
|
||||
/// * "now" or "today"
|
||||
/// * "yesterday"
|
||||
/// * "tomorrow"
|
||||
/// * use "ago" for the past
|
||||
///
|
||||
/// [num] can be a positive or negative integer.
|
||||
/// [unit] can be one of the following: "fortnight", "week", "day", "hour",
|
||||
/// "minute", "min", "second", "sec" and their plural forms.
|
||||
///
|
||||
/// It is also possible to pass "1 hour 2 minutes" or "2 days and 2 hours"
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Some(Duration)` - If the input string can be parsed as a relative time
|
||||
/// * `None` - If the input string cannot be parsed as a relative time
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return `None` if the input string cannot be parsed as a
|
||||
/// relative time.
|
||||
pub fn from_str(s: &str) -> Option<Duration> {
|
||||
let time_pattern: Regex = Regex::new(
|
||||
r"(?x)
|
||||
(?P<value>[-+]?\d*)\s*
|
||||
(?P<unit>years?|months?|fortnights?|weeks?|days?|hours?|h|minutes?|mins?|m|seconds?|secs?|s|yesterday|tomorrow|now|today)
|
||||
(\s*(?P<separator>and|,)?\s*)?"
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut total_duration = Duration::ZERO;
|
||||
let mut is_ago = s.contains(" ago");
|
||||
|
||||
for capture in time_pattern.captures_iter(s) {
|
||||
let value_str = capture.name("value").unwrap().as_str();
|
||||
let value = if value_str.is_empty() {
|
||||
1
|
||||
} else {
|
||||
value_str.parse::<i64>().unwrap_or(1)
|
||||
};
|
||||
let unit = capture.name("unit").unwrap().as_str();
|
||||
|
||||
if capture.name("ago").is_some() {
|
||||
is_ago = true;
|
||||
}
|
||||
|
||||
let duration = match unit {
|
||||
"years" | "year" => Duration::days(value * 365),
|
||||
"months" | "month" => Duration::days(value * 30),
|
||||
"fortnights" | "fortnight" => Duration::weeks(value * 2),
|
||||
"weeks" | "week" => Duration::weeks(value),
|
||||
"days" | "day" => Duration::days(value),
|
||||
"hours" | "hour" | "h" => Duration::hours(value),
|
||||
"minutes" | "minute" | "mins" | "min" | "m" => Duration::minutes(value),
|
||||
"seconds" | "second" | "secs" | "sec" | "s" => Duration::seconds(value),
|
||||
"yesterday" => Duration::days(-1),
|
||||
"tomorrow" => Duration::days(1),
|
||||
"now" | "today" => Duration::ZERO,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
total_duration = total_duration.checked_add(if is_ago { -duration } else { duration })?;
|
||||
}
|
||||
|
||||
if total_duration == Duration::ZERO && !time_pattern.is_match(s) {
|
||||
None
|
||||
} else {
|
||||
Some(total_duration)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::from_str;
|
||||
use time::Duration;
|
||||
|
||||
#[test]
|
||||
fn test_years() {
|
||||
assert_eq!(from_str("1 year"), Some(Duration::seconds(31536000)));
|
||||
assert_eq!(from_str("-2 years"), Some(Duration::seconds(-63072000)));
|
||||
assert_eq!(from_str("2 years ago"), Some(Duration::seconds(-63072000)));
|
||||
assert_eq!(from_str("year"), Some(Duration::seconds(31536000)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_months() {
|
||||
assert_eq!(from_str("1 month"), Some(Duration::seconds(2592000)));
|
||||
assert_eq!(from_str("1 month and 2 weeks"), Some(Duration::seconds(3801600)));
|
||||
assert_eq!(from_str("1 month and 2 weeks ago"), Some(Duration::seconds(-3801600)));
|
||||
assert_eq!(from_str("2 months"), Some(Duration::seconds(5184000)));
|
||||
assert_eq!(from_str("month"), Some(Duration::seconds(2592000)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fortnights() {
|
||||
assert_eq!(from_str("1 fortnight"), Some(Duration::seconds(1209600)));
|
||||
assert_eq!(from_str("3 fortnights"), Some(Duration::seconds(3628800)));
|
||||
assert_eq!(from_str("fortnight"), Some(Duration::seconds(1209600)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_weeks() {
|
||||
assert_eq!(from_str("1 week"), Some(Duration::seconds(604800)));
|
||||
assert_eq!(from_str("1 week 3 days"), Some(Duration::seconds(864000)));
|
||||
assert_eq!(from_str("1 week 3 days ago"), Some(Duration::seconds(-864000)));
|
||||
assert_eq!(from_str("-2 weeks"), Some(Duration::seconds(-1209600)));
|
||||
assert_eq!(from_str("2 weeks ago"), Some(Duration::seconds(-1209600)));
|
||||
assert_eq!(from_str("week"), Some(Duration::seconds(604800)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_days() {
|
||||
assert_eq!(from_str("1 day"), Some(Duration::seconds(86400)));
|
||||
assert_eq!(from_str("2 days ago"), Some(Duration::seconds(-172800)));
|
||||
assert_eq!(from_str("-2 days"), Some(Duration::seconds(-172800)));
|
||||
assert_eq!(from_str("day"), Some(Duration::seconds(86400)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hours() {
|
||||
assert_eq!(from_str("1 hour"), Some(Duration::seconds(3600)));
|
||||
assert_eq!(from_str("1 hour ago"), Some(Duration::seconds(-3600)));
|
||||
assert_eq!(from_str("-2 hours"), Some(Duration::seconds(-7200)));
|
||||
assert_eq!(from_str("hour"), Some(Duration::seconds(3600)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_minutes() {
|
||||
assert_eq!(from_str("1 minute"), Some(Duration::seconds(60)));
|
||||
assert_eq!(from_str("2 minutes"), Some(Duration::seconds(120)));
|
||||
assert_eq!(from_str("min"), Some(Duration::seconds(60)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seconds() {
|
||||
assert_eq!(from_str("1 second"), Some(Duration::seconds(1)));
|
||||
assert_eq!(from_str("2 seconds"), Some(Duration::seconds(2)));
|
||||
assert_eq!(from_str("sec"), Some(Duration::seconds(1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relative_days() {
|
||||
assert_eq!(from_str("now"), Some(Duration::seconds(0)));
|
||||
assert_eq!(from_str("today"), Some(Duration::seconds(0)));
|
||||
assert_eq!(from_str("yesterday"), Some(Duration::seconds(-86400)));
|
||||
assert_eq!(from_str("tomorrow"), Some(Duration::seconds(86400)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_spaces() {
|
||||
assert_eq!(from_str("-1hour"), Some(Duration::hours(-1)));
|
||||
assert_eq!(from_str("+3days"), Some(Duration::days(3)));
|
||||
assert_eq!(from_str("2weeks"), Some(Duration::weeks(2)));
|
||||
assert_eq!(from_str("2weeks 1hour"), Some(Duration::seconds(1213200)));
|
||||
assert_eq!(from_str("2weeks 1hour ago"), Some(Duration::seconds(-1213200)));
|
||||
assert_eq!(from_str("+4months"), Some(Duration::days(4 * 30)));
|
||||
assert_eq!(from_str("-2years"), Some(Duration::days(-2 * 365)));
|
||||
assert_eq!(from_str("15minutes"), Some(Duration::minutes(15)));
|
||||
assert_eq!(from_str("-30seconds"), Some(Duration::seconds(-30)));
|
||||
assert_eq!(from_str("30seconds ago"), Some(Duration::seconds(-30)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_input() {
|
||||
assert_eq!(from_str("invalid"), None);
|
||||
assert_eq!(from_str("1 invalid"), None);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user