Skip to content

fix: Validate constructed date has year within typemin/max #54933

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

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion stdlib/Dates/src/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ function len(a, b, c)
i = guess(a, b, c)
v = lo + st * i
prev = v # Ensure `v` does not overflow
hi -= st # Prevent overshooting
while v <= hi && prev <= v
prev = v
v += st
i += 1
end
return i - 1
return i
Comment on lines +18 to +24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this change necessary?

Copy link
Contributor Author

@mcmcgrath13 mcmcgrath13 Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, this is still a work in progress to figure out how to do this. The range implementations currently rely on being able to go past the actual end of the supported range, and now that that errors, this needs to be rethought (it doesn't quite work correctly yet)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it would help to extend the constructable dates from typemin(Date):typemax(Date) to the slightly larger set of representable dates typemin(Date)-Day(161):typemax(Date)+Day(280)

end
Base.length(r::StepRange{<:TimeType}) = isempty(r) ? Int64(0) : len(r.start, r.stop, r.step) + 1
# Period ranges hook into Int64 overflow detection
Expand Down
14 changes: 10 additions & 4 deletions stdlib/Dates/src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,11 @@ function DateTime(y::Int64, m::Int64=1, d::Int64=1,
return DateTime(UTM(rata))
end

const DATETIME_YEAR_TYPEMAX = 146138512
const DATETIME_YEAR_TYPEMIN = -146138511
Comment on lines +252 to +253
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate factoring out these magic numbers.

function validargs(::Type{DateTime}, y::Int64, m::Int64, d::Int64,
h::Int64, mi::Int64, s::Int64, ms::Int64, ampm::AMPM=TWENTYFOURHOUR)
DATETIME_YEAR_TYPEMIN <= y <= DATETIME_YEAR_TYPEMAX || return ArgumentError("Year: $y out of range ($(DATETIME_YEAR_TYPEMIN):$DATETIME_YEAR_TYPEMAX)")
0 < m < 13 || return ArgumentError("Month: $m out of range (1:12)")
0 < d < daysinmonth(y, m) + 1 || return ArgumentError("Day: $d out of range (1:$(daysinmonth(y, m)))")
if ampm == TWENTYFOURHOUR # 24-hour clock
Expand Down Expand Up @@ -278,9 +281,12 @@ function Date(y::Int64, m::Int64=1, d::Int64=1)
return Date(UTD(totaldays(y, m, d)))
end

const DATE_YEAR_TYPEMAX = 252522163911149
const DATE_YEAR_TYPEMIN = -252522163911150
function validargs(::Type{Date}, y::Int64, m::Int64, d::Int64)
0 < m < 13 || return ArgumentError("Month: $m out of range (1:12)")
0 < d < daysinmonth(y, m) + 1 || return ArgumentError("Day: $d out of range (1:$(daysinmonth(y, m)))")
DATE_YEAR_TYPEMIN <= y <= DATE_YEAR_TYPEMAX || return ArgumentError("Year: $y out of range ($(DATE_YEAR_TYPEMIN):$DATE_YEAR_TYPEMAX)")
return nothing
end

Expand Down Expand Up @@ -461,10 +467,10 @@ Base.zero(::Type{Time}) = Nanosecond(0)
Base.zero(::T) where T <: TimeType = zero(T)::Period


Base.typemax(::Union{DateTime, Type{DateTime}}) = DateTime(146138512, 12, 31, 23, 59, 59)
Base.typemin(::Union{DateTime, Type{DateTime}}) = DateTime(-146138511, 1, 1, 0, 0, 0)
Base.typemax(::Union{Date, Type{Date}}) = Date(252522163911149, 12, 31)
Base.typemin(::Union{Date, Type{Date}}) = Date(-252522163911150, 1, 1)
Base.typemax(::Union{DateTime, Type{DateTime}}) = DateTime(DATETIME_YEAR_TYPEMAX, 12, 31, 23, 59, 59)
Base.typemin(::Union{DateTime, Type{DateTime}}) = DateTime(DATETIME_YEAR_TYPEMIN, 1, 1, 0, 0, 0)
Base.typemax(::Union{Date, Type{Date}}) = Date(DATE_YEAR_TYPEMAX, 12, 31)
Base.typemin(::Union{Date, Type{Date}}) = Date(DATE_YEAR_TYPEMIN, 1, 1)
Base.typemax(::Union{Time, Type{Time}}) = Time(23, 59, 59, 999, 999, 999)
Base.typemin(::Union{Time, Type{Time}}) = Time(0)
# Date-DateTime promotion, isless, ==
Expand Down
6 changes: 5 additions & 1 deletion stdlib/Dates/test/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,19 @@ end
@test_throws InexactError Dates.Date(1.2f0, 1.f0, 1.f0)
@test_throws InexactError Dates.Date(3//4, Rational(1), Rational(1)) == test

# Months, days, hours, minutes, seconds, and milliseconds must be in range
# Years, months, days, hours, minutes, seconds, and milliseconds must be in range
@test_throws ArgumentError Dates.Date(2013, 0, 1)
@test_throws ArgumentError Dates.Date(2013, 13, 1)
@test_throws ArgumentError Dates.Date(2013, 1, 0)
@test_throws ArgumentError Dates.Date(2013, 1, 32)
@test_throws ArgumentError Dates.Date(year(typemax(Date))+1, 1, 1)
@test_throws ArgumentError Dates.Date(year(typemin(Date))-1, 1, 32)
@test_throws ArgumentError Dates.DateTime(2013, 0, 1)
@test_throws ArgumentError Dates.DateTime(2013, 13, 1)
@test_throws ArgumentError Dates.DateTime(2013, 1, 0)
@test_throws ArgumentError Dates.DateTime(2013, 1, 32)
@test_throws ArgumentError Dates.DateTime(year(typemax(DateTime))+1, 1, 1)
@test_throws ArgumentError Dates.DateTime(year(typemin(DateTime))-1, 1, 32)
@test_throws ArgumentError Dates.DateTime(2013, 1, 1, 25)
@test_throws ArgumentError Dates.DateTime(2013, 1, 1, -1)
@test_throws ArgumentError Dates.DateTime(2013, 1, 1, 0, -1)
Expand Down