-
Notifications
You must be signed in to change notification settings - Fork 318
👽 Implement tzinfo.fromutc for TzInfo
#864
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
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
|
samuelcolvin
left a comment
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
| 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 |
davidhewitt
left a comment
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
pyseparately 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 caseThere 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 |
samuelcolvin
left a comment
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.
| #[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?
adriangb
left a comment
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
Original-commit-hash: 8477825
Original-commit-link: pydantic/pydantic-core@8477825
Change Summary
Implement
tzinfo.fromutcmethod inTzInfoas default behavior (see: https://docs.python.org/3/library/datetime.html#datetime.tzinfo.fromutc) isn't compatible withTzInfoimplementation.Related issue number
See pydantic/pydantic#6735
Checklist
pydantic-core(except for expected changes)Selected Reviewer: @adriangb