-
Notifications
You must be signed in to change notification settings - Fork 226
Avoid using floating points during timestamp-datetime conversions #591
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
Changes from 1 commit
e92f68e
12d9cc0
64c0e67
34ab273
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -157,12 +157,14 @@ def to_datetime(self): | |
:rtype: `datetime.datetime` | ||
""" | ||
utc = datetime.timezone.utc | ||
return datetime.datetime.fromtimestamp(0, utc) + datetime.timedelta(seconds=self.to_unix()) | ||
return datetime.datetime.fromtimestamp(0, utc) + datetime.timedelta( | ||
seconds=self.seconds, microseconds=round(self.nanoseconds / 1e3) | ||
) | ||
|
||
@staticmethod | ||
def from_datetime(dt): | ||
"""Create a Timestamp from datetime with tzinfo. | ||
|
||
:rtype: Timestamp | ||
""" | ||
return Timestamp.from_unix(dt.timestamp()) | ||
return Timestamp(seconds=int(dt.timestamp() // 1), nanoseconds=dt.microsecond * 10**3) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That Considering you use Also: just use 1000 instead of 10**3? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, i overlooked that part completely, we dont need the floor division at all. I'll remove it.
we can of course use 1000 instead, its just that in the code that i checked, i either saw "10**x" or "1ex" and wanted follow that convention. |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -86,6 +86,24 @@ def test_timestamp_datetime(): | |||||
utc = datetime.timezone.utc | ||||||
assert t.to_datetime() == datetime.datetime(1970, 1, 1, 0, 0, 42, 0, tzinfo=utc) | ||||||
|
||||||
ts = datetime.datetime(2024, 4, 16, 8, 43, 9, 420317, tzinfo=utc) | ||||||
ts2 = datetime.datetime(2024, 4, 16, 8, 43, 9, 420318, tzinfo=utc) | ||||||
|
||||||
assert Timestamp.from_datetime(ts2).nanoseconds - Timestamp.from_datetime(ts).nanoseconds == 1e3 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
ts3 = datetime.datetime(2024, 4, 16, 8, 43, 9, 4256) | ||||||
ts4 = datetime.datetime(2024, 4, 16, 8, 43, 9, 4257) | ||||||
assert ( | ||||||
Timestamp.from_datetime(ts4).nanoseconds - Timestamp.from_datetime(ts3).nanoseconds == 1e3 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
) | ||||||
|
||||||
assert Timestamp.from_datetime(ts).to_datetime() == ts | ||||||
|
||||||
t2 = Timestamp(1713256989, 420318123) | ||||||
t3 = Timestamp(1713256989, 420318499) | ||||||
t4 = Timestamp(1713256989, 420318501) | ||||||
assert t2.to_datetime() == t3.to_datetime() != t4.to_datetime() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe just using 2 asserts would be simpler. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. definitely, we dont necessarily need to compare with t2 now i think of it. But these were to test the rounding specifically, if rounding is wrong way to go, these tests arent necessarily needed imo. |
||||||
|
||||||
|
||||||
def test_unpack_datetime(): | ||||||
t = Timestamp(42, 14) | ||||||
|
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 use 1000 instead of 1e3?
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 prefer
//
over round.Consider "12:34:56.78". It is "12:34" or "12:34:56", not "12:35" nor "12:34:57".
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.
same as the previous comment, I just though "10**x" or "1ex" was the convention that was being used.
I'm not actually knowledgeable about whether there should be a round or not. The idea I had with the round is when working with a timestamp with nanosecond precision, lets say, "100,000,900" nanoseconds, I thought it should round to "100,001" microseconds, whereas "100,000,100" nanoseconds should be "100,000" microseconds.
With the case "12:34:56.78", I'm not sure whether it should "12:34:56" or "12:34:57", if rounding isn't the norm in these cases, we should definitely remove it and the tests related to it.
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.
The year of 2024-10-01 is 2025?
The day of 2024-10-01 15:00:00 is 2024-10-02?
The hour of 15:34 is 16?
Despite what you explain it in natural language, "round down" is consistent in programming.
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.
https://github.com/golang/go/blob/a63907808d14679c723e566cb83acc76fc8cafc2/src/time/time.go#L1214-L1244
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.
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.
https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/time/Instant.html#toEpochMilli()
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.
hmm i see, thanks a lot it makes more sense. Then i'll remove the rounding and tests related to it.
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.
2023-12-31 23:59:59.999999500 -- year 2023, date 2023-12-31.
After round half by micros:
2024-01-01 00:00:00.000000 -- year 2024, date 2024-01-01.
This is why most languages uses consistent "round down" for subseconds.
Consistency is important to avoid bugs. Every programmer should love consistency.
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.
yup of course, I didn't look at it like that when I was proposing the change. Again it makes more sense to round down instead as it would be more consistant as you said. Thanks for the explanation!