mirror of
https://github.com/bspeice/dtparse
synced 2024-11-14 09:58:09 -05:00
Some more complex tests
This commit is contained in:
parent
e4d9257305
commit
1cb981b8f6
60
src/lib.rs
60
src/lib.rs
@ -562,8 +562,8 @@ impl YMD {
|
|||||||
fn resolve_from_stridxs(
|
fn resolve_from_stridxs(
|
||||||
&mut self,
|
&mut self,
|
||||||
strids: &mut HashMap<YMDLabel, usize>,
|
strids: &mut HashMap<YMDLabel, usize>,
|
||||||
) -> ParseIResult<(i32, i32, i32)> {
|
) -> ParseIResult<(Option<i32>, Option<i32>, Option<i32>)> {
|
||||||
if strids.len() == 2 {
|
if self._ymd.len() == 3 && strids.len() == 2 {
|
||||||
let missing_key = if !strids.contains_key(&YMDLabel::Year) {
|
let missing_key = if !strids.contains_key(&YMDLabel::Year) {
|
||||||
YMDLabel::Year
|
YMDLabel::Year
|
||||||
} else if !strids.contains_key(&YMDLabel::Month) {
|
} else if !strids.contains_key(&YMDLabel::Month) {
|
||||||
@ -584,19 +584,19 @@ impl YMD {
|
|||||||
strids.insert(missing_key, missing_val);
|
strids.insert(missing_key, missing_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self._ymd.len() != 3 || strids.len() != 3 {
|
if self._ymd.len() != strids.len() {
|
||||||
return Err(ParseInternalError::YMDEarlyResolve);
|
return Err(ParseInternalError::YMDEarlyResolve);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Why do I have to clone &usize? Isn't it Copy?
|
// TODO: Why do I have to clone &usize? Isn't it Copy?
|
||||||
Ok((
|
Ok((
|
||||||
self._ymd[strids.get(&YMDLabel::Year).unwrap().clone()],
|
Some(self._ymd[strids.get(&YMDLabel::Year).unwrap().clone()]),
|
||||||
self._ymd[strids.get(&YMDLabel::Month).unwrap().clone()],
|
Some(self._ymd[strids.get(&YMDLabel::Month).unwrap().clone()]),
|
||||||
self._ymd[strids.get(&YMDLabel::Day).unwrap().clone()],
|
Some(self._ymd[strids.get(&YMDLabel::Day).unwrap().clone()]),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_ymd(&mut self, yearfirst: bool, dayfirst: bool) -> ParseIResult<(i32, i32, i32)> {
|
fn resolve_ymd(&mut self, yearfirst: bool, dayfirst: bool) -> ParseIResult<(Option<i32>, Option<i32>, Option<i32>)> {
|
||||||
let len_ymd = self._ymd.len();
|
let len_ymd = self._ymd.len();
|
||||||
let mut year: Option<i32> = None;
|
let mut year: Option<i32> = None;
|
||||||
let mut month: Option<i32> = None;
|
let mut month: Option<i32> = None;
|
||||||
@ -708,18 +708,7 @@ impl YMD {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove the error handling here
|
Ok((year, month, day))
|
||||||
// We should be able to justify the UNWRAP, but I haven't
|
|
||||||
// convinced myself of that quite yet.
|
|
||||||
if !year.and(month).and(day).is_some() {
|
|
||||||
let mut ymd_unset = Vec::new();
|
|
||||||
if year.is_none() { ymd_unset.push(YMDLabel::Year) }
|
|
||||||
else if month.is_none() { ymd_unset.push(YMDLabel::Month) }
|
|
||||||
else { ymd_unset.push(YMDLabel::Day) }
|
|
||||||
Err(ParseInternalError::YMDValueUnset(ymd_unset))
|
|
||||||
} else {
|
|
||||||
Ok((year.unwrap(), month.unwrap(), day.unwrap()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,22 +738,21 @@ impl Parser {
|
|||||||
pub fn parse(
|
pub fn parse(
|
||||||
&mut self,
|
&mut self,
|
||||||
timestr: &str,
|
timestr: &str,
|
||||||
default: Option<NaiveDateTime>,
|
default: Option<&NaiveDateTime>,
|
||||||
ignoretz: bool,
|
ignoretz: bool,
|
||||||
tzinfos: Vec<String>,
|
tzinfos: Vec<String>,
|
||||||
) -> Result<(NaiveDateTime, Option<FixedOffset>, Option<Vec<String>>), ParseError> {
|
) -> Result<(NaiveDateTime, Option<FixedOffset>, Option<Vec<String>>), ParseError> {
|
||||||
let now = Local::now().naive_local();
|
let default_date = default.unwrap_or(&Local::now().naive_local()).date();
|
||||||
let default_date = default.unwrap_or(now).date();
|
|
||||||
|
|
||||||
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, None, None, false, false)?;
|
||||||
|
|
||||||
let naive = self.build_naive(&res, default_ts);
|
let naive = self.build_naive(&res, &default_ts);
|
||||||
|
|
||||||
if !ignoretz {
|
if !ignoretz {
|
||||||
let offset = self.build_tzaware(&naive, &res, default_ts);
|
let offset = self.build_tzaware(&naive, &res, &default_ts);
|
||||||
Ok((naive, offset.unwrap(), tokens))
|
Ok((naive, offset.unwrap(), tokens))
|
||||||
} else {
|
} else {
|
||||||
Ok((naive, None, tokens))
|
Ok((naive, None, tokens))
|
||||||
@ -922,9 +910,9 @@ impl Parser {
|
|||||||
let (year, month, day) = ymd.resolve_ymd(yearfirst, dayfirst)?;
|
let (year, month, day) = ymd.resolve_ymd(yearfirst, dayfirst)?;
|
||||||
|
|
||||||
res.century_specified = ymd.century_specified;
|
res.century_specified = ymd.century_specified;
|
||||||
res.year = Some(year);
|
res.year = year;
|
||||||
res.month = Some(month);
|
res.month = month;
|
||||||
res.day = Some(day);
|
res.day = day;
|
||||||
|
|
||||||
if !self.info.validate(&mut res) {
|
if !self.info.validate(&mut res) {
|
||||||
Err(ParseError::InvalidParseResult(res))
|
Err(ParseError::InvalidParseResult(res))
|
||||||
@ -970,7 +958,7 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_naive(&self, res: &ParsingResult, default: NaiveDateTime) -> NaiveDateTime {
|
fn build_naive(&self, res: &ParsingResult, default: &NaiveDateTime) -> NaiveDateTime {
|
||||||
// TODO: Change month/day to u32
|
// TODO: Change month/day to u32
|
||||||
let d = NaiveDate::from_ymd(
|
let d = NaiveDate::from_ymd(
|
||||||
res.year.unwrap_or(default.year()),
|
res.year.unwrap_or(default.year()),
|
||||||
@ -993,12 +981,14 @@ impl Parser {
|
|||||||
&self,
|
&self,
|
||||||
dt: &NaiveDateTime,
|
dt: &NaiveDateTime,
|
||||||
res: &ParsingResult,
|
res: &ParsingResult,
|
||||||
default: NaiveDateTime,
|
default: &NaiveDateTime,
|
||||||
) -> ParseResult<Option<FixedOffset>> {
|
) -> ParseResult<Option<FixedOffset>> {
|
||||||
|
|
||||||
if res.tzname.is_none() && res.tzoffset.is_none() {
|
// TODO: Actual timezone support
|
||||||
|
if res.tzname.is_none() && res.tzoffset.is_none() || res.tzname == Some(" ".to_owned()) {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
|
println!("Tzname: {:?}, Tzoffset: {:?}", res.tzname, res.tzoffset);
|
||||||
Err(ParseError::TimezoneUnsupported)
|
Err(ParseError::TimezoneUnsupported)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1265,13 +1255,19 @@ fn ljust(s: &str, chars: usize, replace: char) -> String {
|
|||||||
fn parse_with_info(
|
fn parse_with_info(
|
||||||
timestr: &str,
|
timestr: &str,
|
||||||
info: ParserInfo,
|
info: ParserInfo,
|
||||||
|
default: Option<&NaiveDateTime>,
|
||||||
) -> ParseResult<(NaiveDateTime, Option<FixedOffset>, Option<Vec<String>>)> {
|
) -> ParseResult<(NaiveDateTime, Option<FixedOffset>, Option<Vec<String>>)> {
|
||||||
// TODO: Is `::new()` more stylistic?
|
// TODO: Is `::new()` more stylistic?
|
||||||
let mut parser = Parser { info: info };
|
let mut parser = Parser { info: info };
|
||||||
parser.parse(timestr, None, false, vec![])
|
parser.parse(timestr, default, false, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(timestr: &str) -> ParseResult<(NaiveDateTime, Option<FixedOffset>)> {
|
fn parse(timestr: &str) -> ParseResult<(NaiveDateTime, Option<FixedOffset>)> {
|
||||||
let parse_result = parse_with_info(timestr, ParserInfo::default())?;
|
let parse_result = parse_with_info(timestr, ParserInfo::default(), None)?;
|
||||||
Ok((parse_result.0, parse_result.1))
|
Ok((parse_result.0, parse_result.1))
|
||||||
}
|
}
|
||||||
|
49
src/tests.rs
49
src/tests.rs
@ -1,12 +1,19 @@
|
|||||||
|
use chrono::Datelike;
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use chrono::NaiveTime;
|
||||||
use pyo3::ObjectProtocol;
|
use pyo3::ObjectProtocol;
|
||||||
use pyo3::PyDict;
|
use pyo3::PyDict;
|
||||||
use pyo3::PyList;
|
use pyo3::PyList;
|
||||||
use pyo3::PyObject;
|
use pyo3::PyObject;
|
||||||
|
use pyo3::PyObjectRef;
|
||||||
use pyo3::Python;
|
use pyo3::Python;
|
||||||
use pyo3::FromPyObject;
|
use pyo3::FromPyObject;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use tokenize;
|
use tokenize;
|
||||||
use parse;
|
use parse;
|
||||||
|
use parse_with_default;
|
||||||
|
|
||||||
macro_rules! test_split {
|
macro_rules! test_split {
|
||||||
($py: ident, $timelex: ident, $s: expr) => {
|
($py: ident, $timelex: ident, $s: expr) => {
|
||||||
@ -36,6 +43,7 @@ fn test_split() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! test_parse_naive {
|
macro_rules! test_parse_naive {
|
||||||
|
// Handle tests where the times involved are unambiguous
|
||||||
($py: ident, $parser: ident, $s: expr) => {
|
($py: ident, $parser: ident, $s: expr) => {
|
||||||
let dt: PyObject = $parser.call_method1("parse", $s).unwrap().extract().unwrap();
|
let dt: PyObject = $parser.call_method1("parse", $s).unwrap().extract().unwrap();
|
||||||
let dt_s: String = dt.call_method1($py, "isoformat", " ").unwrap().extract($py).unwrap();
|
let dt_s: String = dt.call_method1($py, "isoformat", " ").unwrap().extract($py).unwrap();
|
||||||
@ -53,10 +61,34 @@ macro_rules! test_parse_naive {
|
|||||||
assert_eq!(rs.1, None);
|
assert_eq!(rs.1, None);
|
||||||
assert_eq!(s, format!("{}", rs.0));
|
assert_eq!(s, format!("{}", rs.0));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle tests with some ambiguity, and thus needing a `default`
|
||||||
|
($py: ident, $parser: ident, $s: expr, $datetime: ident, $d: expr) => {
|
||||||
|
let rust_date = $d.date();
|
||||||
|
let dt_tuple = (rust_date.year(), rust_date.month(), rust_date.day());
|
||||||
|
let pydefault: &PyObjectRef = $datetime.call_method1("datetime", dt_tuple).unwrap();
|
||||||
|
|
||||||
|
let mut kwargs = HashMap::new();
|
||||||
|
kwargs.insert("default", pydefault);
|
||||||
|
|
||||||
|
let dt: PyObject = $parser.call_method("parse", $s, kwargs).unwrap().extract().unwrap();
|
||||||
|
let dt_s: String = dt.call_method1($py, "isoformat", " ").unwrap().extract($py).unwrap();
|
||||||
|
let s = format!("{}", dt_s);
|
||||||
|
|
||||||
|
let r_rs = parse_with_default($s, $d);
|
||||||
|
if r_rs.is_err() {
|
||||||
|
println!("{:?}", r_rs);
|
||||||
|
assert!(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rs = r_rs.unwrap();
|
||||||
|
assert_eq!(rs.1, None);
|
||||||
|
assert_eq!(s, format!("{}", rs.0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse() {
|
fn test_basic_parse() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
let parser = py.import("dateutil.parser").unwrap();
|
let parser = py.import("dateutil.parser").unwrap();
|
||||||
@ -67,3 +99,18 @@ fn test_parse() {
|
|||||||
test_parse_naive!(py, parser, "19990101T23");
|
test_parse_naive!(py, parser, "19990101T23");
|
||||||
test_parse_naive!(py, parser, "19990101T2359");
|
test_parse_naive!(py, parser, "19990101T2359");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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();
|
||||||
|
|
||||||
|
let default = NaiveDateTime::new(NaiveDate::from_ymd(2003, 9, 25), NaiveTime::from_hms(0, 0, 0));
|
||||||
|
|
||||||
|
// testDateCommandFormatStrip1
|
||||||
|
test_parse_naive!(py, parser, "Thu Sep 25 10:36:28 2003");
|
||||||
|
// testDateCommandFormatStrip2
|
||||||
|
test_parse_naive!(py, parser, "Thu Sep 25 10:36:28", datetime, &default);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user