Skip to content

Commit efcc303

Browse files
authored
Merge pull request #22006 from JuliaLang/nl/hashperiods
Fix Period objects hashing to match ==
2 parents 2c73e9c + 6ae55fb commit efcc303

File tree

2 files changed

+32
-1
lines changed

2 files changed

+32
-1
lines changed

base/dates/periods.jl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,10 +443,20 @@ Base.promote_rule(::Type{Year}, ::Type{Month}) = Month
443443
(==)(x::FixedPeriod, y::OtherPeriod) = throw(MethodError(==, (x, y)))
444444
(==)(x::OtherPeriod, y::FixedPeriod) = throw(MethodError(==, (x, y)))
445445

446+
const fixedperiod_seed = UInt === UInt64 ? 0x5b7fc751bba97516 : 0xeae0fdcb
447+
const otherperiod_seed = UInt === UInt64 ? 0xe1837356ff2d2ac9 : 0x170d1b00
448+
# tons() will overflow for periods longer than ~300,000 years, implying a hash collision
449+
# which is relatively harmless given how infrequent such periods should appear
450+
Base.hash(x::FixedPeriod, h::UInt) = hash(tons(x), h + fixedperiod_seed)
451+
# Overflow can also happen here for really long periods (~8e17 years)
452+
Base.hash(x::Year, h::UInt) = hash(12 * value(x), h + otherperiod_seed)
453+
Base.hash(x::Month, h::UInt) = hash(value(x), h + otherperiod_seed)
454+
446455
Base.isless(x::FixedPeriod, y::OtherPeriod) = throw(MethodError(isless, (x, y)))
447456
Base.isless(x::OtherPeriod, y::FixedPeriod) = throw(MethodError(isless, (x, y)))
448457

449-
# truncating conversions to milliseconds and days:
458+
# truncating conversions to milliseconds, nanoseconds and days:
459+
# overflow can happen for periods longer than ~300,000 years
450460
toms(c::Nanosecond) = div(value(c), 1000000)
451461
toms(c::Microsecond) = div(value(c), 1000)
452462
toms(c::Millisecond) = value(c)

test/dates/periods.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,3 +394,24 @@ cpa = [1y + 1s 1m + 1s 1w + 1s 1d + 1s; 1h + 1s 1mi + 1s 2m + 1s 1s + 1ms]
394394

395395
@test [1y + 1s 1m + 1s; 1w + 1s 1d + 1s] + [1y + 1h 1y + 1mi; 1y + 1s 1y + 1ms] == [2y + 1h + 1s 1y + 1m + 1mi + 1s; 1y + 1w + 2s 1y + 1d + 1s + 1ms]
396396
@test [1y + 1s 1m + 1s; 1w + 1s 1d + 1s] - [1y + 1h 1y + 1mi; 1y + 1s 1y + 1ms] == [1s-1h 1m + 1s-1y-1mi; 1w-1y 1d + 1s-1y-1ms]
397+
398+
# Equality and hashing between FixedPeriod types
399+
let types = (Dates.Week, Dates.Day, Dates.Hour, Dates.Minute,
400+
Dates.Second, Dates.Millisecond, Dates.Microsecond, Dates.Nanosecond)
401+
for i in 1:length(types), j in i:length(types), x in (0, 1, 235, -4677, 15250)
402+
T = types[i]
403+
U = types[j]
404+
y = T(x)
405+
z = convert(U, y)
406+
@test y == z
407+
@test hash(y) == hash(z)
408+
end
409+
end
410+
411+
# Equality and hashing between OtherPeriod types
412+
for x in (0, 1, 235, -4677, 15250)
413+
y = Dates.Year(x)
414+
z = convert(Dates.Month, y)
415+
@test y == z
416+
@test hash(y) == hash(z)
417+
end

0 commit comments

Comments
 (0)