You've already forked parse_datetime
mirror of
https://github.com/uutils/parse_datetime.git
synced 2026-06-10 16:13:15 -07:00
Support weekdays in parse_datetime
This commit resolves issue #23. Adds parse_weekday function that uses chrono weekday parser with a map for edge cases. Adds tests cases to make sure it works correctly. Use nom for parsing.
This commit is contained in:
Generated
+17
@@ -118,6 +118,22 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
@@ -138,6 +154,7 @@ name = "parse_datetime"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"nom",
|
||||
"regex",
|
||||
]
|
||||
|
||||
|
||||
@@ -10,3 +10,4 @@ readme = "README.md"
|
||||
[dependencies]
|
||||
regex = "1.9"
|
||||
chrono = { version="0.4", default-features=false, features=["std", "alloc", "clock"] }
|
||||
nom = "7.1.3"
|
||||
|
||||
+77
-1
@@ -15,7 +15,12 @@ use std::fmt::{self, Display};
|
||||
// Expose parse_datetime
|
||||
mod parse_relative_time;
|
||||
|
||||
use chrono::{DateTime, FixedOffset, Local, LocalResult, NaiveDateTime, TimeZone};
|
||||
mod parse_weekday;
|
||||
|
||||
use chrono::{
|
||||
DateTime, Datelike, Duration, FixedOffset, Local, LocalResult, NaiveDateTime, TimeZone,
|
||||
Timelike,
|
||||
};
|
||||
|
||||
use parse_relative_time::parse_relative_time;
|
||||
|
||||
@@ -168,6 +173,27 @@ pub fn parse_datetime_at_date<S: AsRef<str> + Clone>(
|
||||
}
|
||||
}
|
||||
|
||||
// parse weekday
|
||||
if let Some(weekday) = parse_weekday::parse_weekday(s.as_ref()) {
|
||||
let mut beginning_of_day = date
|
||||
.with_hour(0)
|
||||
.unwrap()
|
||||
.with_minute(0)
|
||||
.unwrap()
|
||||
.with_second(0)
|
||||
.unwrap()
|
||||
.with_nanosecond(0)
|
||||
.unwrap();
|
||||
|
||||
while beginning_of_day.weekday() != weekday {
|
||||
beginning_of_day += Duration::days(1);
|
||||
}
|
||||
|
||||
let dt = DateTime::<FixedOffset>::from(beginning_of_day);
|
||||
|
||||
return Ok(dt);
|
||||
}
|
||||
|
||||
// Parse epoch seconds
|
||||
if s.as_ref().bytes().next() == Some(b'@') {
|
||||
if let Ok(parsed) = NaiveDateTime::parse_from_str(&s.as_ref()[1..], "%s") {
|
||||
@@ -353,6 +379,56 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod weekday {
|
||||
use chrono::{DateTime, Local, TimeZone};
|
||||
|
||||
use crate::parse_datetime_at_date;
|
||||
|
||||
fn get_formatted_date(date: DateTime<Local>, weekday: &str) -> String {
|
||||
let result = parse_datetime_at_date(date, weekday).unwrap();
|
||||
|
||||
return result.format("%F %T %f").to_string();
|
||||
}
|
||||
#[test]
|
||||
fn test_weekday() {
|
||||
// add some constant hours and minutes and seconds to check its reset
|
||||
let date = Local.with_ymd_and_hms(2023, 02, 28, 10, 12, 3).unwrap();
|
||||
|
||||
// 2023-2-28 is tuesday
|
||||
assert_eq!(
|
||||
get_formatted_date(date, "tuesday"),
|
||||
"2023-02-28 00:00:00 000000000"
|
||||
);
|
||||
|
||||
// 2023-3-01 is wednesday
|
||||
assert_eq!(
|
||||
get_formatted_date(date, "wed"),
|
||||
"2023-03-01 00:00:00 000000000"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
get_formatted_date(date, "thu"),
|
||||
"2023-03-02 00:00:00 000000000"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
get_formatted_date(date, "fri"),
|
||||
"2023-03-03 00:00:00 000000000"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
get_formatted_date(date, "sat"),
|
||||
"2023-03-04 00:00:00 000000000"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
get_formatted_date(date, "sun"),
|
||||
"2023-03-05 00:00:00 000000000"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod timestamp {
|
||||
use crate::parse_datetime;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
use crate::ParseDateTimeError;
|
||||
use chrono::{Duration, Local, NaiveDate, Utc};
|
||||
use regex::Regex;
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
use chrono::Weekday;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::combinator::value;
|
||||
use nom::{self, IResult};
|
||||
|
||||
// Helper macro to simplify tag matching
|
||||
macro_rules! tag_match {
|
||||
($day:expr, $($pattern:expr),+) => {
|
||||
value($day, alt(($(tag($pattern)),+)))
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn parse_weekday(s: &str) -> Option<Weekday> {
|
||||
let s = s.trim().to_lowercase();
|
||||
let s = s.as_str();
|
||||
|
||||
let parse_result: IResult<&str, Weekday> = nom::combinator::all_consuming(alt((
|
||||
tag_match!(Weekday::Mon, "monday", "mon"),
|
||||
tag_match!(Weekday::Tue, "tuesday", "tues", "tue"),
|
||||
tag_match!(Weekday::Wed, "wednesday", "wednes", "wed"),
|
||||
tag_match!(Weekday::Thu, "thursday", "thurs", "thur", "thu"),
|
||||
tag_match!(Weekday::Fri, "friday", "fri"),
|
||||
tag_match!(Weekday::Sat, "saturday", "sat"),
|
||||
tag_match!(Weekday::Sun, "sunday", "sun"),
|
||||
)))(s);
|
||||
|
||||
match parse_result {
|
||||
Ok((_, weekday)) => Some(weekday),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use chrono::Weekday::*;
|
||||
|
||||
use crate::parse_weekday::parse_weekday;
|
||||
|
||||
#[test]
|
||||
fn test_valid_weekdays() {
|
||||
let days = [
|
||||
("mon", Mon),
|
||||
("monday", Mon),
|
||||
("tue", Tue),
|
||||
("tues", Tue),
|
||||
("tuesday", Tue),
|
||||
("wed", Wed),
|
||||
("wednes", Wed),
|
||||
("wednesday", Wed),
|
||||
("thu", Thu),
|
||||
("thursday", Thu),
|
||||
("fri", Fri),
|
||||
("friday", Fri),
|
||||
("sat", Sat),
|
||||
("saturday", Sat),
|
||||
("sun", Sun),
|
||||
("sunday", Sun),
|
||||
];
|
||||
|
||||
for (name, weekday) in days {
|
||||
assert_eq!(parse_weekday(name), Some(weekday));
|
||||
assert_eq!(parse_weekday(&format!(" {}", name)), Some(weekday));
|
||||
assert_eq!(parse_weekday(&format!(" {} ", name)), Some(weekday));
|
||||
assert_eq!(parse_weekday(&format!("{} ", name)), Some(weekday));
|
||||
|
||||
let (left, right) = name.split_at(1);
|
||||
let (test_str1, test_str2) = (
|
||||
format!("{}{}", left.to_uppercase(), right.to_lowercase()),
|
||||
format!("{}{}", left.to_lowercase(), right.to_uppercase()),
|
||||
);
|
||||
|
||||
assert_eq!(parse_weekday(&test_str1), Some(weekday));
|
||||
assert_eq!(parse_weekday(&test_str2), Some(weekday));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_weekdays() {
|
||||
let days = [
|
||||
"mond",
|
||||
"tuesda",
|
||||
"we",
|
||||
"th",
|
||||
"fr",
|
||||
"sa",
|
||||
"su",
|
||||
"garbageday",
|
||||
"tomorrow",
|
||||
"yesterday",
|
||||
];
|
||||
for day in days {
|
||||
assert!(parse_weekday(day).is_none());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user