-
Notifications
You must be signed in to change notification settings - Fork 245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
👽 Implement tzinfo.fromutc
for TzInfo
#864
👽 Implement tzinfo.fromutc
for TzInfo
#864
Conversation
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #864 +/- ##
==========================================
+ Coverage 93.78% 93.79% +0.01%
==========================================
Files 105 105
Lines 15331 15362 +31
Branches 25 25
==========================================
+ Hits 14378 14409 +31
Misses 947 947
Partials 6 6
Continue to review full report in Codecov by Sentry.
|
please review |
CodSpeed Performance ReportMerging #864 will not alter performanceComparing Summary
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please can we test fromutc
directly, and also check if there are some nice tests for it in cpython and copy them?
I have no idea if the current implementation is correct.
src/input/datetime.rs
Outdated
@@ -496,6 +496,11 @@ impl TzInfo { | |||
None | |||
} | |||
|
|||
fn fromutc<'p>(&self, py: Python<'p>, dt: &'p PyDateTime) -> PyResult<&'p PyDateTime> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn fromutc<'p>(&self, py: Python<'p>, dt: &'p PyDateTime) -> PyResult<&'p PyDateTime> { | |
fn fromutc<'py>(&self, py: Python<'py>, dt: &'py PyDateTime) -> PyResult<&'py PyDateTime> { |
use use py
everywhere.
Well, the current implementation of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quoting the Python docs:
Most implementations of dst() will probably look like one of these two:
def dst(self, dt): # a fixed-offset class: doesn't account for DST return timedelta(0)
...
... if our TzInfo
type represents a fixed time zone, maybe we want to implement dst
like that too?
src/input/datetime.rs
Outdated
fn fromutc<'p>(&self, py: Python<'p>, dt: &'p PyDateTime) -> PyResult<&'p PyDateTime> { | ||
dt.call_method1("__add__", (self.utcoffset(py, py.None().as_ref(py))?,))? | ||
.extract() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Being unable to resist a little golfing here:
- No need to take
py
separately as you can get it fromdt.py()
- I don't think there's value in casting the return value to datetime, that's just spending CPU cycles
fn fromutc<'p>(&self, py: Python<'p>, dt: &'p PyDateTime) -> PyResult<&'p PyDateTime> { | |
dt.call_method1("__add__", (self.utcoffset(py, py.None().as_ref(py))?,))? | |
.extract() | |
} | |
fn fromutc<'py>(&self, dt: &'py PyDateTime) -> PyResult<&'py PyAny> { | |
let py = dt.py(); | |
dt.call_method1("__add__", (self.utcoffset(py, py.None().as_ref(py))?,)) | |
} |
(self.utcoffset()
could actually get similar treatment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the benefit of taking py
from dt? I thought the preferred way is requesting it with the function argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just that it's all the same py
so no need to have a separate argument for it. It's a bit like the following would be redundant:
fn f(x: i32, (_, y): (i32, i32)) { ... }
f(x, (x, y)) // passed x twice, ignored in second case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requesting it with the function argument is better than Python::with_gil
if you don't already have it for sure.
19858e9
to
9aa86b3
Compare
Returning 0 from dst explicitly means that there is no day light saving time in effect. However, there could be but we just do not know it in our implementation. So, having it return |
Is this the case? If we parse Looks like https://datatracker.ietf.org/doc/draft-ietf-sedate-datetime-extended/ is needed for RFC3339 datetimes to support zones with dst, in which case it's passed explicitly. |
We cannot assume that. For example, CEST is exactly +02:00 but it has dst offset of +01:00 now included in that +02:00. |
Discussed offline - |
@samuelcolvin shall we bring some of those tests in? |
please review |
94b1b36
to
605a8f9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
otherwise LGTM.
@@ -508,11 +512,18 @@ pub struct TzInfo { | |||
#[pymethods] | |||
impl TzInfo { | |||
#[new] | |||
fn new(seconds: i32) -> Self { | |||
Self { seconds } | |||
fn py_new(seconds: f32) -> PyResult<Self> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to add
impl TryFrom<i32> for TzInfo {
type Error = PyErr;
fn try_from(seconds: i32) -> PyResult<Self> {
if seconds.abs() >= 86400 {
Err(PyValueError::new_err(format!(
"TzInfo offset must be strictly between -86400 and 86400 (24 hours) seconds, got {seconds}"
)))
} else {
Ok(Self { seconds })
}
}
}
Then use (seconds.trunc() as i32).try_into()
here, then you can use let tz_info: TzInfo = offset.try_into()?;
above and avoid going i32 -> f32 -> i32
.
DSTEND = datetime(1, 10, 25, 1) | ||
|
||
|
||
class TestTzInfo(unittest.TestCase): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assuming this is copied from cpython somewhere, please include a link to the original in a docstring.
self.EST = TzInfo(-timedelta(hours=5).total_seconds()) | ||
self.DT = datetime(2010, 1, 1) | ||
|
||
def test_str(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can probably use parameterize
for these and thereby make any error easier to read.
Ideally these should be rewritten in pytest style, it shouldn't take very long, but I know you've already spend lots of time on this, so don't worry if you can't do it quickly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kinda like the idea of keeping them as unittest. @lig can you add a link / note to permalink to the standard library for future selfs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good, I like that we are trying to match str()
and such as closely as possible to the standard library 👍🏻
605a8f9
to
4c8c7ba
Compare
Change Summary
Implement
tzinfo.fromutc
method inTzInfo
as default behavior (see: https://docs.python.org/3/library/datetime.html#datetime.tzinfo.fromutc) isn't compatible withTzInfo
implementation.Related issue number
See pydantic/pydantic#6735
Checklist
pydantic-core
(except for expected changes)Selected Reviewer: @adriangb