You are on page 1of 10

The Oracle DATE column

Overview of DATE Datatype

This is a cut and paste straight from the manual:

The oracle DATE datatype column is used for storing point-in-time values (dates and times)
in a table. The DATE datatype stores the year (including the century), the month, the day, the
hours, the minutes, and the seconds (after midnight). Oracle can store dates in the Julian
era, ranging from January 1, 4712 BCE through December 31, 4712 CE (Common Era, or
'AD'). Unless BCE ('BC' in the format mask) is specifically used, CE date entries are the
default.

Oracle uses its own internal format to store dates. Date data is stored in fixed-length
fields of seven bytes each, corresponding to century, year, month, day, hour, minute,
and second. For input and output of dates, the standard Oracle date format is DD-MON-YY,
as follows: ’13-NOV-92’.

After that refreshing bit of technical writing how about we dig into the date field a little
bit. I have found that people have the problems with the DATE field in oracle databases.
The major issue is simply the name “DATE” most people go looking for a TIME
datatype and do not realize that the time is stored in the date field as well. The most
common mistake is people storing dates as a character field in a VARCHAR2 datatype
column. So I will delve into dates a little and hopefully make things a little easier for you
in the future. All examples are done on an Oracle 10gR2 database. From version 9i
onward there is also the TIMESTAMP column which has much more precision as well
the TIMESTAMP column can be created with built in time zone conversions. I will not
touching on the TIMESTAMP datatype at all in this document.

The example base data set.

SQL> DROP TABLE DATETEST;

Table dropped.

SQL>
SQL> CREATE TABLE DATETEST
2 (DATECOL DATE)
3 /

Table created.

