Skip to content

Commit

Permalink
dateparser: add parse_with() for configurable tz and default time
Browse files Browse the repository at this point in the history
a date string without time (eg. `July 14, 2021`) can be parsed into
DateTime<Utc> assigned with local time or midnight at UTC, for example,
either `2021-07-14T22:51:35.983216400Z` or `2021-07-14T00:00:00Z`.
  • Loading branch information
Rollie Ma committed Oct 10, 2021
1 parent fcd7e97 commit 826b8c4
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 44 deletions.
7 changes: 3 additions & 4 deletions belt/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ where
self.config.reset()?;
self.config.list()?;
} else if let Some(add) = &c.add {
self.config.add(&add)?;
self.config.add(add)?;
self.config.list()?;
} else if let Some(delete) = &c.delete {
self.config.delete(&delete)?;
self.config.delete(delete)?;
self.config.list()?;
}
}
Expand Down Expand Up @@ -118,8 +118,7 @@ mod tests {
}
let re = Regex::new(r"[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} [0-9-+]{5}")
.expect("failed to parse regex");
let matches: Vec<&str> = re.find_iter(&printed).map(|mat| mat.as_str()).collect();
assert_eq!(matches.len(), num_timezones + 1); // num_timezones + local
assert_eq!(re.find_iter(&printed).count(), num_timezones + 1); // num_timezones + local
}

#[test]
Expand Down
100 changes: 64 additions & 36 deletions dateparser/src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use regex::Regex;
/// Parse struct has methods implemented parsers for accepted formats.
pub struct Parse<'z, Tz2> {
tz: &'z Tz2,
default_time: NaiveTime,
}

impl<'z, Tz2> Parse<'z, Tz2>
Expand All @@ -15,8 +16,8 @@ where
{
/// Create a new instrance of [`Parse`] with a custom parsing timezone that handles the
/// datetime string without time offset.
pub fn new(tz: &'z Tz2) -> Self {
Self { tz }
pub fn new(tz: &'z Tz2, default_time: NaiveTime) -> Self {
Self { tz, default_time }
}

/// This method tries to parse the input datetime string with a list of accepted formats. See
Expand Down Expand Up @@ -265,7 +266,10 @@ where
if !RE.is_match(input) {
return None;
}
let now = Utc::now().with_timezone(self.tz);
let now = Utc::now()
.date()
.and_time(self.default_time)?
.with_timezone(self.tz);
NaiveDate::parse_from_str(input, "%Y-%m-%d")
.ok()
.map(|parsed| parsed.and_time(now.time()))
Expand All @@ -291,7 +295,10 @@ where
if let Some(matched_tz) = caps.name("tz") {
return match timezone::parse(matched_tz.as_str().trim()) {
Ok(offset) => {
let now = Utc::now().with_timezone(&offset);
let now = Utc::now()
.date()
.and_time(self.default_time)?
.with_timezone(&offset);
NaiveDate::parse_from_str(input, "%Y-%m-%d %Z")
.ok()
.map(|parsed| parsed.and_time(now.time()))
Expand Down Expand Up @@ -378,7 +385,10 @@ where
return None;
}

let now = Utc::now().with_timezone(self.tz);
let now = Utc::now()
.date()
.and_time(self.default_time)?
.with_timezone(self.tz);
NaiveDate::parse_from_str(input, "%Y-%m-%d")
.or_else(|_| NaiveDate::parse_from_str(input, "%Y-%b-%d"))
.ok()
Expand Down Expand Up @@ -490,7 +500,10 @@ where
return None;
}

let now = Utc::now().with_timezone(self.tz);
let now = Utc::now()
.date()
.and_time(self.default_time)?
.with_timezone(self.tz);
let dt = input.replace(", ", " ").replace(". ", " ");
NaiveDate::parse_from_str(&dt, "%B %d %y")
.or_else(|_| NaiveDate::parse_from_str(&dt, "%B %d %Y"))
Expand Down Expand Up @@ -541,7 +554,10 @@ where
return None;
}

let now = Utc::now().with_timezone(self.tz);
let now = Utc::now()
.date()
.and_time(self.default_time)?
.with_timezone(self.tz);
NaiveDate::parse_from_str(input, "%d %B %y")
.or_else(|_| NaiveDate::parse_from_str(input, "%d %B %Y"))
.ok()
Expand Down Expand Up @@ -604,7 +620,10 @@ where
return None;
}

let now = Utc::now().with_timezone(self.tz);
let now = Utc::now()
.date()
.and_time(self.default_time)?
.with_timezone(self.tz);
NaiveDate::parse_from_str(input, "%m/%d/%y")
.or_else(|_| NaiveDate::parse_from_str(input, "%m/%d/%Y"))
.ok()
Expand Down Expand Up @@ -654,7 +673,10 @@ where
return None;
}

let now = Utc::now().with_timezone(self.tz);
let now = Utc::now()
.date()
.and_time(self.default_time)?
.with_timezone(self.tz);
NaiveDate::parse_from_str(input, "%Y/%m/%d")
.ok()
.map(|parsed| parsed.and_time(now.time()))
Expand All @@ -678,7 +700,10 @@ where
return None;
}

