When Oracle implicitly converts from a DATE to a string or vice-versa (or when TO_CHAR()(opens new window) or TO_DATE()(opens new window) are explicitly called without a format model) the NLS_DATE_FORMAT session parameter will be used as the format model in the conversion. If the literal does not match the format model then an exception will be raised.
You can review this parameter using:
SELECTVALUEFROM NLS_SESSION_PARAMETERS WHERE PARAMETER ='NLS_DATE_FORMAT';
You can set this value within your current session using:
(Note: this does not change the value for any other users.)
If you rely on the NLS_DATE_FORMAT to provide the format mask in TO_DATE() or TO_CHAR() then you should not be surprised when your queries break if this value is ever changed.
Date Arithmetic - Difference between Dates in Months or Years
SELECT MONTHS_BETWEEN(DATE'2016-03-10',DATE'2015-03-10')AS difference FROM DUAL;
Outputs:
DIFFERENCE
----------12
If the difference includes part months then it will return the fraction of the month based on there being 31 days in each month:
SELECT MONTHS_BETWEEN(DATE'2015-02-15',DATE'2015-01-01')AS difference FROM DUAL;
Outputs:
DIFFERENCE
----------1.4516129
Due to MONTHS_BETWEEN assuming 31 days per month when there can be fewer days per month then this can result in different values for differences spanning the boundaries between months.
The difference in years can be found by dividing the month difference by 12.
Generating Dates with No Time Component
All DATEs have a time component; however, it is customary to store dates which do not need to include time information with the hours/minutes/seconds set to zero (i.e. midnight).
(If you are converting language specific terms such as month names then it is good practice to include the 3rd nlsparam parameter to the TO_DATE() function and specify the language to be expected.)
Oracle will implicitly cast a TIMESTAMP to a DATE when storing it in a DATE column of a table; however you can explicitly CAST()(opens new window) the value to a DATE:
In Oracle a DATE data type does not have a format; when Oracle sends a DATE to the client program (SQL/Plus, SQL/Developer, Toad, Java, Python, etc) it will send 7- or 8- bytes which represent the date.
A DATE which is not stored in a table (i.e. generated by SYSDATE and having "type 13" when using the DUMP() command) has 8-bytes and has the structure (the numbers on the right are the internal representation of 2012-11-26 16:41:09):
BYTE VALUE EXAMPLE
---- ------------------------------- --------------------------------------1Year modulo 2562202Year multiples of2567(7*256+220=2012)3Month114Day265 Hours 166 Minutes 417 Seconds 98 Unused 0
A DATE which is stored in a table ("type 12" when using the DUMP() command) has 7-bytes and has the structure (the numbers on the right are the internal representation of 2012-11-26 16:41:09):
BYTE VALUE EXAMPLE
---- ------------------------------- --------------------------------------1(Year multiples of100)+1001202(Year modulo 100)+100112((120-100)*100+(112-100)=2012)3Month114Day265 Hours +1176 Minutes +1427 Seconds +110
(Note: if a format model(opens new window) is not provided then the NLS_DATE_FORMAT session parameter will be used as the default format model(opens new window); this can be different for every session so should not be relied on. It is good practice to always specify the format model.)
However, this is dependent on the NLS_TERRITORY session parameter:
ALTERSESSIONSET NLS_TERRITORY ='AMERICA';-- First day of week is SundaySELECT TO_CHAR(DATE'1970-01-01','D')FROM DUAL;
Outputs 5
ALTERSESSIONSET NLS_TERRITORY ='UNITED KINGDOM';-- First day of week is MondaySELECT TO_CHAR(DATE'1970-01-01','D')FROM DUAL;
Outputs 4
To do this independent of the NLS settings, you can truncate the date to midnight of the current day (to remove any fractions of days) and subtract the date truncated to the start of the current iso-week (which always starts on Monday):