Skip to content

Commit

Permalink
Support weekdays in parse_datetime
Browse files Browse the repository at this point in the history
This commit resolves issue uutils#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.
  • Loading branch information
philolo1 committed Aug 29, 2023
1 parent 2737b4a commit de2ef37
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
target/
fuzz/corpus
24 changes: 23 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ 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,
};

use parse_relative_time::parse_relative_time;

Expand Down Expand Up @@ -168,6 +172,24 @@ pub fn parse_datetime_at_date<S: AsRef<str> + Clone>(
}
}

// parse weekday
if let Ok(weekday) = parse_weekday::parse_weekday(s.as_ref()) {
let now = Local::now().naive_local();
let beginning_of_day = Local.with_ymd_and_hms(now.year(), now.month(), now.day(), 0, 0, 0);
let mut beginning_of_day = beginning_of_day.unwrap().naive_local();

loop {
if beginning_of_day.weekday() == weekday {
break;
}
beginning_of_day += Duration::days(1);
}

if let Ok(dt) = naive_dt_to_fixed_offset(date, 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") {
Expand Down
80 changes: 80 additions & 0 deletions src/parse_weekday.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use chrono::{ParseWeekdayError, Weekday};

const WEEKDAY_CONVERSION_MAP: [(&str, &str); 3] =
[("tues", "tue"), ("thurs", "thu"), ("thus", "thu")];

pub(crate) fn parse_weekday(s: &str) -> Result<Weekday, ParseWeekdayError> {
let mut s = s.trim().to_lowercase();
for (before, after) in WEEKDAY_CONVERSION_MAP {
if s == before {
s = String::from(after);
}
}

s.trim().parse::<Weekday>()
}

#[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),
("wednesday", Wed),
("thu", Thu),
("thursday", Thu),
("fri", Fri),
("friday", Fri),
("sat", Sat),
("saturday", Sat),
("sun", Sun),
("sunday", Sun),
];

for day in days.iter() {
let mut test_strings = vec![];
test_strings.push(String::from(day.0));
test_strings.push(format!(" {}", day.0));
test_strings.push(format!(" {} ", day.0));
test_strings.push(format!("{} ", day.0));
test_strings.push(format!(" {}", day.0.to_uppercase()));

let (left, right) = day.0.split_at(1);
test_strings.push(format!("{}{}", left.to_uppercase(), right.to_lowercase()));
test_strings.push(format!("{}{}", left.to_lowercase(), right.to_uppercase()));

for str in test_strings.iter() {
assert!(parse_weekday(str).is_ok());
assert_eq!(parse_weekday(str).unwrap(), day.1);
}
}
}

#[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_err());
}
}
}

0 comments on commit de2ef37

Please sign in to comment.