Different behavior between Dbeaver and OSD clients with dates

I use Oracle Database 11g Release 11.2.0.4.0

When doing this query :

SELECT 
    CASE WHEN (DATE '2005-11-25') IN ('25/11/05') THEN
        'TRUE'
    ELSE
        'FALSE'
    END
FROM DUAL;

I get FALSE from Oracle SQL Developer 21.4.1.349 (French version) and TRUE from DBeaver 21.1.4.202108020335 (French version).

I understand that I compare a date with a string, so in my opinion the result should be FALSE, but that may me implementation-dependant.

How does it come that Oracle answers differently in those situations ? Does DBeaver do a preprocessing on dates before submitting the request?


Solution 1:

It depends on the NLS_DATE_FORMAT the session is using.

Oracle will implicitly convert your query to the equivalent of:

SELECT CASE WHEN DATE '2005-11-25' IN (
              TO_DATE(
                '25/11/05',
                (SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT')
              )
            )
       THEN 'TRUE'
       ELSE 'FALSE'
       END
FROM   DUAL;

As it relies on an implicit conversion from string to date so Oracle will use the session's date format in the conversion.


For example:

ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/RR';

SELECT CASE WHEN DATE '2005-11-25' IN ('25/11/05')
       THEN 'TRUE'
       ELSE 'FALSE'
       END
FROM   DUAL;

Outputs: TRUE

But:

ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI:SS';

SELECT CASE WHEN DATE '2005-11-25' IN ('25/11/05')
       THEN 'TRUE'
       ELSE 'FALSE'
       END
FROM   DUAL;

Outputs: FALSE

You can see why using:

SELECT TO_CHAR(CAST('25/11/05' AS DATE), 'YYYY-MM-DD HH24:MI:SS') FROM DUAL;

Which outputs 0005-11-25 00:00:00 and is the 1st century, and not the 21st century, as the implicit conversion uses the DD/MM/YYYY HH24:MI:SS format model and expects a 4-digit year and gets the input 05 and assumes it to be a correct value of 5 AD rather than the 5th year of the current century (which you would get with the YY format model).


The best solution is to not rely on implicit string-to-date conversions.

Either using date literals:

SELECT CASE WHEN DATE '2005-11-25' IN (DATE '2005-11-25')
       THEN 'TRUE'
       ELSE 'FALSE'
       END
FROM   DUAL;

Or, providing an explicit format model for the conversion:

SELECT CASE WHEN DATE '2005-11-25' IN (TO_DATE('25/11/05', 'DD/MM/RR'))
       THEN 'TRUE'
       ELSE 'FALSE'
       END
FROM   DUAL;

db<>fiddle here

Solution 2:

Because you are comparing a date to a string, you are causing the string to be implicitly converted to a date, using your session's NLS settings. Those may be derived from your locale, or may be set explicitly by your client, e.g. through preferences.

If NLS_DATE_FORMAT is is set to 'DD/MM/YYYY' then '25/11/05' is converted to 0005-11-25, so the comparison is false. If it is set to 'DD/MM/YY' (or 'DD/MM/RR', or 'DD/MM/RRRR') then it is converted to 2005-11-25, so the comparison is true.

db<>fiddle

You should not rely on implicit conversions, or NLS settings - since you can't control the environment of whoever runs your code.