Date and Time Functions in SQL
We live in a civilization governed by times and dates, and most major implementations of SQL have functions to cope with these concepts.
Date and Time Functions
This blog uses the table PROJECT to demonstrate the time and date functions.
SQL> SELECT * FROM PROJECT;
TASK | STARTDATE | ENDDATE |
---|---|---|
KICKOFF MTG | 01-APR-95 | 01-APR-95 |
TECH SURVEY | 02-APR-95 | 01-MAY-95 |
USER MTGS | 15-MAY-95 | 30-MAY-95 |
DESIGN WIDGET | 01-JUN-95 | 30-JUN-95 |
CODE WIDGET | 01-JUL-95 | 02-SEP-95 |
TESTING | 03-SEP-95 | 17-JAN-96 |
ADD_MONTHS
This function adds a number of months to a specified date. For example, say something extraordinary happened, and the preceding project slipped to the right by two months. We could make a new schedule by typing
SQL> SELECT TASK, STARTDATE, ENDDATE ORIGINAL_END, ADD_MONTHS(ENDDATE,2)FROM
PROJECT;
TASK | STARTDATE | ORIGINAL_ | ADD_MONTH |
---|---|---|---|
KICKOFF MTG | 01-APR-95 | 01-APR-95 | 01-JUN-95 |
TECH SURVEY | 02-APR-95 | 01-MAY-95 | 01-JUL-95 |
USER MTGS | 15-MAY-95 | 30-MAY-95 | 30-JUL-95 |
DESIGN WIDGET | 01-JUN-95 | 30-JUN-95 | 31-AUG-95 |
CODE WIDGET | 01-JUL-95 | 02-SEP-95 | 02-NOV-95 |
TESTING | 03-SEP-95 | 17-JAN-96 | 17-MAR-96 |
Not that a slip like this is possible, but it’s nice to have a function that makes it so easy. ADD_MONTHS also works outside the SELECT clause
SQL> SELECT TASK TASKS_SHORTER_THAN_ONE_MONTH FROM PROJECT WHERE
ADD_MONTHS(STARTDATE,1) > ENDDATE;
TASKS_SHORTER_THAN_ONE_MONTH
----------------------------
KICKOFF MTG
TECH SURVEY
USER MTGS
DESIGN WIDGET
Analysis: ***### we will find that all the functions in this section work in more than one place. However, ADD MONTHS does not work with other data types like character or number without the help of functions TO_CHAR and TO_DATE, which are discussed later today.
LAST_DAY
LAST_DAY returns the last day of a specified month. It is for those of us who haven’t mastered the Thirty days has September...
rhyme or at least those of us who have not yet taught it to our computers. If, for example, we need to know what the last day of the month is in the column ENDDATE, we would type
SQL> SELECT ENDDATE, LAST_DAY(ENDDATE) FROM PROJECT;
ENDDATE | LAST_DAY(ENDDATE) |
---|---|
01-APR-95 | 30-APR-95 |
01-MAY-95 | 31-MAY-95 |
30-MAY-95 | 31-MAY-95 |
30-JUN-95 | 30-JUN-95 |
02-SEP-95 | 30-SEP-95 |
17-JAN-96 | 31-JAN-96 |
How does LAST DAY handle leap years?
SQL>SELECT LAST_DAY('1-FEB-95') NON_LEAP,LAST_DAY('1-FEB-96')LEAP FROM PROJECT;
NON_LEAP | LEAP |
---|---|
28-FEB-95 | 29-FEB-96 |
28-FEB-95 | 29-FEB-96 |
28-FEB-95 | 29-FEB-96 |
28-FEB-95 | 29-FEB-96 |
28-FEB-95 | 29-FEB-96 |
28-FEB-95 | 29-FEB-96 |
Analysis: We got the right result, but why were so many rows returned? Because we didn’t specify an existing column or any conditions, the SQL engine applied the date functions in the statement to each existing row. Let’s get something less redundant by using the following:
SQL>SELECT DISTINCT LAST_DAY('1-FEB-95') NON_LEAP,LAST_DAY('1-FEB-96') LEAP
FROM PROJECT;
This statement uses the word DISTINCT to produce the singular result
NON_LEAP LEAP
--------- ---------
28-FEB-95 29-FEB-96
Unlike we, this function knows which years are leap years. But before we trust our own or our company’s financial future to this or any other function, check our implementation!
MONTHS_BETWEEN
If we need to know how many months fall between month x and month y, use MONTHS_BETWEEN like this:
SQL> SELECT TASK,STARTDATE,ENDDATE,MONTHS_BETWEEN(STARTDATE,ENDDATE)DURATION
FROM PROJECT;
TASK | STARTDATE | ENDDATE | DURATION |
---|---|---|---|
KICKOFF MTG | 01-APR-95 | 01-APR-95 | o |
TECH SURVEY | 02-APR-95 | 01-MAY-95 | -.9677419 |
USER MTGS | 15-MAY-95 | 30-MAY-95 | -.483871 |
DESIGN WIDGET | 01-JUN-95 | 30-JUN-95 | -.9354839 |
CODE WIDGET | 01-JUL-95 | 02-SEP-95 | -2.032258 |
TESTING | 03-SEP-95 | 17-JAN-96 | -4.451613 |
Wait a minute–that doesn’t look right. Try this:
SQL> SELECT TASK, STARTDATE,ENDDATE,MONTHS_BETWEEN(ENDDATE,STARTDATE) DURATION
FROM PROJECT;
TASK | STARTDATE | ENDDATE | DURATION |
---|---|---|---|
KICKOFF MTG | 01-APR-95 | 01-APR-95 | 0 |
TECH SURVEY | 02-APR-95 | 01-MAY-95 | .96774194 |
USER MTGS | 15-MAY-95 | 30-MAY-95 | .48387097 |
DESIGN WIDGET | 01-JUN-95 | 30-JUN-95 | .93548387 |
CODE WIDGET | 01-JUL-95 | 02-SEP-95 | 2.0322581 |
TESTING | 03-SEP-95 | 17-JAN-96 | 4.4516129 |
Analysis:
That’s better. We see that MONTHS_BETWEEN is sensitive to the way we order the months. Negative months might not be bad. For example, we could use a negative result to determine whether one date happened before another. For example, the following statement shows all the tasks that started before May 19, 1995:
SQL> SELECT * FROM PROJECT WHERE MONTHS_BETWEEN('19 MAY 95', STARTDATE) > 0;
TASK | STARTDATE | ENDDATE |
---|---|---|
KICKOFF MTG | 01-APR-95 | 01-APR-95 |
TECH SURVEY | 02-APR-95 | 01-MAY-95 |
USER MTGS | 15-MAY-95 | 30-MAY-95 |
NEXT_DAY
NEXT_DAY finds the name of the first day of the week that is equal to or later than another specified date. For example, to send a report on the Friday following the first day of each event, we would type
SQL> SELECT STARTDATE,NEXT_DAY(STARTDATE, 'FRIDAY')FROM PROJECT;
STARTDATE | NEXT_DAY( |
---|---|
01-APR-95 | 07-APR-95 |
02-APR-95 | 07-APR-95 |
15-MAY-95 | 19-MAY-95 |
01-JUN-95 | 02-JUN-95 |
01-JUL-95 | 07-JUL-95 |
03-SEP-95 | 08-SEP-95 |
Analysis: The output tells us the date of the first Friday that occurs after our STARTDATE.
SYSDATE
SYSDATE returns the system time and date:
SQL> SELECT DISTINCT SYSDATE FROM PROJECT;
SYSDATE
----------------
18-JUN-95 1020PM
If we wanted to see where we stand today in a certain project, we can type
SQL> SELECT * FROM PROJECT WHERE STARTDATE > SYSDATE;
TASK | STARTDATE | ENDDATE |
---|---|---|
CODE WIDGET | 01-JUL-95 | 02-SEP-95 |
TESTING | 03-SEP-95 | 17-JAN-96 |
Now we can see what parts of the project start after today.
⌖ sql rdbms functions