Fix yearfirst/dayfirst support

pull/1/head
Bradlee Speice 2018-06-03 16:11:51 -04:00
parent d49a12d947
commit 451aa2aeb5
2 changed files with 158 additions and 61 deletions

View File

@ -745,14 +745,22 @@ pub struct ParsingResult {
} }
#[derive(Default)] #[derive(Default)]
struct Parser { pub struct Parser {
info: ParserInfo, info: ParserInfo,
} }
impl Parser { impl Parser {
pub fn new(info: ParserInfo) -> Self {
Parser { info }
}
pub fn parse( pub fn parse(
&mut self, &mut self,
timestr: &str, timestr: &str,
dayfirst: Option<bool>,
yearfirst: Option<bool>,
fuzzy: bool,
fuzzy_with_tokens: bool,
default: Option<&NaiveDateTime>, default: Option<&NaiveDateTime>,
ignoretz: bool, ignoretz: bool,
tzinfos: Vec<String>, tzinfos: Vec<String>,
@ -762,7 +770,8 @@ impl Parser {
let default_ts = NaiveDateTime::new(default_date, NaiveTime::from_hms(0, 0, 0)); let default_ts = NaiveDateTime::new(default_date, NaiveTime::from_hms(0, 0, 0));
// TODO: What should be done with the tokens? // TODO: What should be done with the tokens?
let (res, tokens) = self.parse_with_tokens(timestr, None, None, false, false)?; let (res, tokens) =
self.parse_with_tokens(timestr, dayfirst, yearfirst, fuzzy, fuzzy_with_tokens)?;
let naive = self.build_naive(&res, &default_ts); let naive = self.build_naive(&res, &default_ts);
@ -1273,25 +1282,8 @@ fn ljust(s: &str, chars: usize, replace: char) -> String {
} }
} }
pub fn parse_with_info(
timestr: &str,
info: ParserInfo,
default: Option<&NaiveDateTime>,
) -> ParseResult<(NaiveDateTime, Option<FixedOffset>, Option<Vec<String>>)> {
// TODO: Is `::new()` more stylistic?
let mut parser = Parser { info: info };
parser.parse(timestr, default, false, vec![])
}
pub fn parse_with_default(
timestr: &str,
default: &NaiveDateTime,
) -> ParseResult<(NaiveDateTime, Option<FixedOffset>)> {
let parse_result = parse_with_info(timestr, ParserInfo::default(), Some(default))?;
Ok((parse_result.0, parse_result.1))
}
pub fn parse(timestr: &str) -> ParseResult<(NaiveDateTime, Option<FixedOffset>)> { pub fn parse(timestr: &str) -> ParseResult<(NaiveDateTime, Option<FixedOffset>)> {
let parse_result = parse_with_info(timestr, ParserInfo::default(), None)?; let res = Parser::default().parse(timestr, None, None, false, false, None, false, vec![])?;
Ok((parse_result.0, parse_result.1))
Ok((res.0, res.1))
} }

View File

@ -5,6 +5,7 @@ use chrono::Datelike;
use chrono::NaiveDate; use chrono::NaiveDate;
use chrono::Timelike; use chrono::Timelike;
use pyo3::ObjectProtocol; use pyo3::ObjectProtocol;
use pyo3::PyBool;
use pyo3::PyDict; use pyo3::PyDict;
use pyo3::PyList; use pyo3::PyList;
use pyo3::PyObject; use pyo3::PyObject;
@ -13,7 +14,8 @@ use std::collections::HashMap;
extern crate dtparse; extern crate dtparse;
use dtparse::parse_with_default; use dtparse::Parser;
use dtparse::ParserInfo;
use dtparse::tokenize; use dtparse::tokenize;
macro_rules! test_split { macro_rules! test_split {
@ -44,7 +46,21 @@ fn test_split() {
} }
macro_rules! test_parse { macro_rules! test_parse {
($py:ident, $parser:ident, $datetime:ident, $s:expr) => { // Full parsing options
(
$py:ident,
$parser:ident,
$datetime:ident,
$info:expr,
$s:expr,
$dayfirst:expr,
$yearfirst:expr,
$fuzzy:expr,
$fuzzy_with_tokens:expr,
$default:expr,
$ignoretz:expr,
$tzinfos:expr
) => {
let default_pydate = $datetime let default_pydate = $datetime
.call_method1("datetime", (2003, 9, 25)) .call_method1("datetime", (2003, 9, 25))
.expect("Unable to create default datetime"); .expect("Unable to create default datetime");
@ -55,15 +71,30 @@ macro_rules! test_parse {
kwargs.insert("default", default_pydate); kwargs.insert("default", default_pydate);
kwargs.insert("tzinfos", default_tzinfos.into()); kwargs.insert("tzinfos", default_tzinfos.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());
}
let py_parsed: PyObject = $parser let py_parsed: PyObject = $parser
.call_method("parse", $s, kwargs) .call_method("parse", $s, kwargs)
.expect("Unable to call method `parse`") .expect("Unable to call method `parse`")
.extract() .extract()
.expect("Unable to extract result of `parse` call"); .expect("Unable to extract result of `parse` call");
let default_rsdate = &NaiveDate::from_ymd(2003, 9, 25).and_hms(0, 0, 0); let mut parser = Parser::new($info);
let rs_parsed = let rs_parsed = parser.parse(
parse_with_default($s, default_rsdate).expect("Unable to parse date in Rust"); $s,
$dayfirst,
$yearfirst,
$fuzzy,
$fuzzy_with_tokens,
$default,
$ignoretz,
$tzinfos).expect("Unable to parse date in Rust");
if let Some(_offset) = rs_parsed.1 { if let Some(_offset) = rs_parsed.1 {
// TODO: Handle tests involving timezones // TODO: Handle tests involving timezones
@ -125,9 +156,83 @@ macro_rules! test_parse {
.expect("Unable to get `microsecond` value") .expect("Unable to get `microsecond` value")
.extract($py) .extract($py)
.expect("Unable to convert `microsecond` to u32"); .expect("Unable to convert `microsecond` to u32");
assert_eq!(py_microsecond, rs_dt.nanosecond() / 1000, "Mismatched microsecond for '{}'", $s); assert_eq!(
py_microsecond,
rs_dt.nanosecond() / 1000,
"Mismatched microsecond for '{}'",
$s
);
} }
}; };
(
$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);
test_parse!(
$py,
$parser,
$datetime,
info,
$s,
None,
None,
false,
false,
Some(default_rsdate),
false,
vec![]
);
};
}
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);
test_parse!(
$py,
$parser,
$datetime,
info,
$s,
None,
Some(true),
false,
false,
Some(default_rsdate),
false,
vec![]
);
};
}
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);
test_parse!(
$py,
$parser,
$datetime,
info,
$s,
Some(true),
None,
false,
false,
Some(default_rsdate),
false,
vec![]
);
};
} }
#[test] #[test]
@ -204,56 +309,56 @@ fn test_dateutil_compat() {
test_parse!(py, parser, datetime, "09-25-2003"); test_parse!(py, parser, datetime, "09-25-2003");
// testDateWithDash7 // testDateWithDash7
test_parse!(py, parser, datetime, "25-09-2003"); test_parse!(py, parser, datetime, "25-09-2003");
// testDateWithDash8 - Needs `dayfirst` support // testDateWithDash8
// test_parse!(py, parser, datetime, "10-09-2003"); test_parse_dayfirst!(py, parser, datetime, "10-09-2003");
// testDateWithDash9 // testDateWithDash9
test_parse!(py, parser, datetime, "10-09-2003"); test_parse!(py, parser, datetime, "10-09-2003");
// testDateWithDash10 // testDateWithDash10
test_parse!(py, parser, datetime, "10-09-03"); test_parse!(py, parser, datetime, "10-09-03");
// testDateWithDash11 - Needs `yearfirst` support // testDateWithDash11
// test_parse!(py, parser, datetime, "10-09-03") test_parse_yearfirst!(py, parser, datetime, "10-09-03");
// testDateWithDot1 // testDateWithDot1
test_parse!(py, parser, datetime, "2003.09.25"); test_parse!(py, parser, datetime, "2003.09.25");
// testDateWithDot6 // testDateWithDot6
test_parse!(py, parser, datetime, "09.25.2003"); test_parse!(py, parser, datetime, "09.25.2003");
// testDateWithDot7 // testDateWithDot7
test_parse!(py, parser, datetime, "25.09.2003"); test_parse!(py, parser, datetime, "25.09.2003");
// testDateWithDot8 - Needs `dayfirst` support // testDateWithDot8
// test_parse!(py, parser, datetime, "10.09.2003"); test_parse_dayfirst!(py, parser, datetime, "10.09.2003");
// testDateWithDot9 // testDateWithDot9
test_parse!(py, parser, datetime, "10.09.2003"); test_parse!(py, parser, datetime, "10.09.2003");
// testDateWithDot10 // testDateWithDot10
test_parse!(py, parser, datetime, "10.09.03"); test_parse!(py, parser, datetime, "10.09.03");
// testDateWithDot11 - Needs `yearfirst` support // testDateWithDot11
// test_parse!(py, parser, datetime, "10.09.03"); test_parse_yearfirst!(py, parser, datetime, "10.09.03");
// testDateWithSlash1 // testDateWithSlash1
test_parse!(py, parser, datetime, "2003/09/25"); test_parse!(py, parser, datetime, "2003/09/25");
// testDateWithSlash6 // testDateWithSlash6
test_parse!(py, parser, datetime, "09/25/2003"); test_parse!(py, parser, datetime, "09/25/2003");
// testDateWithSlash7 // testDateWithSlash7
test_parse!(py, parser, datetime, "25/09/2003"); test_parse!(py, parser, datetime, "25/09/2003");
// testDateWithSlash8 - Needs `dayfirst` support // testDateWithSlash8
// test_parse!(py, parser, datetime, "10/09/2003"); test_parse_dayfirst!(py, parser, datetime, "10/09/2003");
// testDateWithSlash9 // testDateWithSlash9
test_parse!(py, parser, datetime, "10/09/2003"); test_parse!(py, parser, datetime, "10/09/2003");
// testDateWithSlash10 // testDateWithSlash10
test_parse!(py, parser, datetime, "10/09/03"); test_parse!(py, parser, datetime, "10/09/03");
// testDateWithSlash11 - Needs `yearfirst` support // testDateWithSlash11
// test_parse!(py, parser, datetime, "10/09/03"); test_parse_yearfirst!(py, parser, datetime, "10/09/03");
// testDateWithSpace1 // testDateWithSpace1
test_parse!(py, parser, datetime, "2003 09 25"); test_parse!(py, parser, datetime, "2003 09 25");
// testDateWithSpace6 // testDateWithSpace6
test_parse!(py, parser, datetime, "09 25 2003"); test_parse!(py, parser, datetime, "09 25 2003");
// testDateWithSpace7 // testDateWithSpace7
test_parse!(py, parser, datetime, "25 09 2003"); test_parse!(py, parser, datetime, "25 09 2003");
// testDateWithSpace8 - Needs `dayfirst` support // testDateWithSpace8
// test_parse!(py, parser, datetime, "10 09 2003"); test_parse_dayfirst!(py, parser, datetime, "10 09 2003");
// testDateWithSpace9 // testDateWithSpace9
test_parse!(py, parser, datetime, "10 09 2003"); test_parse!(py, parser, datetime, "10 09 2003");
// testDateWithSpace10 // testDateWithSpace10
test_parse!(py, parser, datetime, "10 09 03"); test_parse!(py, parser, datetime, "10 09 03");
// testDateWithSpace11 - Needs `yearfirst` support // testDateWithSpace11
// test_parse!(py, parser, datetime, "10 09 03"); test_parse_yearfirst!(py, parser, datetime, "10 09 03");
// testDateWithSpace12 // testDateWithSpace12
test_parse!(py, parser, datetime, "25 09 03"); test_parse!(py, parser, datetime, "25 09 03");
// testStrangelyOrderedDate1 // testStrangelyOrderedDate1
@ -273,8 +378,8 @@ fn test_dateutil_compat() {
// TODO: Fix half a minute being 30 seconds // TODO: Fix half a minute being 30 seconds
// testHourWithLettersStrip5 // testHourWithLettersStrip5
// test_parse!(py, parser, datetime, "10 h 36.5"); // test_parse!(py, parser, datetime, "10 h 36.5");
// testMinuteWithLettersSpaces1 // testMinuteWithLettersSpaces1
test_parse!(py, parser, datetime, "36 m 5"); test_parse!(py, parser, datetime, "36 m 5");
// testMinuteWithLettersSpaces2 // testMinuteWithLettersSpaces2
@ -335,20 +440,20 @@ fn test_dateutil_compat() {
// testRandomFormat1 // testRandomFormat1
test_parse!(py, parser, datetime, "Wed, July 10, '96"); test_parse!(py, parser, datetime, "Wed, July 10, '96");
// testRandomFormat2 - Needs `ignoretz` // testRandomFormat2 - Needs ignoretz
// test_parse!(py, parser, datetime, "1996.07.10 AD at 15:08:56 PDT"); // test_parse_ignoretz!(py, parser, datetime, "1996.07.10 AD at 15:08:56 PDT");
// testRandomFormat3 // testRandomFormat3
test_parse!(py, parser, datetime, "1996.July.10 AD 12:08 PM"); test_parse!(py, parser, datetime, "1996.July.10 AD 12:08 PM");
// testRandomFormat4 - Needs `ignoretz` // testRandomFormat4 - Needs ignoretz
// test_parse!(py, parser, datetime, "Tuesday, April 12, 1952 AD 3:30:42pm PST"); // test_parse_ignoretz!(py, parser, datetime, "Tuesday, April 12, 1952 AD 3:30:42pm PST");
// testRandomFormat5 - Needs `ignoretz` // testRandomFormat5 - Needs ignoretz
// test_parse!(py, parser, datetime, "November 5, 1994, 8:15:30 am EST"); // test_parse_ignoretz!(py, parser, datetime, "November 5, 1994, 8:15:30 am EST");
// testRandomFormat6 - Needs `ignoretz` // testRandomFormat6 - Needs ignoretz
// test_parse!(py, parser, datetime, "1994-11-05T08:15:30-05:00"); // test_parse_ignoretz!(py, parser, datetime, "1994-11-05T08:15:30-05:00");
// testRandomFormat7 - Needs `ignoretz` // testRandomFormat7 - Needs ignoretz
// test_parse!(py, parser, datetime, "1994-11-05T08:15:30Z"); // test_parse_ignoretz!(py, parser, datetime, "1994-11-05T08:15:30Z");
// testRandomFormat8 // testRandomFormat8
test_parse!(py, parser, datetime, "July 4, 1976"); test_parse!(py, parser, datetime, "July 4, 1976");
// testRandomFormat9 // testRandomFormat9
@ -365,22 +470,22 @@ fn test_dateutil_compat() {
test_parse!(py, parser, datetime, "12h 01m02s am"); test_parse!(py, parser, datetime, "12h 01m02s am");
// testRandomFormat15; NB: testRandomFormat16 is exactly the same // testRandomFormat15; NB: testRandomFormat16 is exactly the same
test_parse!(py, parser, datetime, "0:01:02 on July 4, 1976"); test_parse!(py, parser, datetime, "0:01:02 on July 4, 1976");
// testRandomFormat17 - Needs `ignoretz` // testRandomFormat17 - Needs ignoretz
// test_parse!(py, parser, datetime, "1976-07-04T00:01:02Z"); // test_parse_ignoretz!(py, parser, datetime, "1976-07-04T00:01:02Z");
// testRandomFormat18 // testRandomFormat18
test_parse!(py, parser, datetime, "July 4, 1976 12:01:02 am"); test_parse!(py, parser, datetime, "July 4, 1976 12:01:02 am");
// testRandomFormat19 // testRandomFormat19
test_parse!(py, parser, datetime, "Mon Jan 2 04:24:27 1995"); test_parse!(py, parser, datetime, "Mon Jan 2 04:24:27 1995");
// testRandomFormat20 - Needs `ignoretz` // testRandomFormat20 - Needs ignoretz
// test_parse!(py, parser, datetime, "Tue Apr 4 00:22:12 PDT 1995"); // test_parse_ignoretz!(py, parser, datetime, "Tue Apr 4 00:22:12 PDT 1995");
// testRandomFormat21 // testRandomFormat21
test_parse!(py, parser, datetime, "04.04.95 00:22"); test_parse!(py, parser, datetime, "04.04.95 00:22");
// testRandomFormat22 // testRandomFormat22
test_parse!(py, parser, datetime, "Jan 1 1999 11:23:34.578"); test_parse!(py, parser, datetime, "Jan 1 1999 11:23:34.578");
// testRandomFormat23 // testRandomFormat23
test_parse!(py, parser, datetime, "950404 122212"); test_parse!(py, parser, datetime, "950404 122212");
// testRandomFormat24 - Needs `ignoretz` // testRandomFormat24 - Needs ignoretz
// test_parse!(py, parser, datetime, "0:00 PM, PST"); // test_parse_ignoretz!(py, parser, datetime, "0:00 PM, PST");
// testRandomFormat25 // testRandomFormat25
test_parse!(py, parser, datetime, "12:08 PM"); test_parse!(py, parser, datetime, "12:08 PM");
// testRandomFormat26 // testRandomFormat26