sql-servert-sqlsql-server-2022

Why ISDATE() is returning 9996 as date?


Question: I'm using the latest version of SQL Server 2022 Enterprise edition with SSMS ver 20.

Why do all statements - except for the last line - return 1 as value? All my settings are default. I have made no changes to configuration.

select ISDATE(9996) --returns 1, why?
select ISDATE(8234)
select ISDATE(7234)
select ISDATE(6234)
select ISDATE(5234)
select ISDATE(4234)
select ISDATE(3234)
select ISDATE(2234)
select ISDATE(1234) --returns 0, as expected

Solution

  • Question 1:

    Why does ISDATE( 9996 ) indicate that 9996 "is" a date?

    Because ISDATE(9996) expects a character-string argument, so the integer value 9996 is implicitly converted to the string '9996', which is then interpreted as a year-number, which can be widened to a datetime value by assuming it's midnight on the January 1st of that year-number: so '9996' is interpreted as 9996-Jan-01, which is a real date - just one that's some 7,971 years into the future...

    This implicit conversion is described in the documentation:

    ISDATE( <expression> )
    

    <expression> is a character string or expression that can be converted to a character string


    Question 2:

    Why does ISDATE( 2234 ) == 1, but ISDATE( 1234 ) == 0?

    Come with me on a journey through history...

    ...accordingly, 2234 is a valid year (as is 9996, 8234, etc), but 1234 is not a valid year because it predates 1753.

    Proof:

    SELECT
        t.int_arg,
        v.is_date,
        v.is_date_chr,
        v.as_date,
        v.as_old_and_horrible_datetime
    FROM ( 
        VALUES  (10000), (9999) ,(9996), (8234), (7234), (6234), (5234), (4234)
        , (3234), (2234), (1754), (1753), (1752), (1751), (1234)
    ) t (int_arg)
    CROSS APPLY (
        SELECT  ISDATE(int_arg) AS is_date
        ,   ISDATE(CAST(int_arg AS VARCHAR(30))) AS is_date_chr
        ,   TRY_CONVERT(date, CAST(int_arg AS VARCHAR(30))) AS as_date
        ,   TRY_CONVERT(datetime, CAST(int_arg AS VARCHAR(30))) AS as_old_and_horrible_datetime
        ) v
    

    ...has these results - note the transition between 1753 and 1752:

    int_arg is_date is_date_chr as_date as_old_and_horrible_datetime
    10000 0 0 NULL NULL
    9999 1 1 9999-01-01 9999-01-01 00:00:00.000
    9996 1 1 9996-01-01 9996-01-01 00:00:00.000
    8234 1 1 8234-01-01 8234-01-01 00:00:00.000
    7234 1 1 7234-01-01 7234-01-01 00:00:00.000
    6234 1 1 6234-01-01 6234-01-01 00:00:00.000
    5234 1 1 5234-01-01 5234-01-01 00:00:00.000
    4234 1 1 4234-01-01 4234-01-01 00:00:00.000
    3234 1 1 3234-01-01 3234-01-01 00:00:00.000
    2234 1 1 2234-01-01 2234-01-01 00:00:00.000
    1754 1 1 1754-01-01 1754-01-01 00:00:00.000
    1753 1 1 1753-01-01 1753-01-01 00:00:00.000
    1752 0 0 1752-01-01 NULL
    1751 0 0 1751-01-01 NULL
    1234 0 0 1234-01-01 NULL

    And here's a DB-Fiddle for good measure.