Skip to content

Commit

Permalink
fix ValueError on year zero (#1583)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt authored Dec 18, 2024
1 parent 39435c2 commit 15adfc7
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 34 deletions.
72 changes: 42 additions & 30 deletions src/input/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,26 @@ pub fn pydate_as_date(py_date: &Bound<'_, PyAny>) -> PyResult<Date> {
})
}

impl<'py> IntoPyObject<'py> for EitherDate<'py> {
type Target = PyDate;
type Output = Bound<'py, PyDate>;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
impl<'py> EitherDate<'py> {
pub fn try_into_py(&self, py: Python<'py>, input: &(impl Input<'py> + ?Sized)) -> ValResult<PyObject> {
match self {
Self::Raw(date) => PyDate::new(py, date.year.into(), date.month, date.day),
Self::Py(date) => Ok(date),
Self::Raw(date) => {
if date.year == 0 {
return Err(ValError::new(
ErrorType::DateParsing {
error: Cow::Borrowed("year 0 is out of range"),
context: None,
},
input,
));
};
let py_date = PyDate::new(py, date.year.into(), date.month, date.day)?;
Ok(py_date.into())
}
Self::Py(py_date) => Ok(py_date.clone().into()),
}
}
}

impl EitherDate<'_> {
pub fn as_raw(&self) -> PyResult<Date> {
match self {
Self::Raw(date) => Ok(date.clone()),
Expand Down Expand Up @@ -278,30 +284,36 @@ pub fn pydatetime_as_datetime(py_dt: &Bound<'_, PyAny>) -> PyResult<DateTime> {
})
}

impl<'py> IntoPyObject<'py> for EitherDateTime<'py> {
type Target = PyDateTime;
type Output = Bound<'py, PyDateTime>;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
impl<'py> EitherDateTime<'py> {
pub fn try_into_py(&self, py: Python<'py>, input: &(impl Input<'py> + ?Sized)) -> ValResult<PyObject> {
match self {
Self::Raw(dt) => PyDateTime::new(
py,
dt.date.year.into(),
dt.date.month,
dt.date.day,
dt.time.hour,
dt.time.minute,
dt.time.second,
dt.time.microsecond,
time_as_tzinfo(py, &dt.time)?.as_ref(),
),
Self::Py(dt) => Ok(dt),
Self::Raw(dt) => {
if dt.date.year == 0 {
return Err(ValError::new(
ErrorType::DatetimeParsing {
error: Cow::Borrowed("year 0 is out of range"),
context: None,
},
input,
));
};
let py_dt = PyDateTime::new(
py,
dt.date.year.into(),
dt.date.month,
dt.date.day,
dt.time.hour,
dt.time.minute,
dt.time.second,
dt.time.microsecond,
time_as_tzinfo(py, &dt.time)?.as_ref(),
)?;
Ok(py_dt.into())
}
Self::Py(py_dt) => Ok(py_dt.clone().into()),
}
}
}

impl EitherDateTime<'_> {
pub fn as_raw(&self) -> PyResult<DateTime> {
match self {
Self::Raw(dt) => Ok(dt.clone()),
Expand Down
3 changes: 1 addition & 2 deletions src/validators/date.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use pyo3::intern;
use pyo3::prelude::*;
use pyo3::types::{PyDate, PyDict, PyString};
use pyo3::IntoPyObjectExt;
use speedate::{Date, Time};
use strum::EnumMessage;

Expand Down Expand Up @@ -98,7 +97,7 @@ impl Validator for DateValidator {
}
}
}
Ok(date.into_py_any(py)?)
date.try_into_py(py, input)
}

fn get_name(&self) -> &str {
Expand Down
3 changes: 1 addition & 2 deletions src/validators/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use pyo3::intern;
use pyo3::prelude::*;
use pyo3::sync::GILOnceCell;
use pyo3::types::{PyDict, PyString};
use pyo3::IntoPyObjectExt;
use speedate::{DateTime, Time};
use std::cmp::Ordering;
use strum::EnumMessage;
Expand Down Expand Up @@ -131,7 +130,7 @@ impl Validator for DateTimeValidator {
tz_constraint.tz_check(speedate_dt.time.tz_offset, input)?;
}
}
Ok(datetime.into_py_any(py)?)
datetime.try_into_py(py, input)
}

fn get_name(&self) -> &str {
Expand Down
6 changes: 6 additions & 0 deletions tests/validators/test_date.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@
),
pytest.param('-', Err('Input should be a valid date or datetime, input is too short'), id='minus'),
pytest.param('+', Err('Input should be a valid date or datetime, input is too short'), id='pus'),
pytest.param('0001-01-01', date(1, 1, 1), id='min-date'),
pytest.param(
'0000-12-31',
Err('Input should be a valid date in the format YYYY-MM-DD, year 0 is out of range [type=date_parsing,'),
id='year-0',
),
],
)
def test_date(input_value, expected):
Expand Down
8 changes: 8 additions & 0 deletions tests/validators/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@
'Input should be a valid datetime or date, day value is outside expected range [type=datetime_from_date_parsing,'
),
),
(
'0001-01-01T00:00:00.000000Z',
datetime(1, 1, 1, tzinfo=timezone.utc),
),
(
'0000-12-31T23:59:59.999999Z',
Err('Input should be a valid datetime, year 0 is out of range [type=datetime_parsing,'),
),
],
)
def test_datetime(input_value, expected):
Expand Down

0 comments on commit 15adfc7

Please sign in to comment.