-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Add rounding support for Periods
#24182
Conversation
Could/should these be |
Unless something has changed, these signatures actually do match the signatures of the date rounding functions, but the confusion is completely understandable (this is actually addressed in the first point of contention above). |
To clarify, both the "round a date(time) to a period value" and "round a date(time) to a period type" have the same signature, which is But I could definitely see an argument for "round to a period value" and "round to a period type" having different interfaces (with the latter using the same order as |
The initial decision to use the rounding signature we currently have for date(time)s was informed by Generally, we have So... this is why this is an RFC. 😁 |
This is the argument I was trying to make 😄 |
I could definitely go either way on that one. But if we move to:
then the interfaces for both |
We should see what @omus thinks |
I think julia> mod(260,256)
4
julia> mod(260,UInt8)
0x04 |
That's interesting. So you're in favour of keeping the existing signatures, @omus? |
Yes, I think the existing signatures are reasonable |
Periods
Periods
base/dates/rounding.jl
Outdated
|
||
# Make rounding functions callable using Period types in addition to values. | ||
Base.floor(dt::TimeType, p::Type{<:Period}) = Base.floor(dt, p(1)) | ||
Base.ceil(dt::TimeType, p::Type{<:Period}) = Base.ceil(dt, p(1)) | ||
Base.floor(x::TimeTypePeriod, p::Type{<:Period}) = Base.floor(x, p(1)) |
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.
Should probably be changed to:
Base.floor(x::TimeTypePeriod, ::Type{P}) where P <: Period = Base.floor(x, oneunit(P))
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.
Interesting. I was completely unaware of oneunit
.
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.
It's the unit aware sibling to one
test/dates/rounding.jl
Outdated
end | ||
@testset "Rounding for periods that should not need rounding" begin | ||
for x in [Dates.Week(3), Dates.Day(14), Dates.Microsecond(604800000000)] | ||
local dt |
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.
Unused
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 should not be allowed to use copy/paste.
test/dates/rounding.jl
Outdated
for x in [Dates.Week(3), Dates.Day(14), Dates.Microsecond(604800000000)] | ||
local dt | ||
for p in [Dates.Week, Dates.Day, Dates.Hour, Dates.Second, Dates.Millisecond, Dates.Microsecond, Dates.Nanosecond] | ||
local p |
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.
Unneeded?
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.
@kshyatt can you elaborate on why this is needed?
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 is a case where I noticed that local dt
had been added to the tests in several places, so I replicated that syntax for these tests (in this case local dt
should actually be local x
above). But yeah, I don't actually know what those lines are meant to accomplish.
base/dates/rounding.jl
Outdated
@@ -7,6 +7,9 @@ const DATETIMEEPOCH = value(DateTime(0)) | |||
# According to ISO 8601, the first day of the first week of year 0000 is 0000-01-03 | |||
const WEEKEPOCH = value(Date(0, 1, 3)) | |||
|
|||
const ConvertiblePeriod = Union{TimePeriod, Week, Day} | |||
const TimeTypePeriod = Union{TimeType, ConvertiblePeriod} |
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.
Maybe rename to TimeTypeOrPeriod
b0c87ad
to
586d793
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.
Looks good. I'd like to know what the local
loop variables are for yet.
Maybe spoke too soon @spurll there looks like there is a 32-bit issue. |
Ah yes. It looks like |
Periods
Periods
Period rounding functions no longer unnecessarily convert all values to nanoseconds prior to rounding.
Periods
Periods
AppVeyor failure seems unrelated. I believe everything else is fixed. |
base/dates/rounding.jl
Outdated
""" | ||
function Base.floor(x::ConvertiblePeriod, precision::T) where T <: ConvertiblePeriod | ||
value(precision) < 1 && throw(DomainError(precision)) | ||
x, precision = promote(x, precision) |
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.
Should use a different variable name for the assignment to ensure type stability
base/dates/rounding.jl
Outdated
function Base.round(x::ConvertiblePeriod, precision::ConvertiblePeriod, r::RoundingMode{:NearestTiesUp}) | ||
f, c = floorceil(x, precision) | ||
common_x, common_f, common_c = promote(x, f, c) | ||
return (common_x - common_f) < (common_c - common_x) ? f : c |
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 think _x, _f, _c = ...
is fine if you want to use a more concise name
base/dates/rounding.jl
Outdated
function Base.round(dt::TimeType, p::Type{<:Period}, r::RoundingMode=RoundNearestTiesUp) | ||
return Base.round(dt, p(1), r) | ||
function Base.round(x::TimeTypeOrPeriod, ::Type{P}, r::RoundingMode=RoundNearestTiesUp) where P <: Period | ||
return Base.round(x, oneunit(P), r) |
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.
Extra space
I'm not sure what's going on with CI here. |
Looks good. I'm going to retry the CI. |
Allows (most) subtypes of
Period
to be rounded withfloor
,ceil
, andround
, also allowing for easy conversion betweenPeriod
subtypes where we would otherwise encounter anInexactError
.As with the date rounding functions, this approach also supports rounding to values (e.g., the nearest 15 minutes) as well as types:
I can see at least two potential concerns being raised about this approach:
round(value, precision)
) and the convert signature (convert(type, value)
).For certain very large values, the rounding may still result in anInexactError
. This is because all values are converted toNanosecond
for rounding, so we can't easily accommodate values that exceedtypemax(Int64)
nanoseconds (which is a little over 292 years). This could be mitigated by using a lower-precisionPeriod
type when nanosecond precision is not required.Edit: Point 2 above has been mitigated via
promote
.