Skip to content

Date and DateTime should reject year inputs that they can't store #50981

@Seelengrab

Description

@Seelengrab

This MWE was found via fuzzing through PropCheck.jl:

julia> using PropCheck, Dates

julia> year_invariant_dt(y) = try
    return Year(DateTime(y)) === Year(y)
catch 
    return true # caught bad input
end
year_invariant_dt (generic function with 1 method)

julia> year_invariant_d(y) = try 
    return Year(Date(y)) === Year(y)
catch
    return true # caught bad input
end
year_invariant_d (generic function with 1 method)

julia> check(year_invariant_d, PropCheck.iposint(Int, PropCheck.shrinkTowards(typemin(Int))); transform = y -> (Date(y), Year(Date(y)), y))
┌ Info: Found counterexample for 'year_invariant_d', beginning shrinking...
└   Counterexample = 7514048394603581534
[ Info: 842 counterexamples found for year_invariant_d
(Date("-252522163911150-6028347736506387-29"), Year(-252522163911150), 252522163911151)

julia> check(year_invariant_d, PropCheck.inegint(Int, PropCheck.shrinkTowards(typemax(Int))); transform = y -> (Date(y), Year(Date(y)), y))
┌ Info: Found counterexample for 'year_invariant_d', beginning shrinking...
└   Counterexample = -8422004767203074864
[ Info: 784 counterexamples found for year_invariant_d
(Date("252522163911150--6028347736506385--26"), Year(252522163911150), -252522163911151)

julia> check(year_invariant_dt, PropCheck.iposint(Int, PropCheck.shrinkTowards(typemin(Int))); transform = y -> (DateTime(y), Year(DateTime(y)), y))
┌ Info: Found counterexample for 'year_invariant_dt', beginning shrinking...
└   Counterexample = 8427913184424949470
[ Info: 1317 counterexamples found for year_invariant_dt
(DateTime("-292277024-09-29T09:34:08.384"), Year(-292277024), 292277026)

julia> check(year_invariant_dt, PropCheck.inegint(Int, PropCheck.shrinkTowards(typemax(Int))); transform = y -> (DateTime(y), Year(DateTime(y)), y))
┌ Info: Found counterexample for 'year_invariant_dt', beginning shrinking...
└   Counterexample = -7698482265388645055
[ Info: 1060 counterexamples found for year_invariant_dt
(DateTime("292277025-04-03T14:25:51.616"), Year(292277025), -292277024)

The DateTime and Date constructors check all their other arguments, so I'd expect the year to be checked as well. For display purposes only, the found values are transformed into a 3-tuple with the contents:

  • Date/DateTime dt that was constructed (you can see the bad results in here)
  • Year(dt) that was received from the bad Date/DateTime
  • the year that was given to the constructor

which you can see below the last [ Info: per invocation of check above.

The property that was expected to hold is "The year given to the constructor is the same I get back out, or the constructor rejected the input."

I'm not 100% sure the values found are actually minimally maximal/maximally minimal, as the property only checks the year. It may very well be that there are representable dates larger/smaller than these, where not the whole year can be created without overflowing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    correctness bug ⚠Bugs that are likely to lead to incorrect results in user code without throwingdatesDates, times, and the Dates stdlib module

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions