diff --git a/stdlib/Dates/src/ranges.jl b/stdlib/Dates/src/ranges.jl index c4299c7b02be5..c253e211edb98 100644 --- a/stdlib/Dates/src/ranges.jl +++ b/stdlib/Dates/src/ranges.jl @@ -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 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 diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index e1f7f900bff51..1b941764fa4c6 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -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 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 @@ -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 @@ -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, == diff --git a/stdlib/Dates/test/types.jl b/stdlib/Dates/test/types.jl index 35a793867dc5a..21b02913b4387 100644 --- a/stdlib/Dates/test/types.jl +++ b/stdlib/Dates/test/types.jl @@ -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)