From bd5224e1f249c2a8f50fa3d740b6a3b314c72353 Mon Sep 17 00:00:00 2001 From: Mary McGrath Date: Tue, 25 Jun 2024 16:12:36 -0400 Subject: [PATCH 1/8] fix: Validate constructed date has year within typemin/max --- stdlib/Dates/src/types.jl | 1 + stdlib/Dates/test/types.jl | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index e1f7f900bff51..2c686e0e4440b 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -281,6 +281,7 @@ end 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)))") + year(typemin(T)) <= y <= year(typemax(T)) || return ArgumentError("Year: $y out of range ($(year(typemin(T))):$(year(typemax(T))))") return nothing end diff --git a/stdlib/Dates/test/types.jl b/stdlib/Dates/test/types.jl index 35a793867dc5a..3ad61e41cb07d 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(Date))+1, 1, 1) + @test_throws ArgumentError Dates.DateTime(year(typemin(Date))-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) From 7b4edaed5c6f28555595cb5690a2ad6649537c81 Mon Sep 17 00:00:00 2001 From: Mary McGrath Date: Tue, 25 Jun 2024 16:23:53 -0400 Subject: [PATCH 2/8] fix: finish the copy/pasting --- stdlib/Dates/src/types.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index 2c686e0e4440b..d28523c743d6f 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -278,7 +278,7 @@ function Date(y::Int64, m::Int64=1, d::Int64=1) return Date(UTD(totaldays(y, m, d))) end -function validargs(::Type{Date}, y::Int64, m::Int64, d::Int64) +function validargs(T::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)))") year(typemin(T)) <= y <= year(typemax(T)) || return ArgumentError("Year: $y out of range ($(year(typemin(T))):$(year(typemax(T))))") From dc1b53d3e0a2da98ecd5a58482a38a9b44cff5f0 Mon Sep 17 00:00:00 2001 From: Mary McGrath Date: Tue, 25 Jun 2024 17:15:49 -0400 Subject: [PATCH 3/8] fix: avoid stack overflow --- stdlib/Dates/src/types.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index d28523c743d6f..f3a8bdaf0ae9b 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -278,10 +278,11 @@ function Date(y::Int64, m::Int64=1, d::Int64=1) return Date(UTD(totaldays(y, m, d))) end +const DATE_YEAR_TYPEMAX = 252522163911149 function validargs(T::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)))") - year(typemin(T)) <= y <= year(typemax(T)) || return ArgumentError("Year: $y out of range ($(year(typemin(T))):$(year(typemax(T))))") + -DATE_YEAR_TYPEMAX <= y <= DATE_YEAR_TYPEMAX || return ArgumentError("Year: $y out of range ($(-DATE_YEAR_TYPEMAX):$DATE_YEAR_TYPEMAX)") return nothing end @@ -464,8 +465,8 @@ 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{Date, Type{Date}}) = Date(DATE_YEAR_TYPEMAX, 12, 31) +Base.typemin(::Union{Date, Type{Date}}) = Date(-DATE_YEAR_TYPEMAX, 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, == From 37b781c573ab922fb7bcc5d12a3c10dda50c670f Mon Sep 17 00:00:00 2001 From: Mary McGrath Date: Tue, 25 Jun 2024 17:18:43 -0400 Subject: [PATCH 4/8] fix: datetime has a different year typemax --- stdlib/Dates/src/types.jl | 6 ++++-- stdlib/Dates/test/types.jl | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index f3a8bdaf0ae9b..3b282b117f419 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -249,8 +249,10 @@ function DateTime(y::Int64, m::Int64=1, d::Int64=1, return DateTime(UTM(rata)) end +const DATETIME_YEAR_TYPEMAX = 146138512 function validargs(::Type{DateTime}, y::Int64, m::Int64, d::Int64, h::Int64, mi::Int64, s::Int64, ms::Int64, ampm::AMPM=TWENTYFOURHOUR) + -DATETIME_YEAR_TYPEMAX <= y <= DATETIME_YEAR_TYPEMAX || return ArgumentError("Year: $y out of range ($(-DATETIME_YEAR_TYPEMAX):$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 @@ -463,8 +465,8 @@ 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{DateTime, Type{DateTime}}) = DateTime(DATETIME_YEAR_TYPEMAX, 12, 31, 23, 59, 59) +Base.typemin(::Union{DateTime, Type{DateTime}}) = DateTime(-DATETIME_YEAR_TYPEMAX, 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_TYPEMAX, 1, 1) Base.typemax(::Union{Time, Type{Time}}) = Time(23, 59, 59, 999, 999, 999) diff --git a/stdlib/Dates/test/types.jl b/stdlib/Dates/test/types.jl index 3ad61e41cb07d..21b02913b4387 100644 --- a/stdlib/Dates/test/types.jl +++ b/stdlib/Dates/test/types.jl @@ -162,8 +162,8 @@ end @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(Date))+1, 1, 1) - @test_throws ArgumentError Dates.DateTime(year(typemin(Date))-1, 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) From f245773c4e0f7f80a9544e7e72f2fa30443c3180 Mon Sep 17 00:00:00 2001 From: Mary McGrath Date: Tue, 25 Jun 2024 17:33:55 -0400 Subject: [PATCH 5/8] fix: remove unneccessary T --- stdlib/Dates/src/types.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index 3b282b117f419..29430d33f5808 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -281,7 +281,7 @@ function Date(y::Int64, m::Int64=1, d::Int64=1) end const DATE_YEAR_TYPEMAX = 252522163911149 -function validargs(T::Type{Date}, y::Int64, m::Int64, d::Int64) +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_TYPEMAX <= y <= DATE_YEAR_TYPEMAX || return ArgumentError("Year: $y out of range ($(-DATE_YEAR_TYPEMAX):$DATE_YEAR_TYPEMAX)") From c785f4ca3c6bd9d680bc6aa6c1009468745cb93b Mon Sep 17 00:00:00 2001 From: Mary McGrath Date: Tue, 25 Jun 2024 18:29:30 -0400 Subject: [PATCH 6/8] fix: min/max assymmetry --- stdlib/Dates/src/types.jl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index 29430d33f5808..02d4a46cb571d 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -250,9 +250,10 @@ function DateTime(y::Int64, m::Int64=1, d::Int64=1, 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_TYPEMAX <= y <= DATETIME_YEAR_TYPEMAX || return ArgumentError("Year: $y out of range ($(-DATETIME_YEAR_TYPEMAX):$DATETIME_YEAR_TYPEMAX)") + 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 @@ -281,10 +282,11 @@ function Date(y::Int64, m::Int64=1, d::Int64=1) end const DATE_YEAR_TYPEMAX = 252522163911149 +const DATE_YEAR_TYPEMIN = -252522163911148 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_TYPEMAX <= y <= DATE_YEAR_TYPEMAX || return ArgumentError("Year: $y out of range ($(-DATE_YEAR_TYPEMAX):$DATE_YEAR_TYPEMAX)") + DATE_YEAR_TYPEMIN <= y <= DATE_YEAR_TYPEMAX || return ArgumentError("Year: $y out of range ($(DATE_YEAR_TYPEMIN):$DATE_YEAR_TYPEMAX)") return nothing end @@ -466,9 +468,9 @@ Base.zero(::T) where T <: TimeType = zero(T)::Period Base.typemax(::Union{DateTime, Type{DateTime}}) = DateTime(DATETIME_YEAR_TYPEMAX, 12, 31, 23, 59, 59) -Base.typemin(::Union{DateTime, Type{DateTime}}) = DateTime(-DATETIME_YEAR_TYPEMAX, 1, 1, 0, 0, 0) +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_TYPEMAX, 1, 1) +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, == From c7cefd04e4e044a40e95ed83afae4f0effa7932d Mon Sep 17 00:00:00 2001 From: Mary McGrath Date: Tue, 25 Jun 2024 19:23:59 -0400 Subject: [PATCH 7/8] fix: date typemin --- stdlib/Dates/src/types.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index 02d4a46cb571d..1b941764fa4c6 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -282,7 +282,7 @@ function Date(y::Int64, m::Int64=1, d::Int64=1) end const DATE_YEAR_TYPEMAX = 252522163911149 -const DATE_YEAR_TYPEMIN = -252522163911148 +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)))") From 459fcb53f0c4082f77f1725b44c0755c18616868 Mon Sep 17 00:00:00 2001 From: Mary McGrath Date: Tue, 25 Jun 2024 19:40:24 -0400 Subject: [PATCH 8/8] fix: try to prevent overshooting on range calculation --- stdlib/Dates/src/ranges.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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