diff --git a/.travis.yml b/.travis.yml index 8cc0fa8..fb8032e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,5 @@ language: rust -python: - - '3.5' - -env: - global: - - CRATE_NAME=dtparse - -matrix: - include: - # Tests will only run on nightly, and where I can install Python/Dateutil - # Should still build on stable though - - env: TARGET=aarch64-unknown-linux-gnu - rust: nightly - - env: TARGET=x86_64-unknown-linux-gnu - rust: nightly - - env: TARGET=x86_64-unknown-linux-musl - rust: nightly - -before_install: - - set -e - - pip install --user python-dateutil - - rustup self update - +rust: + - stable + - beta + - nightly \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 124b157..08d86f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,4 @@ authors = ["Bradlee Speice "] chrono = "0.4" lazy_static = "*" num-traits = "0.2" -rust_decimal = "0.8" - -[dev-dependencies] -pyo3 = "0.2" \ No newline at end of file +rust_decimal = "0.8" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 5077d78..7cb74be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -955,7 +955,9 @@ impl Parser { tzoffset: Option, token: &str, ) -> bool { - let all_ascii_upper = token.chars().all(|c| 65u8 as char <= c && c <= 90u8 as char); + let all_ascii_upper = token + .chars() + .all(|c| 65u8 as char <= c && c <= 90u8 as char); return hour.is_some() && tzname.is_none() && tzoffset.is_none() && token.len() <= 5 && all_ascii_upper; } @@ -1018,7 +1020,9 @@ impl Parser { { Ok(None) } else if res.tzname.is_some() && tzinfos.contains_key(res.tzname.as_ref().unwrap()) { - Ok(Some(FixedOffset::east(tzinfos.get(res.tzname.as_ref().unwrap()).unwrap().clone()))) + Ok(Some(FixedOffset::east( + tzinfos.get(res.tzname.as_ref().unwrap()).unwrap().clone(), + ))) } else if res.tzname.is_some() { // TODO: Dateutil issues a warning/deprecation notice here. Should we force the issue? println!("tzname {} identified but not understood. Ignoring for the time being, but behavior is subject to change.", res.tzname.as_ref().unwrap()); @@ -1292,7 +1296,16 @@ fn ljust(s: &str, chars: usize, replace: char) -> String { } pub fn parse(timestr: &str) -> ParseResult<(NaiveDateTime, Option)> { - let res = Parser::default().parse(timestr, None, None, false, false, None, false, HashMap::new())?; + let res = Parser::default().parse( + timestr, + None, + None, + false, + false, + None, + false, + HashMap::new(), + )?; Ok((res.0, res.1)) } diff --git a/tests/compat.rs b/tests/compat.rs deleted file mode 100644 index e1b4a7f..0000000 --- a/tests/compat.rs +++ /dev/null @@ -1,565 +0,0 @@ -extern crate chrono; -extern crate pyo3; - -use chrono::Datelike; -use chrono::NaiveDate; -use chrono::NaiveDateTime; -use chrono::Timelike; -use pyo3::ObjectProtocol; -use pyo3::PyBool; -use pyo3::PyDict; -use pyo3::PyList; -use pyo3::PyObject; -use pyo3::PyModule; -use pyo3::Python; -use std::collections::HashMap; -use std::time::Duration; -use std::thread; - -extern crate dtparse; - -use dtparse::Parser; -use dtparse::ParserInfo; -use dtparse::tokenize; - -macro_rules! test_split { - ($py:ident, $timelex:ident, $s:expr) => { - let f = $timelex.call_method1($py, "split", $s).unwrap(); - let l: &PyList = f.extract($py).unwrap(); - let s: Vec = l.iter().map(|i| format!("{}", i)).collect(); - - assert_eq!(s, tokenize($s)); - }; -} - -#[test] -fn test_split() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let module = py.import("dateutil.parser").unwrap(); - let t: PyObject = module.get("_timelex").unwrap().extract().unwrap(); - - // TODO: Fix disagreement about whether or not to replace commas with periods - // test_split!(py, t, "24, 50, ABC"); - test_split!(py, t, "2018.5.15"); - test_split!(py, t, "May 5, 2018"); - test_split!(py, t, "Mar. 5, 2018"); - test_split!(py, t, "19990101T23"); - test_split!(py, t, "19990101T2359"); -} - -macro_rules! py_offset_secs { - ($py:ident, $dt:expr) => ({ - let float: f32 = $dt.getattr($py, "tzinfo") - .expect("Unable to get `tzinfo` value") - .getattr($py, "_offset") - .expect("Unable to get `_offset` value") - .call_method0($py, "total_seconds") - .expect("Unable to call total_seconds()") - .extract($py) - .expect("Unable to extract total_seconds()"); - - float as i32 - }); -} - -macro_rules! rs_tzinfo_map { - () => ({ - let mut h = HashMap::new(); - h.insert("BRST".to_owned(), -10800); - - h - }) -} - -fn parse_and_assert( - py: Python, - parser: &PyModule, - datetime: &PyModule, - info: ParserInfo, - s: &str, - dayfirst: Option, - yearfirst: Option, - fuzzy: bool, - fuzzy_with_tokens: bool, - default: Option<&NaiveDateTime>, - ignoretz: bool, - tzinfos: HashMap, -) { - - let default_pydate = datetime - .call_method1("datetime", (2003, 9, 25)) - .expect("Unable to create default datetime"); - let default_tzinfos = PyDict::new(py); - default_tzinfos.set_item("BRST", -10800).unwrap(); - - let mut kwargs = HashMap::new(); - kwargs.insert("default", default_pydate); - kwargs.insert("tzinfos", default_tzinfos.into()); - kwargs.insert("ignoretz", PyBool::new(py, ignoretz).into()); - - let py_true = PyBool::new(py, true); - if dayfirst == Some(true) { - kwargs.insert("dayfirst", py_true.into()); - } - if yearfirst == Some(true) { - kwargs.insert("yearfirst", py_true.into()); - } - - // pyo3 has issues if we don't sleep here - thread::sleep(Duration::from_millis(5)); - let py_parsed: PyObject = parser - .call_method("parse", s, kwargs) - .expect("Unable to call method `parse`") - .extract() - .expect("Unable to extract result of `parse` call"); - - let mut parser = Parser::new(info); - let rs_parsed = parser.parse( - s, - dayfirst, - yearfirst, - fuzzy, - fuzzy_with_tokens, - default, - ignoretz, - tzinfos).expect(&format!("Unable to parse date in Rust '{}'", s)); - - if let Some(tzoffset) = rs_parsed.1 { - // Make sure the offsets are correct, and then normalize the naive date - // to match the aware date - let offset_secs = py_offset_secs!(py, py_parsed); - - // TODO: Should I be using utc_minus_local instead? - assert_eq!(offset_secs, tzoffset.local_minus_utc(), "Mismatched tzoffset for '{}'", s); - } else { - // First make sure that Python doesn't have any timestamps set - let py_tzoffset = py_parsed - .getattr(py, "tzinfo") - .expect("Unable to get `tzinfo` value"); - - if py_tzoffset != py.None() { - let offset_secs = py_offset_secs!(py, py_parsed); - assert!(false, "Tzinfo had value {} when dtparse didn't detect timezone for '{}'", offset_secs, s); - } - } - - // Naive timestamps - let rs_dt = rs_parsed.0; - - // TODO: Should years by i32? - let py_year: i32 = py_parsed - .getattr(py, "year") - .expect("Unable to get `year` value") - .extract(py) - .expect("Unable to convert `year` to i32"); - assert_eq!(py_year, rs_dt.year(), "Mismatched year for '{}'", s); - - let py_month: u32 = py_parsed - .getattr(py, "month") - .expect("Unable to get `month` value") - .extract(py) - .expect("Unable to convert `month` to u32"); - assert_eq!(py_month, rs_dt.month(), "Mismatched month for '{}'", s); - - let py_day: u32 = py_parsed - .getattr(py, "day") - .expect("Unable to get `day` value") - .extract(py) - .expect("Unable to convert `day` to u32"); - assert_eq!(py_day, rs_dt.day(), "Mismatched day for '{}'", s); - - let py_hour: u32 = py_parsed - .getattr(py, "hour") - .expect("Unable to get `hour` value") - .extract(py) - .expect("Unable to convert `hour` to u32"); - assert_eq!(py_hour, rs_dt.hour(), "Mismatched hour for '{}'", s); - - let py_minute: u32 = py_parsed - .getattr(py, "minute") - .expect("Unable to get `minute` value") - .extract(py) - .expect("Unable to convert `minute` to u32"); - assert_eq!(py_minute, rs_dt.minute(), "Mismatched minute for '{}'", s); - - let py_second: u32 = py_parsed - .getattr(py, "second") - .expect("Unable to get `second` value") - .extract(py) - .expect("Unable to convert `second` to u32"); - assert_eq!(py_second, rs_dt.second(), "Mismatched second for '{}'", s); - - let py_microsecond: u32 = py_parsed - .getattr(py, "microsecond") - .expect("Unable to get `microsecond` value") - .extract(py) - .expect("Unable to convert `microsecond` to u32"); - assert_eq!( - py_microsecond, - rs_dt.nanosecond() / 1000, - "Mismatched microsecond for '{}'", - s - ); -} - -macro_rules! test_parse { - ($py:ident, $parser:ident, $datetime:ident, $s:expr) => { - let info = ParserInfo::default(); - let default_rsdate = &NaiveDate::from_ymd(2003, 9, 25).and_hms(0, 0, 0); - - parse_and_assert( - $py, - $parser, - $datetime, - info, - $s, - None, - None, - false, - false, - Some(default_rsdate), - false, - rs_tzinfo_map!() - ); - }; -} - -macro_rules! test_parse_yearfirst { - ($py:ident, $parser:ident, $datetime:ident, $s:expr) => { - let info = ParserInfo::default(); - let default_rsdate = &NaiveDate::from_ymd(2003, 9, 25).and_hms(0, 0, 0); - - parse_and_assert( - $py, - $parser, - $datetime, - info, - $s, - None, - Some(true), - false, - false, - Some(default_rsdate), - false, - rs_tzinfo_map!() - ); - }; -} - -macro_rules! test_parse_dayfirst { - ($py:ident, $parser:ident, $datetime:ident, $s:expr) => { - let info = ParserInfo::default(); - let default_rsdate = &NaiveDate::from_ymd(2003, 9, 25).and_hms(0, 0, 0); - - parse_and_assert( - $py, - $parser, - $datetime, - info, - $s, - Some(true), - None, - false, - false, - Some(default_rsdate), - false, - rs_tzinfo_map!() - ); - }; -} - -macro_rules! test_parse_ignoretz { - ($py:ident, $parser:ident, $datetime:ident, $s:expr) => { - let info = ParserInfo::default(); - let default_rsdate = &NaiveDate::from_ymd(2003, 9, 25).and_hms(0, 0, 0); - - parse_and_assert( - $py, - $parser, - $datetime, - info, - $s, - None, - None, - false, - false, - Some(default_rsdate), - true, - rs_tzinfo_map!() - ); - }; -} - -#[test] -fn test_dateutil_compat() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let parser = py.import("dateutil.parser").unwrap(); - let datetime = py.import("datetime").unwrap(); - - // testDateCommandFormat - test_parse!(py, parser, datetime, "Thu Sep 25 10:36:28 BRST 2003"); - // testDateCommandFormatReversed - test_parse!(py, parser, datetime, "2003 10:36:28 BRST 25 Sep Thu"); - - // testDateCommandFormatStrip1 - test_parse!(py, parser, datetime, "Thu Sep 25 10:36:28 2003"); - // testDateCommandFormatStrip2 - test_parse!(py, parser, datetime, "Thu Sep 25 10:36:28"); - // testDateCommandFormatStrip3 - test_parse!(py, parser, datetime, "Thu Sep 10:36:28"); - // testDateCommandFormatStrip4 - test_parse!(py, parser, datetime, "Thu 10:36:28"); - // testDateCommandFormatStrip5 - test_parse!(py, parser, datetime, "Sep 10:36:28"); - // testDateCommandFormatStrip6 - test_parse!(py, parser, datetime, "10:36:28"); - // testDateCommandFormatStrip7 - test_parse!(py, parser, datetime, "10:36"); - // testDateCommandFormatStrip8 - test_parse!(py, parser, datetime, "Thu Sep 25 2003"); - // testDateCommandFormatStrip10 - test_parse!(py, parser, datetime, "Sep 2003"); - // testDateCommandFormatStrip11 - test_parse!(py, parser, datetime, "Sep"); - // testDateCommandFormatStrip12 - test_parse!(py, parser, datetime, "2003"); - // testDateRCommandFormat - test_parse!(py, parser, datetime, "Thu, 25 Sep 2003 10:49:41 -0300"); - // testISOFormat - Needs some debug work on the python side - test_parse!(py, parser, datetime, "2003-09-25T10:49:41.5-03:00"); - // testISOFormatStrip1 - Needs same as above - test_parse!(py, parser, datetime, "2003-09-25T10:49:41-03:00"); - // testISOFormatStrip2 - test_parse!(py, parser, datetime, "2003-09-25T10:49:41"); - // testISOFormatStrip3 - test_parse!(py, parser, datetime, "2003-09-25T10:49"); - // testISOFormatStrip4 - test_parse!(py, parser, datetime, "2003-09-25T10"); - // testISOFormatStrip5 - test_parse!(py, parser, datetime, "2003-09-25"); - // testISOStrippedFormat - test_parse!(py, parser, datetime, "20030925T104941.5-0300"); - // testISOStrippedFormatStrip1 - test_parse!(py, parser, datetime, "20030925T104941-0300"); - // testISOStrippedFormatStrip2 - test_parse!(py, parser, datetime, "20030925T104941"); - // testISOStrippedFormatStrip3 - test_parse!(py, parser, datetime, "20030925T1049"); - // testISOStrippedFormatStrip4 - test_parse!(py, parser, datetime, "20030925T10"); - // testISOStrippedFormatStrip5 - test_parse!(py, parser, datetime, "20030925"); - // testPythonLoggerFormat - test_parse!(py, parser, datetime, "2003-09-25 10:49:41,502"); - // testNoSeparator1 - test_parse!(py, parser, datetime, "199709020908"); - // testNoSeparator1 - test_parse!(py, parser, datetime, "19970902090807"); - // testDateWithDash1 - test_parse!(py, parser, datetime, "2003-09-25"); - // testDateWithDash6 - test_parse!(py, parser, datetime, "09-25-2003"); - // testDateWithDash7 - test_parse!(py, parser, datetime, "25-09-2003"); - // testDateWithDash8 - test_parse_dayfirst!(py, parser, datetime, "10-09-2003"); - // testDateWithDash9 - test_parse!(py, parser, datetime, "10-09-2003"); - // testDateWithDash10 - test_parse!(py, parser, datetime, "10-09-03"); - // testDateWithDash11 - test_parse_yearfirst!(py, parser, datetime, "10-09-03"); - // testDateWithDot1 - test_parse!(py, parser, datetime, "2003.09.25"); - // testDateWithDot6 - test_parse!(py, parser, datetime, "09.25.2003"); - // testDateWithDot7 - test_parse!(py, parser, datetime, "25.09.2003"); - // testDateWithDot8 - test_parse_dayfirst!(py, parser, datetime, "10.09.2003"); - // testDateWithDot9 - test_parse!(py, parser, datetime, "10.09.2003"); - // testDateWithDot10 - test_parse!(py, parser, datetime, "10.09.03"); - // testDateWithDot11 - test_parse_yearfirst!(py, parser, datetime, "10.09.03"); - // testDateWithSlash1 - test_parse!(py, parser, datetime, "2003/09/25"); - // testDateWithSlash6 - test_parse!(py, parser, datetime, "09/25/2003"); - // testDateWithSlash7 - test_parse!(py, parser, datetime, "25/09/2003"); - // testDateWithSlash8 - test_parse_dayfirst!(py, parser, datetime, "10/09/2003"); - // testDateWithSlash9 - test_parse!(py, parser, datetime, "10/09/2003"); - // testDateWithSlash10 - test_parse!(py, parser, datetime, "10/09/03"); - // testDateWithSlash11 - test_parse_yearfirst!(py, parser, datetime, "10/09/03"); - // testDateWithSpace1 - test_parse!(py, parser, datetime, "2003 09 25"); - // testDateWithSpace6 - test_parse!(py, parser, datetime, "09 25 2003"); - // testDateWithSpace7 - test_parse!(py, parser, datetime, "25 09 2003"); - // testDateWithSpace8 - test_parse_dayfirst!(py, parser, datetime, "10 09 2003"); - // testDateWithSpace9 - test_parse!(py, parser, datetime, "10 09 2003"); - // testDateWithSpace10 - test_parse!(py, parser, datetime, "10 09 03"); - // testDateWithSpace11 - test_parse_yearfirst!(py, parser, datetime, "10 09 03"); - // testDateWithSpace12 - test_parse!(py, parser, datetime, "25 09 03"); - // testStrangelyOrderedDate1 - test_parse!(py, parser, datetime, "03 25 Sep"); - // testStrangelyOrderedDate3 - test_parse!(py, parser, datetime, "25 03 Sep"); - // testHourWithLetters - test_parse!(py, parser, datetime, "10h36m28.5s"); - // testHourWithLettersStrip1 - test_parse!(py, parser, datetime, "10h36m28s"); - // testHourWithLettersStrip2 - test_parse!(py, parser, datetime, "10h36m"); - // testHourWithLettersStrip3 - test_parse!(py, parser, datetime, "10h"); - // testHourWithLettersStrip4 - test_parse!(py, parser, datetime, "10 h 36"); - - // testHourWithLettersStrip5 - test_parse!(py, parser, datetime, "10 h 36.5"); - - // testMinuteWithLettersSpaces1 - test_parse!(py, parser, datetime, "36 m 5"); - // testMinuteWithLettersSpaces2 - test_parse!(py, parser, datetime, "36 m 5 s"); - // testMinuteWithLettersSpaces3 - test_parse!(py, parser, datetime, "36 m 05"); - // testMinuteWithLettersSpaces4 - test_parse!(py, parser, datetime, "36 m 05 s"); - - // TODO: Add testAMPMNoHour - - // testHourAmPm1 - test_parse!(py, parser, datetime, "10h am"); - // testHourAmPm2 - test_parse!(py, parser, datetime, "10h pm"); - // testHourAmPm3 - test_parse!(py, parser, datetime, "10am"); - // testHourAmPm4 - test_parse!(py, parser, datetime, "10pm"); - // testHourAmPm5 - test_parse!(py, parser, datetime, "10:00 am"); - // testHourAmPm6 - test_parse!(py, parser, datetime, "10:00 pm"); - // testHourAmPm7 - test_parse!(py, parser, datetime, "10:00am"); - // testHourAmPm8 - test_parse!(py, parser, datetime, "10:00pm"); - // testHourAmPm9 - test_parse!(py, parser, datetime, "10:00a.m"); - // testHourAmPm10 - test_parse!(py, parser, datetime, "10:00p.m"); - // testHourAmPm11 - test_parse!(py, parser, datetime, "10:00a.m."); - // testHourAmPm12 - test_parse!(py, parser, datetime, "10:00p.m."); - - // TODO: Add testAMPMRange - - // testPertain - test_parse!(py, parser, datetime, "Sep 03"); - test_parse!(py, parser, datetime, "Sep of 03"); - - // TODO: Handle weekdays, rather than absolute days - // testWeekdayAlone - // test_parse!(py, parser, datetime, "Wed"); - // testLongWeekday - // test_parse!(py, parser, datetime, "Wednesday"); - - // testLongMonth - test_parse!(py, parser, datetime, "October"); - // testZeroYear - test_parse!(py, parser, datetime, "31-Dec-00"); - - // TODO: Handle fuzzy tests - - // testExtraSpace - test_parse!(py, parser, datetime, " July 4 , 1976 12:01:02 am "); - - // testRandomFormat1 - test_parse!(py, parser, datetime, "Wed, July 10, '96"); - // testRandomFormat2 - test_parse_ignoretz!(py, parser, datetime, "1996.07.10 AD at 15:08:56 PDT"); - - // testRandomFormat3 - test_parse!(py, parser, datetime, "1996.July.10 AD 12:08 PM"); - - // testRandomFormat4 - test_parse_ignoretz!(py, parser, datetime, "Tuesday, April 12, 1952 AD 3:30:42pm PST"); - // testRandomFormat5 - test_parse_ignoretz!(py, parser, datetime, "November 5, 1994, 8:15:30 am EST"); - // testRandomFormat6 - test_parse_ignoretz!(py, parser, datetime, "1994-11-05T08:15:30-05:00"); - // testRandomFormat7 - test_parse_ignoretz!(py, parser, datetime, "1994-11-05T08:15:30Z"); - // testRandomFormat8 - test_parse!(py, parser, datetime, "July 4, 1976"); - // testRandomFormat9 - test_parse!(py, parser, datetime, "7 4 1976"); - // testRandomFormat10 - test_parse!(py, parser, datetime, "4 jul 1976"); - // testRandomFormat11 - test_parse!(py, parser, datetime, "7-4-76"); - // testRandomFormat12 - test_parse!(py, parser, datetime, "19760704"); - // testRandomFormat13 - test_parse!(py, parser, datetime, "0:01:02"); - // testRandomFormat14 - test_parse!(py, parser, datetime, "12h 01m02s am"); - // testRandomFormat15; NB: testRandomFormat16 is exactly the same - test_parse!(py, parser, datetime, "0:01:02 on July 4, 1976"); - // testRandomFormat17 - test_parse_ignoretz!(py, parser, datetime, "1976-07-04T00:01:02Z"); - // testRandomFormat18 - test_parse!(py, parser, datetime, "July 4, 1976 12:01:02 am"); - // testRandomFormat19 - test_parse!(py, parser, datetime, "Mon Jan 2 04:24:27 1995"); - // testRandomFormat20 - test_parse_ignoretz!(py, parser, datetime, "Tue Apr 4 00:22:12 PDT 1995"); - // testRandomFormat21 - test_parse!(py, parser, datetime, "04.04.95 00:22"); - // testRandomFormat22 - test_parse!(py, parser, datetime, "Jan 1 1999 11:23:34.578"); - // testRandomFormat23 - test_parse!(py, parser, datetime, "950404 122212"); - // testRandomFormat24 - test_parse_ignoretz!(py, parser, datetime, "0:00 PM, PST"); - // testRandomFormat25 - test_parse!(py, parser, datetime, "12:08 PM"); - // testRandomFormat26 - test_parse!(py, parser, datetime, "5:50 A.M. on June 13, 1990"); - // testRandomFormat27 - test_parse!(py, parser, datetime, "3rd of May 2001"); - // testRandomFormat28 - test_parse!(py, parser, datetime, "5th of March 2001"); - // testRandomFormat29 - test_parse!(py, parser, datetime, "1st of May 2003"); - // testRandomFormat30 - test_parse!(py, parser, datetime, "01h02m03"); - // testRandomFormat31 - test_parse!(py, parser, datetime, "01h02"); - // testRandomFormat32 - test_parse!(py, parser, datetime, "01h02s"); - // testRandomFormat33 - test_parse!(py, parser, datetime, "01m02"); - // testRandomFormat34 - test_parse!(py, parser, datetime, "01m02h"); - // testRandomFormat35 - test_parse!(py, parser, datetime, "2004 10 Apr 11h30m"); -} diff --git a/tests/pycompat.rs b/tests/pycompat.rs new file mode 100644 index 0000000..1df3e8b --- /dev/null +++ b/tests/pycompat.rs @@ -0,0 +1,1352 @@ + +extern crate chrono; + +use chrono::Datelike; +use chrono::NaiveDate; +use chrono::NaiveDateTime; +use chrono::Timelike; +use std::collections::HashMap; + +extern crate dtparse; + +use dtparse::Parser; +use dtparse::ParserInfo; + +struct PyDateTime { + year: i32, + month: u32, + day: u32, + hour: u32, + minute: u32, + second: u32, + micros: u32, + tzo: Option +} + +fn parse_and_assert( + pdt: PyDateTime, + info: ParserInfo, + s: &str, + dayfirst: Option, + yearfirst: Option, + fuzzy: bool, + fuzzy_with_tokens: bool, + default: Option<&NaiveDateTime>, + ignoretz: bool, + tzinfos: HashMap, +) { + + let mut parser = Parser::new(info); + let rs_parsed = parser.parse( + s, + dayfirst, + yearfirst, + fuzzy, + fuzzy_with_tokens, + default, + ignoretz, + tzinfos).expect(&format!("Unable to parse date in Rust '{}'", s)); + + assert_eq!(pdt.year, rs_parsed.0.year(), "Year mismatch for {}", s); + assert_eq!(pdt.month, rs_parsed.0.month(), "Month mismatch for {}", s); + assert_eq!(pdt.day, rs_parsed.0.day(), "Day mismatch for {}", s); + assert_eq!(pdt.hour, rs_parsed.0.hour(), "Hour mismatch for {}", s); + assert_eq!(pdt.minute, rs_parsed.0.minute(), "Minute mismatch for {}", s); + assert_eq!(pdt.second, rs_parsed.0.second(), "Second mismatch for {}", s); + assert_eq!(pdt.micros, rs_parsed.0.timestamp_subsec_micros(), "Microsecond mismatch for {}", s); + assert_eq!(pdt.tzo, rs_parsed.1.map(|u| u.local_minus_utc()), "Timezone Offset mismatch for {}", s); +} + +macro_rules! rs_tzinfo_map { + () => ({ + let mut h = HashMap::new(); + h.insert("BRST".to_owned(), -10800); + + h + }); +} + +macro_rules! test_parse { + ($pdt:expr, $s:expr) => { + let info = ParserInfo::default(); + let default_rsdate = &NaiveDate::from_ymd(2003, 9, 25).and_hms(0, 0, 0); + + parse_and_assert( + $pdt, + info, + $s, + None, + None, + false, + false, + Some(default_rsdate), + false, + rs_tzinfo_map!() + ); + }; +} + +macro_rules! test_parse_yearfirst { + ($pdt:expr, $s:expr) => { + let info = ParserInfo::default(); + let default_rsdate = &NaiveDate::from_ymd(2003, 9, 25).and_hms(0, 0, 0); + + parse_and_assert( + $pdt, + info, + $s, + None, + Some(true), + false, + false, + Some(default_rsdate), + false, + rs_tzinfo_map!() + ); + }; +} + +macro_rules! test_parse_dayfirst { + ($pdt:expr, $s:expr) => { + let info = ParserInfo::default(); + let default_rsdate = &NaiveDate::from_ymd(2003, 9, 25).and_hms(0, 0, 0); + + parse_and_assert( + $pdt, + info, + $s, + Some(true), + None, + false, + false, + Some(default_rsdate), + false, + rs_tzinfo_map!() + ); + }; +} + +macro_rules! test_parse_yfdf { + ($pdt:expr, $s:expr) => { + let info = ParserInfo::default(); + let default_rsdate = &NaiveDate::from_ymd(2003, 9, 25).and_hms(0, 0, 0); + + parse_and_assert( + $pdt, + info, + $s, + Some(true), + None, + true, + true, + Some(default_rsdate), + false, + rs_tzinfo_map!() + ); + }; +} + +macro_rules! test_parse_ignoretz { + ($pdt:expr, $s:expr) => { + let info = ParserInfo::default(); + let default_rsdate = &NaiveDate::from_ymd(2003, 9, 25).and_hms(0, 0, 0); + + parse_and_assert( + $pdt, + info, + $s, + None, + None, + false, + false, + Some(default_rsdate), + true, + rs_tzinfo_map!() + ); + }; +} + +#[test] +fn test_parse0() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 10, + minute: 36, + second: 28, + micros: 0, + tzo: Some(-10800), + }; + test_parse!(pdt, "Thu Sep 25 10:36:28 BRST 2003"); +} + +#[test] +fn test_parse1() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "Thu Sep 25 2003"); +} + +#[test] +fn test_parse2() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 10, + minute: 49, + second: 41, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "2003-09-25T10:49:41"); +} + +#[test] +fn test_parse3() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 10, + minute: 49, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "2003-09-25T10:49"); +} + +#[test] +fn test_parse4() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 10, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "2003-09-25T10"); +} + +#[test] +fn test_parse5() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "2003-09-25"); +} + +#[test] +fn test_parse6() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 10, + minute: 49, + second: 41, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "20030925T104941"); +} + +#[test] +fn test_parse7() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 10, + minute: 49, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "20030925T1049"); +} + +#[test] +fn test_parse8() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 10, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "20030925T10"); +} + +#[test] +fn test_parse9() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "20030925"); +} + +#[test] +fn test_parse10() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 10, + minute: 49, + second: 41, + micros: 502000, + tzo: None, + }; + test_parse!(pdt, "2003-09-25 10:49:41,502"); +} + +#[test] +fn test_parse11() { + let pdt = PyDateTime { + year: 1997, + month: 9, + day: 2, + hour: 9, + minute: 8, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "199709020908"); +} + +#[test] +fn test_parse12() { + let pdt = PyDateTime { + year: 1997, + month: 9, + day: 2, + hour: 9, + minute: 8, + second: 7, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "19970902090807"); +} + +#[test] +fn test_parse13() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "2003-09-25"); +} + +#[test] +fn test_parse14() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "09-25-2003"); +} + +#[test] +fn test_parse15() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "25-09-2003"); +} + +#[test] +fn test_parse16() { + let pdt = PyDateTime { + year: 2003, + month: 10, + day: 9, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "10-09-2003"); +} + +#[test] +fn test_parse17() { + let pdt = PyDateTime { + year: 2003, + month: 10, + day: 9, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "10-09-03"); +} + +#[test] +fn test_parse18() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "2003.09.25"); +} + +#[test] +fn test_parse19() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "09.25.2003"); +} + +#[test] +fn test_parse20() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "25.09.2003"); +} + +#[test] +fn test_parse21() { + let pdt = PyDateTime { + year: 2003, + month: 10, + day: 9, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "10.09.2003"); +} + +#[test] +fn test_parse22() { + let pdt = PyDateTime { + year: 2003, + month: 10, + day: 9, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "10.09.03"); +} + +#[test] +fn test_parse23() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "2003/09/25"); +} + +#[test] +fn test_parse24() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "09/25/2003"); +} + +#[test] +fn test_parse25() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "25/09/2003"); +} + +#[test] +fn test_parse26() { + let pdt = PyDateTime { + year: 2003, + month: 10, + day: 9, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "10/09/2003"); +} + +#[test] +fn test_parse27() { + let pdt = PyDateTime { + year: 2003, + month: 10, + day: 9, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "10/09/03"); +} + +#[test] +fn test_parse28() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "2003 09 25"); +} + +#[test] +fn test_parse29() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "09 25 2003"); +} + +#[test] +fn test_parse30() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "25 09 2003"); +} + +#[test] +fn test_parse31() { + let pdt = PyDateTime { + year: 2003, + month: 10, + day: 9, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "10 09 2003"); +} + +#[test] +fn test_parse32() { + let pdt = PyDateTime { + year: 2003, + month: 10, + day: 9, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "10 09 03"); +} + +#[test] +fn test_parse33() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "25 09 03"); +} + +#[test] +fn test_parse34() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "03 25 Sep"); +} + +#[test] +fn test_parse35() { + let pdt = PyDateTime { + year: 2025, + month: 9, + day: 3, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "25 03 Sep"); +} + +#[test] +fn test_parse36() { + let pdt = PyDateTime { + year: 1976, + month: 7, + day: 4, + hour: 0, + minute: 1, + second: 2, + micros: 0, + tzo: None, + }; + test_parse!(pdt, " July 4 , 1976 12:01:02 am "); +} + +#[test] +fn test_parse37() { + let pdt = PyDateTime { + year: 1996, + month: 7, + day: 10, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "Wed, July 10, '96"); +} + +#[test] +fn test_parse38() { + let pdt = PyDateTime { + year: 1996, + month: 7, + day: 10, + hour: 12, + minute: 8, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "1996.July.10 AD 12:08 PM"); +} + +#[test] +fn test_parse39() { + let pdt = PyDateTime { + year: 1976, + month: 7, + day: 4, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "July 4, 1976"); +} + +#[test] +fn test_parse40() { + let pdt = PyDateTime { + year: 1976, + month: 7, + day: 4, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "7 4 1976"); +} + +#[test] +fn test_parse41() { + let pdt = PyDateTime { + year: 1976, + month: 7, + day: 4, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "4 jul 1976"); +} + +#[test] +fn test_parse42() { + let pdt = PyDateTime { + year: 1976, + month: 7, + day: 4, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "7-4-76"); +} + +#[test] +fn test_parse43() { + let pdt = PyDateTime { + year: 1976, + month: 7, + day: 4, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "19760704"); +} + +#[test] +fn test_parse44() { + let pdt = PyDateTime { + year: 1976, + month: 7, + day: 4, + hour: 0, + minute: 1, + second: 2, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "0:01:02 on July 4, 1976"); +} + +#[test] +fn test_parse45() { + let pdt = PyDateTime { + year: 1976, + month: 7, + day: 4, + hour: 0, + minute: 1, + second: 2, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "0:01:02 on July 4, 1976"); +} + +#[test] +fn test_parse46() { + let pdt = PyDateTime { + year: 1976, + month: 7, + day: 4, + hour: 0, + minute: 1, + second: 2, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "July 4, 1976 12:01:02 am"); +} + +#[test] +fn test_parse47() { + let pdt = PyDateTime { + year: 1995, + month: 1, + day: 2, + hour: 4, + minute: 24, + second: 27, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "Mon Jan 2 04:24:27 1995"); +} + +#[test] +fn test_parse48() { + let pdt = PyDateTime { + year: 1995, + month: 4, + day: 4, + hour: 0, + minute: 22, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "04.04.95 00:22"); +} + +#[test] +fn test_parse49() { + let pdt = PyDateTime { + year: 1999, + month: 1, + day: 1, + hour: 11, + minute: 23, + second: 34, + micros: 578000, + tzo: None, + }; + test_parse!(pdt, "Jan 1 1999 11:23:34.578"); +} + +#[test] +fn test_parse50() { + let pdt = PyDateTime { + year: 1995, + month: 4, + day: 4, + hour: 12, + minute: 22, + second: 12, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "950404 122212"); +} + +#[test] +fn test_parse51() { + let pdt = PyDateTime { + year: 2001, + month: 5, + day: 3, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "3rd of May 2001"); +} + +#[test] +fn test_parse52() { + let pdt = PyDateTime { + year: 2001, + month: 3, + day: 5, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "5th of March 2001"); +} + +#[test] +fn test_parse53() { + let pdt = PyDateTime { + year: 2003, + month: 5, + day: 1, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "1st of May 2003"); +} + +#[test] +fn test_parse54() { + let pdt = PyDateTime { + year: 2008, + month: 2, + day: 27, + hour: 21, + minute: 26, + second: 1, + micros: 123456, + tzo: None, + }; + test_parse!(pdt, "20080227T21:26:01.123456789"); +} + +#[test] +fn test_parse55() { + let pdt = PyDateTime { + year: 2017, + month: 11, + day: 13, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "13NOV2017"); +} + +#[test] +fn test_parse56() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 3, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "Sep 03"); +} + +#[test] +fn test_parse57() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "Sep of 03"); +} + +#[test] +fn test_parse58() { + let pdt = PyDateTime { + year: 2009, + month: 4, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "April 2009"); +} + +#[test] +fn test_parse59() { + let pdt = PyDateTime { + year: 2007, + month: 2, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "Feb 2007"); +} + +#[test] +fn test_parse60() { + let pdt = PyDateTime { + year: 2008, + month: 2, + day: 25, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse!(pdt, "Feb 2008"); +} + +#[test] +fn test_parse_yearfirst0() { + let pdt = PyDateTime { + year: 2010, + month: 9, + day: 3, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse_yearfirst!(pdt, "10-09-03"); +} + +#[test] +fn test_parse_yearfirst1() { + let pdt = PyDateTime { + year: 2010, + month: 9, + day: 3, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse_yearfirst!(pdt, "10.09.03"); +} + +#[test] +fn test_parse_yearfirst2() { + let pdt = PyDateTime { + year: 2010, + month: 9, + day: 3, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse_yearfirst!(pdt, "10/09/03"); +} + +#[test] +fn test_parse_yearfirst3() { + let pdt = PyDateTime { + year: 2010, + month: 9, + day: 3, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse_yearfirst!(pdt, "10 09 03"); +} + +#[test] +fn test_parse_yearfirst4() { + let pdt = PyDateTime { + year: 2009, + month: 1, + day: 7, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse_yearfirst!(pdt, "090107"); +} + +#[test] +fn test_parse_dayfirst0() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 10, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse_dayfirst!(pdt, "10-09-2003"); +} + +#[test] +fn test_parse_dayfirst1() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 10, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse_dayfirst!(pdt, "10.09.2003"); +} + +#[test] +fn test_parse_dayfirst2() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 10, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse_dayfirst!(pdt, "10/09/2003"); +} + +#[test] +fn test_parse_dayfirst3() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 10, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse_dayfirst!(pdt, "10 09 2003"); +} + +#[test] +fn test_parse_dayfirst4() { + let pdt = PyDateTime { + year: 2007, + month: 1, + day: 9, + hour: 0, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse_dayfirst!(pdt, "090107"); +} + +#[test] +fn test_parse_ignoretz0() { + let pdt = PyDateTime { + year: 1996, + month: 7, + day: 10, + hour: 15, + minute: 8, + second: 56, + micros: 0, + tzo: None, + }; + test_parse_ignoretz!(pdt, "1996.07.10 AD at 15:08:56 PDT"); +} + +#[test] +fn test_parse_ignoretz1() { + let pdt = PyDateTime { + year: 1952, + month: 4, + day: 12, + hour: 15, + minute: 30, + second: 52, + micros: 0, + tzo: None, + }; + test_parse_ignoretz!(pdt, "Tuesday, April 12, 1952 AD 3:30:52pm PST"); +} + +#[test] +fn test_parse_ignoretz2() { + let pdt = PyDateTime { + year: 1994, + month: 11, + day: 5, + hour: 8, + minute: 15, + second: 30, + micros: 0, + tzo: None, + }; + test_parse_ignoretz!(pdt, "November 5, 1994, 8:15:30 am EST"); +} + +#[test] +fn test_parse_ignoretz3() { + let pdt = PyDateTime { + year: 1994, + month: 11, + day: 5, + hour: 8, + minute: 15, + second: 30, + micros: 0, + tzo: None, + }; + test_parse_ignoretz!(pdt, "1994-11-05T08:15:30-05:00"); +} + +#[test] +fn test_parse_ignoretz4() { + let pdt = PyDateTime { + year: 1994, + month: 11, + day: 5, + hour: 8, + minute: 15, + second: 30, + micros: 0, + tzo: None, + }; + test_parse_ignoretz!(pdt, "1994-11-05T08:15:30Z"); +} + +#[test] +fn test_parse_ignoretz5() { + let pdt = PyDateTime { + year: 1976, + month: 7, + day: 4, + hour: 0, + minute: 1, + second: 2, + micros: 0, + tzo: None, + }; + test_parse_ignoretz!(pdt, "1976-07-04T00:01:02Z"); +} + +#[test] +fn test_parse_ignoretz6() { + let pdt = PyDateTime { + year: 1995, + month: 4, + day: 4, + hour: 0, + minute: 22, + second: 12, + micros: 0, + tzo: None, + }; + test_parse_ignoretz!(pdt, "Tue Apr 4 00:22:12 PDT 1995"); +} + +#[test] +fn test_parse_ignoretz7() { + let pdt = PyDateTime { + year: 2003, + month: 9, + day: 25, + hour: 12, + minute: 0, + second: 0, + micros: 0, + tzo: None, + }; + test_parse_ignoretz!(pdt, "0:00PM, PST"); +}