let now = Utc::now().with_timezone(self.tz);
let now = Utc::now()
.date()
.and_time(self.default_time)?
.with_timezone(self.tz);
NaiveDate::parse_from_str(input, "%m.%d.%y")
.or_else(|_| NaiveDate::parse_from_str(input, "%m.%d.%Y"))
.or_else(|_| NaiveDate::parse_from_str(input, "%Y.%m.%d"))
Expand Down Expand Up @@ -736,7 +761,10 @@ where
return None;
}

let now = Utc::now().with_timezone(self.tz);
let now = Utc::now()
.date()
.and_time(self.default_time)?
.with_timezone(self.tz);
NaiveDate::parse_from_str(input, "%Y年%m月%d日")
.ok()
.map(|parsed| parsed.and_time(now.time()))
Expand All @@ -752,7 +780,7 @@ mod tests {

#[test]
fn unix_timestamp() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
("0000000000", Utc.ymd(1970, 1, 1).and_hms(0, 0, 0)),
Expand Down Expand Up @@ -786,7 +814,7 @@ mod tests {

#[test]
fn rfc3339() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand All @@ -813,7 +841,7 @@ mod tests {

#[test]
fn rfc2822() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand All @@ -840,7 +868,7 @@ mod tests {

#[test]
fn postgres_timestamp() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand Down Expand Up @@ -882,7 +910,7 @@ mod tests {

#[test]
fn ymd_hms() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
("2021-04-30 21:14", Utc.ymd(2021, 4, 30).and_hms(21, 14, 0)),
Expand Down Expand Up @@ -925,7 +953,7 @@ mod tests {

#[test]
fn ymd_hms_z() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand Down Expand Up @@ -975,7 +1003,7 @@ mod tests {

#[test]
fn ymd() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![(
"2021-02-21",
Expand All @@ -1001,7 +1029,7 @@ mod tests {

#[test]
fn ymd_z() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());
let now_at_pst = Utc::now().with_timezone(&FixedOffset::west(8 * 3600));
let now_at_cst = Utc::now().with_timezone(&FixedOffset::east(8 * 3600));

Expand Down Expand Up @@ -1048,7 +1076,7 @@ mod tests {

#[test]
fn hms() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand Down Expand Up @@ -1078,7 +1106,7 @@ mod tests {

#[test]
fn hms_z() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());
let now_at_pst = Utc::now().with_timezone(&FixedOffset::west(8 * 3600));

let test_cases = vec![
Expand Down Expand Up @@ -1125,7 +1153,7 @@ mod tests {

#[test]
fn month_ymd() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![(
"2021-Feb-21",
Expand All @@ -1151,7 +1179,7 @@ mod tests {

#[test]
fn month_md_hms() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand All @@ -1177,7 +1205,7 @@ mod tests {

#[test]
fn month_mdy_hms() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand Down Expand Up @@ -1207,7 +1235,7 @@ mod tests {

#[test]
fn month_mdy_hms_z() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand Down Expand Up @@ -1241,7 +1269,7 @@ mod tests {

#[test]
fn month_mdy() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand Down Expand Up @@ -1289,7 +1317,7 @@ mod tests {

#[test]
fn month_dmy_hms() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand All @@ -1316,7 +1344,7 @@ mod tests {

#[test]
fn month_dmy() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
("7 oct 70", Utc.ymd(1970, 10, 7).and_time(Utc::now().time())),
Expand Down Expand Up @@ -1353,7 +1381,7 @@ mod tests {

#[test]
fn slash_mdy_hms() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
("4/8/2014 22:05", Utc.ymd(2014, 4, 8).and_hms(22, 5, 0)),
Expand Down Expand Up @@ -1392,7 +1420,7 @@ mod tests {

#[test]
fn slash_mdy() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand Down Expand Up @@ -1426,7 +1454,7 @@ mod tests {

#[test]
fn slash_ymd_hms() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
("2014/4/8 22:05", Utc.ymd(2014, 4, 8).and_hms(22, 5, 0)),
Expand Down Expand Up @@ -1456,7 +1484,7 @@ mod tests {

#[test]
fn slash_ymd() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
(
Expand Down Expand Up @@ -1488,7 +1516,7 @@ mod tests {

#[test]
fn dot_mdy_or_ymd() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
// mm.dd.yyyy
Expand Down Expand Up @@ -1532,7 +1560,7 @@ mod tests {

#[test]
fn mysql_log_timestamp() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![
// yymmdd hh:mm:ss mysql log
Expand All @@ -1552,7 +1580,7 @@ mod tests {

#[test]
fn chinese_ymd_hms() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![(
"2014年04月08日11时25分18秒",
Expand All @@ -1572,7 +1600,7 @@ mod tests {

#[test]
fn chinese_ymd() {
let parse = Parse::new(&Utc);
let parse = Parse::new(&Utc, Utc::now().time());

let test_cases = vec![(
"2014年04月08日",
Expand Down

0 comments on commit 826b8c4

Please sign in to comment.