SQL>
SQL> INSERT INTO DATETEST (DATECOL) VALUES (TO_DATE('01-JAN-2006 08:18:23','DD-
MON-YYYY HH24:MI:SS'));

1 row created.

SQL> COMMIT;

Commit complete.

Jeremy Birkett Page 1 of 10 11/7/2006


Displaying dates

As the manual says by default the display for a date column is DD-MON-YY. You have
to remember that is just the display of the date, not actually how the date is stored. You
can set your session to show the date format the way you desire. This is done by issuing

alter session set nls_date_format='DD/MM/YYYY';

For example

SQL> select datecol from datetest;

DATECOL
---------
01-JAN-06

SQL> alter session set nls_date_format='DD/MON/YYYY';

Session altered.

SQL> select datecol from datetest;

DATECOL
-----------
01/JAN/2006

The above query shows the default layout of a date via SQL*Plus, as shown you can
change the default layout via database parameters or session parameters.

Alternatively you can use the TO_CHAR function to display the date in a myriad of
formats. This does not show all of the possible format masks available. Please check the
documentation for all of the hundreds of possible combinations.
You can spell the date out in words.

SQL> SELECT TO_CHAR(DATECOL,'Day, Month dth yyyy') DATECOL FROM DATETEST;

DATECOL
-----------------------------
Sunday , January 1st 2006

You can change the time and the date format.

SQL> SELECT TO_CHAR(DATECOL,'DD-MON-YYYY HH pm') DATECOL FROM DATETEST;

DATECOL
-----------------
01-JAN-2006 08 am

Jeremy Birkett Page 2 of 10 11/7/2006


Some care must be taken in the layout mask chosen, as you can see below the output
looks the exact same but the data is actually very different.

SQL> SELECT TO_CHAR(DATECOL,'MM/DD/YYYY') DATECOL FROM DATETEST;

DATECOL
----------
01/01/2006

SQL> SELECT TO_CHAR(DATECOL,'DD/MM/YYYY') DATECOL FROM DATETEST;

DATECOL
----------
01/01/2006

You can go with a format that is easy to read.

SQL> SELECT TO_CHAR(DATECOL,'DD-MON-YYYY') DATECOL FROM DATETEST;

DATECOL
-----------
01-JAN-2006

When you format the date, you have the capability of putting in almost any delimiter
between the possible formats as you wish.

SQL> SELECT TO_CHAR(DATECOL,'YYYY~!~MM$#DD') DATECOL FROM DATETEST;

DATECOL
-------------
2006~!~01$#01

You can ignore the date and simply display the time.

SQL> SELECT TO_CHAR(DATECOL,'HH24:MI:SS') DATECOL FROM DATETEST;

DATECOL
--------
08:18:23

Jeremy Birkett Page 3 of 10 11/7/2006


Dates in GUI’s

GUI’s such as SQL Navigator and SQL Developer have a default date format that you
want to see. You must take care when setting those as it is possible to have the GUI
override format masks you apply to dates.

Converting text to date

When you have a character field you wish to display as a date, the function TO_DATE is
what you need. The quickest way to show this is to create a quick view that converts
characters to a date format.

SQL> create or replace view text2date as select to_date('05-JAN-2006','DD-MON-


YYY') text2date from d
ual;

View created.
SQL> desc text2date
Name Null? Type
----------------------------------------------- -------- -------------------
TEXT2DATE DATE

SQL> select text2date from text2date;

TEXT2DATE
---------
05-JAN-06

SQL> select to_char(text2date,'Day') text2date from text2date;

TEXT2DATE
---------
Thursday

You can see that the text field is converted to a true date format in the view and you can
then manipulate it as you see fit. When you are converting text to a date, you must match
the format of TO_DATE function exactly with the text you are passing in. Even a small
mistake will cause an error.

SQL> select to_date('09-JAN-2006','MON-DD-YYYY') from dual;


select to_date('09-JAN-2006','MON-DD-YYYY') from dual
*
ERROR at line 1:
ORA-01843: not a valid month

SQL> select to_date('01-JAN-06 11:00','DD-MON-YYYY HH24') from dual;


select to_date('01-JAN-06 11:00','DD-MON-YYYY HH24') from dual
*
ERROR at line 1:
ORA-01830: date format picture ends before converting entire input string

Jeremy Birkett Page 4 of 10 11/7/2006


The function does employ some “smarts” though, but it is best not to rely on that and
ensure your text is completely mapped.

SQL> select to_date('09-JAN-06','DD-MON-YYYY') from dual;

TO_DATE('
---------
09-JAN-06

Elapsed: 00:00:00.00
SQL> select to_date('09-JAN-2006','DD-MON-YY') from dual;

TO_DATE('
---------
09-JAN-06

Date Math
Getting the difference between Dates

Doing math with dates is remarkably easy but seems a little complicated at the beginning.
I need a new base data set, so here it is.

SQL> create table datemath


2 (start_date date
3 ,end_date date);

SQL> insert into datemath (start_date,end_date) values (to_date('01-OCT-2006


13:00:00','DD-MON-YYYY HH24:MI:SS'),to_date('01-OCT-2006 14:00:00','DD-MON-YYYY
HH24:MI:SS'));

1 row created.

SQL> commit;

Commit complete.

SQL> select to_char(start_date,'DD-MON-YYYY HH24:MI:SS') START_DATE


2 ,to_char(end_date,'DD-MON-YYYY HH24:MI:SS') END_DATE
3 from datemath;

START_DATE END_DATE
-------------------- --------------------
01-OCT-2006 13:00:00 01-OCT-2006 14:00:00

First thing we will work on is finding the difference between two dates
SQL> select end_date-start_date from datemath;

END_DATE-START_DATE
-------------------
.041666667

Jeremy Birkett Page 5 of 10 11/7/2006


Well that doesn’t look like what we are looking for, but what that shows is the number
(or part thereof) of days between the two dates. You simply have to do some simple
math to get what you are looking for. There are many ways to do the math I will show
what I find to be the easiest to remember.

Since we know that it is the number of days that is shown between to dates and there is
24 hours in a day, multiply the outcome to get the number of hours.

SQL> select (end_date-start_date)*24 hours from datemath;

HOURS
----------
1

Multiply by 24 and again by 60 to get the number of minutes

SQL> select (end_date-start_date)*24*60 minutes from datemath;

MINUTES
----------
60

Multiply by 24 and again by 60 and again by 60 to get the number of seconds

SQL> select (end_date-start_date)*24*60*60 seconds from datemath;

SECONDS
----------
3600

If you want more detail on your date math for periods that are maybe longer than a day,
you can do it very easily but with a fair amount of typing. First thing we need some more
test data.

SQL> truncate table datemath;


Table truncated.
SQL> insert into datemath (start_date,end_date) values (to_date('01-OCT-2006
13:10:00','DD-MON-YYYY HH24:MI:SS'),to_date('10-OCT-2006 14:00:00','DD-MON-YYYY
HH24:MI:SS'));
1 row created.

SQL> commit;

Commit complete.

Jeremy Birkett Page 6 of 10 11/7/2006


Now to do the SQL, it is simply some nested MOD’s of the outcome.

SQL> select to_char( start_date, 'dd-mon-yyyy hh24:mi:ss' ) start_date,


2 trunc( end_date-start_date ) days,
3 trunc( mod( (end_date-start_date)*24, 24 ) ) hours,
4 trunc( mod( (end_date-start_date)*24*60, 60 ) ) Minutes,
5 trunc( mod( (end_date-start_date)*24*60*60, 60 ) ) Seconds,
6 to_char( end_date, 'dd-mon-yyyy hh24:mi:ss' ) end_date
7* from datemath
SQL> /

START_DATE Days HOURS MINUTES SECONDS END_DATE


-------------------- ---------- ---------- ---------- ---------- --------------------
01-oct-2006 13:10:00 9 0 50 0 10-oct-2006 14:00:00

SQL>

I had to shrink the font on the output so it would all fit on one line. You can see now you
know the exact time between the two dates.

Adding or Subtracting time

Adding or subtracting time to a date is similar to working out the difference between.
When you add a number to a date, oracle assumes that number is in days, or part thereof.

SQL> select start_date from datemath;

START_DAT
---------
01-OCT-06

SQL> select start_date,start_date+1,start_date+10 from datemath;

START_DAT START_DAT START_DAT


--------- --------- ---------
01-OCT-06 02-OCT-06 11-OCT-06

You can easily add or subtract any combination of time you need

SQL> alter session set nls_date_format='DD-MON-YYYY HH24:MI:SS';

Session altered.

SQL> select
2 ,start_date,start_date+(1/24) onehour
3 ,start_date+(1/24/60) oneminute
4 ,start_date+(1/24/60/60) onesecond
5 from datemath;

START_DATE ONEHOUR ONEMINUTE ONESECOND


-------------------- -------------------- -------------------- --------------------
01-OCT-2006 13:10:00 01-OCT-2006 14:10:00 01-OCT-2006 13:11:00 01-OCT-2006 13:10:01

Jeremy Birkett Page 7 of 10 11/7/2006


SQL> select start_date,start_date - (1/24) onehour
2 ,start_date-(1/24)-(1/24/60) onehouroneminute
3 from datemath;

START_DATE ONEHOUR ONEHOURONEMINUTE


-------------------- -------------------- --------------------
01-OCT-2006 13:10:00 01-OCT-2006 12:10:00 01-OCT-2006 12:09:00

Queries using dates

The common question is “Why use a date field? We only want to store a date for a day,
VARCHAR makes more sense”.

Admittedly at first glance using a VARCHAR field to store just a date looks pretty darn
attractive. You don’t have to worry about formatting during queries or reports and not
standard configuration on the client end won’t cause data problems, for example if the
GUI was configured to read the date formatting of the local windows PC. I will do some
very simple tests to show why there is a problem with using VARCHAR2 as a date field.

That means we get to create a new test table! Don’t you feel excited? I do!
SQL> CREATE TABLE DATETEST
2 (ID NUMBER PRIMARY KEY
3 ,TEXT_DATE VARCHAR2(17)
4 ,REAL_DATE DATE
5 );

Table created.

Now we get to generate test data!


SQL> INSERT INTO DATETEST (ID,TEXT_DATE,REAL_DATE)
2 SELECT ROWNUM,TO_CHAR(TESTDATE,'DD-MON-YYYY HH24:MI') TEXT_DATE,TESTDATE
REAL_DATE
3 FROM (SELECT
4 TO_DATE(ROWNUM||'-JUN-2006
'||FLOOR(DBMS_RANDOM.VALUE(1,24))||':'||FLOOR(DBMS_RANDOM.VALUE(1,59)),'DD-MON-
YYYY HH24:MI') TESTDATE
5 FROM
6 DUAL
7 CONNECT BY LEVEL <= 30
8 UNION
9 SELECT
10 TO_DATE(ROWNUM||'-JUL-2006
'||FLOOR(DBMS_RANDOM.VALUE(1,24))||':'||FLOOR(DBMS_RANDOM.VALUE(1,59)),'DD-MON-
YYYY HH24:MI') TESTDATE
11 FROM
12 DUAL
13 CONNECT BY LEVEL <= 31) ORDER BY DBMS_RANDOM.VALUE
14 ;

61 rows created.

SQL> COMMIT;

Jeremy Birkett Page 8 of 10 11/7/2006


Commit complete.

Now we have two months worth of data with a random time entry for every day of each
month. Now to start with a simple query on the table and order by the TEXT_DATE
column.

SQL> SELECT * FROM DATETEST ORDER BY TEXT_DATE;

ID TEXT_DATE REAL_DATE
---------- ----------------- ---------
31 01-JUL-2006 13:03 01-JUL-06
1 01-JUN-2006 13:14 01-JUN-06
32 02-JUL-2006 04:36 02-JUL-06
2 02-JUN-2006 15:43 02-JUN-06
33 03-JUL-2006 08:03 03-JUL-06
3 03-JUN-2006 11:05 03-JUN-06
<results removed>
59 29-JUL-2006 21:42 29-JUL-06
29 29-JUN-2006 17:21 29-JUN-06
60 30-JUL-2006 14:16 30-JUL-06
30 30-JUN-2006 02:07 30-JUN-06
61 31-JUL-2006 04:08 31-JUL-06

61 rows selected.

That is very interesting, according to our query the 1st of July comes before the 1st of
June. Must be a calendar I am unfamiliar with. This outcome was to be expected
because as we all know, text sorts differently than date. Now some of you may be saying
“you chose that date format on purpose to fail”. Well, no not really, eventually they all
fail unless you choose MMDDYYYYHH24MISS format then the readability and not to
format the output benefits.

Now to do the same simple query ordering by the REAL_DATE column

SQL> SELECT * FROM DATETEST ORDER BY REAL_DATE;

ID TEXT_DATE REAL_DATE
---------- ----------------- ---------
1 01-JUN-2006 13:14 01-JUN-06
2 02-JUN-2006 15:43 02-JUN-06
3 03-JUN-2006 11:05 03-JUN-06
4 04-JUN-2006 10:54 04-JUN-06
5 05-JUN-2006 09:44 05-JUN-06
6 06-JUN-2006 01:15 06-JUN-06
<results removed>
58 28-JUL-2006 15:44 28-JUL-06
59 29-JUL-2006 21:42 29-JUL-06
60 30-JUL-2006 14:16 30-JUL-06
61 31-JUL-2006 04:08 31-JUL-06

61 rows selected.

Now if we use a predicate to find the data, first on the TEXT_DATE column.

Jeremy Birkett Page 9 of 10 11/7/2006


SQL> SELECT * FROM DATETEST WHERE TEXT_DATE BETWEEN '10-JUN-2006' AND '02-JUL-
2006' ORDER BY TEXT_DATE;

no rows selected

Now the same query using the REAL_DATE column.

SQL> SELECT * FROM DATETEST WHERE REAL_DATE BETWEEN TO_DATE('10-JUN-2006','DD-


MON-YYYY') AND TO_DATE('02-JUL-2006','DD-MON-YYYY') ORDER BY REAL_DATE;

ID TEXT_DATE REAL_DATE
---------- ----------------- ---------
10 10-JUN-2006 03:28 10-JUN-06
11 11-JUN-2006 11:40 11-JUN-06
12 12-JUN-2006 16:18 12-JUN-06
<results removed>
29 29-JUN-2006 17:21 29-JUN-06
30 30-JUN-2006 02:07 30-JUN-06
31 01-JUL-2006 13:03 01-JUL-06

22 rows selected.

Conclusion
The DATE datatype is very easy to work with once you understand the basics and you
can quickly and efficiently manipulate the DATE field in the output format you desire
and do calculations between 2 dates. Use DATE columns to store dates and times
because generally the DATE column is much more efficient and easier to work with than
using VARCHAR2 or NUMBER fields to store a date . There are probably some
particular cases that can devised when using a VARCHAR2 field to store a date value, or
even using a NUMBER field to choose a date value may be more efficient that a DATE
field but those examples would be the exceptions to the rule.

Jeremy Birkett Page 10 of 10 11/7/2006

You might also like