Date Data Types
Week 9: Tutorials
DATE
Data Types
Learning Outcomes
- Learn how to create, modify, and remove
DATE
columns. - Learn how perform math with
DATE
column values.
Dates and timestamps are DATE
and TIMESTAMP
data types respectively. Their implementation is through complex or real numbers. The integer value represents the date, and the decimal value implements time. The range of dates or timestamps is an epoch. An epoch is a set of possible dates and date-times that are valid in the database server.
The DATE
data type in an Oracle database is a date-time value. As such, you can assign a date-time that is accurate to hundredths of a second. The default date format mask in an Oracle database is dd-mon-rr
or dd-mon-yyyy
. The rr
stands for relative date, and the database server chooses whether the date belongs in the current or last century. The yyyy
format mask requires that you explicitly assign the four-digit year to dates.
You use the TRUNC
function to return only the integer portion of a DATE
, which yields midnight of the day. You use the TO_CHAR function with a format mask to see the date value down to the hundredth of a second. The following query returns the integer value generated by the SYSDATE
function and the actual time-stamp value generated by the SYSDATE
function.
COLUMN midnight FORMAT A22 HEADING "Midnight" COLUMN now FORMAT A22 HEADING "Now" SELECT TO_CHAR(SYSDATE,'DD-MON-YYYY HH24:MI:SS') AS midnight , TO_CHAR(TRUNC(SYSDATE),'DD-MON-YYYY HH24:MI:SS') AS now FROM dual; |
It returns the following values:
Midnight Now ---------------------- ---------------------- 07-OCT-2018 20:23:29 07-OCT-2018 00:00:00 |
The behavior of the TRUNC
function lets you return yesterday, today, and tomorrow with the following query:
COLUMN yesterday FORMAT A22 HEADING "Yesterday" COLUMN today FORMAT A22 HEADING "Today" COLUMN tomorrow FORMAT A22 HEADING "Tomorrow" SELECT TO_CHAR(TRUNC(SYSDATE - 1),'DD-MON-YYYY HH24:MI:SS') AS yesterday , TO_CHAR(TRUNC(SYSDATE),'DD-MON-YYYY HH24:MI:SS') AS today , TO_CHAR(TRUNC(SYSDATE + 1),'DD-MON-YYYY HH24:MI:SS') AS tomorrow FROM dual; |
It returns the following values:
Yesterday Today Tomorrow ---------------------- ---------------------- ---------------------- 06-OCT-2018 00:00:00 07-OCT-2018 00:00:00 08-OCT-2018 00:00:00 |
You can find the last day of the month and first day of the month by leveraging the ADD_MONTHS
and LAST_DAY
builtin functions. They’re powerful functions, as shown in the following query:
COLUMN first_day FORMAT A11 "First Day" COLUMN LAST_DAY FORMAT A11 "Last Day" SELECT ADD_MONTHS(LAST_DAY(SYSDATE),-1) + 1 AS first_day , LAST_DAY(SYSDATE) AS LAST_DAY FROM dual; |
The first day calculation shows you two key elements of Oracle SQL syntax. One is that you add a day to a date by using an integer. Second, the ADD_MONTHS
function lets you pass a negative integer to subtract a month.
It returns the following:
First Day Last Day ----------- ----------- 01-OCT-18 31-OCT-18 |
You also have the ability of calculating the hours, minutes, and seconds by performing computations on the date value. The following shows you how to calculate the time from midnight to the value returned by the SYSDATE
function:
COLUMN midnight FORMAT A22 HEADING "Midnight" COLUMN now FORMAT A22 HEADING "Now" COLUMN HOUR FORMAT 999 HEADING "Hour" COLUMN MINUTE FORMAT 999 HEADING "Minute" COLUMN SECOND FORMAT 999 HEADING "Second" SELECT TO_CHAR(SYSDATE,'DD-MON-YYYY HH24:MI:SS') AS midnight , TO_CHAR(TRUNC(SYSDATE),'DD-MON-YYYY HH24:MI:SS') AS now , FLOOR((SYSDATE - TRUNC(SYSDATE)) * 1440 / 60) AS HOUR , FLOOR(MOD((SYSDATE - TRUNC(SYSDATE)) * 1440,60)) AS MINUTE , (MOD((SYSDATE - TRUNC(SYSDATE)) * 1440,60) - FLOOR(MOD((SYSDATE - TRUNC(SYSDATE)) * 1440,60))) * 60 AS SECOND FROM dual; |
The query performs the following calculations:
- The hour value is calculated by subtracting the truncated current date, or midnight, from the current date-time value. Then, you multiply it by the number of minutes in the day and divide it by 60 minutes. Last, you use the
FLOOR
function to return the last whole integer value or hour of the day based on a 24-hour day. - The minute value is calculated by using the remainder of the same calculation used to find the number of hours. You use the
MOD
function performs modulo integer math, which returns the remainder after the division. TheFLOOR
function returns the returns the last whole integer value of minute of the hour. - The second value requires more effort. You subtract the last integer value of the minute from the raw minute value, which includes the integer for the last minute plus a decimal value. Then, you multiple the fractional value times 60 to get the number of seconds.
It returns the following:
Midnight Now Hour Minute Second ---------------------- ---------------------- ---- ------ ------ 07-OCT-2018 20:57:19 07-OCT-2018 00:00:00 20 57 19 |
While adding or subtracting integer values from DATE
data types increases or decreases days respectively, you use the INTERVAL
function to add or subtract minutes. Here’s an example that returns 15 minutes after midnight of today:
COLUMN fifteen FORMAT A22 HEADING "Fifteen Minutes|After Midnight" SELECT TO_CHAR(TRUNC(SYSDATE) + INTERVAL '15' MINUTE,'DD-MON-YYYY HH24:MI:SS') AS fifteen FROM dual; |
It shows you the following:
Fifteen Minutes After Midnight ---------------------- 07-OCT-2018 00:15:00 |
The balance of this tutorial shows you how to create DATE
columns and how to cast various data types when implicit casting fails to deliver what you want.
How to Create Tables with DATE
Columns
Tutorial Material →
The tutorial shows you how to create tables with a DATE
column.
SQL> CREATE TABLE sample_date 2 ( column_name DATE DEFAULT SYSDATE); |
A DATE
data type can be assigned a date-time that is equal to midnight by enclosing the date column in a TRUNC
function. The TRUNC
function shaves off any decimal value from the date- time value. The SYSDATE
is a current date-time function available inside an Oracle database. You would define a date-only value as a default value with this syntax:
SQL> CREATE TABLE sample_date 2 ( column_name DATE DEFAULT TRUNC(SYSDATE)); |
A more accurate timestamp and timestamps with local or general time zone are also available starting with the release of Oracle Database 11g. They’re more accurate because they measure time beyond hundredths of a second. You would define a TIMESTAMP
like this:
SQL> CREATE TABLE sample_timestamp 2 ( column_name TIMESTAMP DEFAULT SYSTIMESTAMP); |
You also have INTERVAL DAY TO SECOND
and INTERVAL DAY TO MONTH
data types. They measure intervening time (like the number of minutes or seconds between two timestamps), which is similar to measuring the difference between two decimal parts of DATE
data types.
Data Type Conversion Functions
Data type conversion is simply casting, which is the process of taking a variable defined as one data type and changing it to another data type. Implicit casting makes the change for you automatically but only works when the rules are simple and well understood. Explicit casting lets you instruct the programming language how to assign one data type to another when the programming language has no rule that applies to how the data type can be converted implicitly, or without formal programming instructions.
Data type conversion is often done implicitly in SQL and PL/SQL. Unlike other strongly typed programming languages, SQL and PL/SQL implicitly convert types even when there is a potential loss of precision. For example, you can assign a complex number in a NUMBER
data type to a SIMPLE_INTEGER
data type and lose any values to the right of the decimal point.
Data Type Conversions
Tutorial Material →
The tutorial demonstrates methods converting data types to DATE
data types.
Oracle supports a series of conversion functions that let you convert data types from one type to another. The generic SQL conversion function is CAST
, which lets you convert the following data types:
- Convert from
BINARY_FLOAT
orBINARY_DOUBLE
data type toBINARY_FLOAT
,BINARY_DOUBLE
,CHAR
,VARCHAR2
,NUMBER
,DATE
,TIMESTAMP
,NCHAR
,NVARCHAR
. - Convert from
CHAR
orVARCHAR2
data type toBINARY_FLOAT
,BINARY_DOUBLE
,CHAR
,VARCHAR2
,NUMBER
,DATE
,TIMESTAMP
,DATE
,TIMESTAMP
,INTERVAL
,RAW
,ROWID
,UROWID
,NCHAR
,NVARCHAR
. Here’s an example of converting a string literal date into a timestamp:CAST('14-FEB-2011' AS TIMESTAMP WITH LOCAL TIME ZONE)
This example works because the date literal conforms to the default format mask for a date in an Oracle database. A nonconforming date literal would raise a conversion error. Many possibilities are available, because you can organize the valid elements of dates many ways. A nonconforming date literal should be converted by using the
TO_DATE
orTO_TIMESTAMP
function, because each of these lets you specify an overriding date format mask value, such as this conversion to aDATE
data type:TO_DATE('2011-02-14', 'YYYY-MM-DD')
or this conversion to a
TIMESTAMP
data type:TO_TIMESTAMP('2011-02-14 18:11:28.1500', 'YYYY-MM-DD HH24:MI:SS.FF')
Converting to an
INTERVAL
data type is covered in the next bullet because you first must extract a time property as a number. It’s also possible that implicit casting of a numeric string can change the base data type to an integer for you. The method of implicit or explicit conversion depends on how you get the initial data value. - Convert from
NUMBER
data type toBINARY_FLOAT
,BINARY_DOUBLE
,CHAR
,VARCHAR2
,NUMBER
,DATE
,TIMESTAMP
,NCHAR
,NVARCHAR
. Interval conversions are a bit more complex, because you need more than one function to convert them. Typically, you pull the value from aDATE
orTIMESTAMP
data type andEXTRACT
the element of time by identifying its type before converting that value to anINTERVAL
type. The following provides an example:NUMTODSINTERVAL(EXTRACT(MINUTE FROM some_date), 'MINUTE')
You will use this type of built-in function layering frequently in some situations. It’s always a better approach to understand and use the built-in functions before you write your own stored functions.
- Convert from
DATETIME
orINTERVAL
data type toCHAR
,VARCHAR2
,DATE
,TIMESTAMP
,DATE
,TIMESTAMP
,INTERVAL
,NCHAR
,NVARCHAR
. - Convert from
RAW
data type toCHAR
,VARCHAR2
,RAW
,NCHAR
,NVARCHAR
. - Convert from
ROWID
orUROWID
data type toCHAR
,VARCHAR2
,ROWID
,UROWID
,
NCHAR
,NVARCHAR
. - Convert from
NCHAR
orNVARCHAR2
data type toBINARY_FLOAT
,BINARY_DOUBLE
,NUMBER
,NCHAR
,NVARCHAR
.
The data type conversion functions are useful when you want to make a conversion that requires you to provide instructions. You have to manually convert strings to dates when strings don’t adhere to default format mask conventions. Likewise, some specialized types require you to take specific actions before you can convert data.
The examples that follow focus on demonstrating how to use these functions. You’ll notice that there are no conversions between user-defined object types and standard types. You should include conversion methods in your object type definitions.
CAST
Function
Tutorial Material →
The tutorial shows you how to use the CAST
function to change one data type value to another data type value.
The CAST
function is very useful because it converts built-in data types to another built-in data type, or converts collection-typed variables to another collection-typed variable. The CAST
function does have some limits; for instance, it uses only the default date conversion format mask. Unlike most functions, CAST
works with all but the LONG
, LONG RAW
, ROWID
, and UROWID
built-in data types. CAST
also limits how it casts data from BLOB
and CLOB
types into a RAW
data type because it relies on an implicit database behavior. You raise an exception when CAST
tries to convert a large object into a RAW type when it is too large to fit inside a RAW
data type.
There are two prototypes: one for scalar built-in variables and another for collections. The scalar variable built-in prototype is
CAST(type1_variable_name AS type2_variable_name) |
and the collection prototype is
CAST(MULTISET(subquery)) AS collection_type_variable_name) |
The following program shows how to cast a date to a string:
SQL> DECLARE 2 source DATE := TO_DATE('30-SEP-07'); 3 target VARCHAR2(24); 4 BEGIN 5 target := CAST(source AS VARCHAR2); 6 dbms_output.put_line(target); 7 END; 8 / |
This type of usage is exactly the same in both SQL and PL/SQL contexts, but the MULTISET
context is restricted to SQL statements. If you attempt to use a CAST
function with a MULTISET
and subquery as a right operand, you raise a PLS-00405
exception. However, you can embed these in SQL statements inside your PL/SQL blocks.
The CAST
operation inside a query statement requires that you cast to a SQL data type, such as a collection of scalar variables. This leaves you with a choice between varrays and nested tables. You should use nested tables because they are easier to manage and don’t require incremental conversion with the TABLE
function call.
This creates a nested table of strings as a SQL data type:
SQL> CREATE OR REPLACE 2 TYPE collection IS TABLE OF VARCHAR2(5); 3/ |
You should create a table or view because the MULTISET
operator disallows queries that use set operators, like INTERSECT
, MINUS
, UNION
, and UNION ALL
. The MULTISET
operator raises a PLS-00605
exception when set operators are found in the subquery.
The following builds a sample table:
SQL> CREATE TABLE casting (num VARCHAR2(5)); |
Next, you can insert into the table the English ordinal numbers “one” to “nine” by using the table fabrication pattern:
SQL> INSERT INTO casting 2 (SELECT 'One' FROM dual UNION ALL 3 SELECT 'Two' FROM dual UNION ALL 4 SELECT 'Three' FROM dual UNION ALL 5 SELECT 'Four' FROM dual UNION ALL 6 SELECT 'Five' FROM dual UNION ALL 7 SELECT 'Six' FROM dual UNION ALL 8 SELECT 'Seven' FROM dual UNION ALL 9 SELECT 'Eight' FROM dual UNION ALL 10 SELECT 'Nine' FROM dual); |
The sample program demonstrates how to use the CAST
and MULTISET
functions together:
SQL> DECLARE 2 counter NUMBER := 1; 3 BEGIN 4 FOR i IN (SELECT CAST( 5 MULTISET( 6 SELECT num 7 FROM casting) AS COLLECTION) AS rs 8 FROM casting) LOOP 9 dbms_output.put_line(i.rs(counter)); 10 counter := counter + 1; 11 END LOOP; 12 END; 13 / |
This prints the ordinal number words in a list. The CAST
function returns a collection of items. Unfortunately, you also need to match a collection structure to the row structure of the query. You use this type of structure to return nested table contents from tables.
CONVERT
Function
Tutorial Material →
The tutorial shows you how to use the CONVERT
function to change the character encoding of a string.
The CONVERT
function converts a string from one character set to another. It has the following prototype:
CONVERT(string, destination_character_set, source_character_set) |
The following demonstrates converting the French word forêt
(forest in English) from the AL32UTF8
character set to the UTF-8 character set:
SQL> DECLARE 2 text VARCHAR2(10) := 'forêt'; 3 BEGIN 4 dbms_output.put_line(CONVERT(text,'AL32UTF8','UTF8')); 5 END; 6 / |
This prints the same forêt but it now takes 3 bytes of storage rather than 2 bytes. You will find this function handy when you work in multiple character sets.
EXTRACT
Function
Tutorial Material →
The tutorial shows you how to use the EXTRACT
function to find the day, month, or year of a valid DATE
data type value.
The EXTRACT
function lets you find an integer equivalent of a month, day, or year for any date data type. It’s handy when you want to identify transactions occurring on a day, in a month, in a year, or in a set of months or years.
The basic example for finding a day is
SQL> SELECT EXTRACT(DAY FROM TO_DATE('15-APR-2013')) AS DAY 2 FROM dual; |
Naturally, the example returns
DAY ---------- 15 |
You can substitute a case-insensitive MONTH
or YEAR
keyword before the FROM
keyword to extract the month or year. The argument following the FROM
keyword must be a DATE
or date-time data type. That means converting a literal date to a timestamp would let you extract the
hour, minute, or second from a timestamp, like this:
SQL> SELECT EXTRACT(HOUR FROM TO_TIMESTAMP('15-APR-2013')) AS HOUR 2 FROM dual; |
This returns
HOUR ---------- 0 |
While extracting a time element isn’t generally too useful by itself, the foregoing example lets me qualify that the time element of any DATE
data type is equal to zero, or 00:00:00
hours, minutes, and seconds. Moreover, a DATE
data type in Oracle is a timestamp set to the first second of each day.
The EXTRACT
function also lets you find ranges of days, months, or years in a WHERE
predicate, like
SQL> SELECT SUM(transaction_amount) 2 FROM TRANSACTION 3 WHERE EXTRACT(MONTH FROM transaction_date) IN (1,2,3); |
The IN
operator is often called a lookup operator because it checks whether one thing matches one thing in a set. More or less, a lookup function compares a single value or variable against a list of values.
Combining the EXTRACT
function and CASE
statement enables you to transform data like a pivot function.
TO_CHAR
Function
Tutorial Material →
The tutorial shows you how to use the TO_CHAR
function to convert and format the display of valid data type values.
The TO_CHAR
function lets you do several types of conversion. You can convert CLOB
, DATE
, NCHAR
, NCLOB
, NUMBER
, or TIMESTAMP
data types to VARCHAR2
data types. This function is overloaded and has two prototypes. The prototype for string data types is
TO_CHAR({clob_type | nchar_type | nclob_type}) |
The alternative prototype for dates, numbers, and times is
TO_CHAR({date_type | timestamp_type | number_type} [, format_mask [, nls_param ]]) |
The following subsections demonstrate converting other types of strings to character strings, dates to characters strings, and numbers to character strings. The date and number subsections also have two examples each: one with the native National Language Support (NLS) character set of the instance, and one that overrides the instance default.
Converting a String to a Character String
The following demonstrates converting a CLOB
data type to a CHAR
data type:
SQL> DECLARE 2 big_string CLOB := 'Not really that big, eh?'; 3 BEGIN 4 dbms_output.put_line(TO_CHAR(big_string)); 5 END; 6 / |
Converting a Date to a Character String
The TO_CHAR
function takes two arguments, or call parameters: the date or date-time, and the format mask. The following demonstrates converting a DATE
to a CHAR
:
SQL> DECLARE 2 today DATE := SYSDATE; 3 BEGIN 4 dbms_output .put_line(TO_CHAR(today,'Mon DD, YYYY')); 5 END; 6 / |
While your date will reflect the current system date, this prints the day this was written:
Sep 27, 2007 |
When you add the NLS_DATE_LANGUAGE
parameter to the function, you can override the NLS setting for the database. The following resets the NLS_DATE_LANGUAGE
parameter to French:
SQL> DECLARE 2 today DATE := SYSDATE; 3 BEGIN 4 dbms_output.put_line(TO_CHAR(today,'Mon DD, YYYY' 5 ,'NLS_DATE_LANGUAGE = FRENCH')); 6 END; 7 / |
This then prints the date in the French style, which adds a period after the abbreviation of the month, like
Sept. 27, 2007 |
Converting a Number to a Character String
Converting numbers to characters works much like converting dates to characters. The following illustrates converting a number to a formatted dollar amount in American English:
SQL> DECLARE 2 amount NUMBER := 2.9; 3 BEGIN 4 dbms_output.put_line(TO_CHAR(amount,'$9,999.90')); 5 END; 6 / |
The format mask prints a digit if one is found when there’s a 9 and always print a 0 when there is no value. The format mask substitutes a 0 since there is no value in the hundredths placeholder, printing
$2.90 |
Adding the NLS parameter, you can now format the currency return in Euros:
SQL> DECLARE 2 amount NUMBER := 2.9; 3 BEGIN 4 dbms_output.put_line(TO_CHAR(amount,'9,999.90L' 5 ,'nls_currency = EUR')); 6 END; 7 / |
This prints
2.90EUR |
This section has demonstrated how to use the TO_CHAR function to convert national language and large object strings to character strings and convert dates, timestamps, and numbers to character strings. The format masks only cover characters in those specific positions. You need to expand the format mask when dealing with larger numbers.
TO_CLOB
Function
Tutorial Material →
The tutorial shows you how to use the TO_CLOB
function to create a Character Large Object (CLOB
) from a string.
The TO_CLOB
function lets you convert the NCLOB
column data type or other character types to character large objects. You can convert CHAR
, NCHAR
, NVARCHAR2
, and VARCHAR2
data types to NCLOB
types.
The prototype for this is
TO_CLOB({char_type | nchar_type | nclob_type | nvarchar2_type | varchar2_type}) The |
following converts a string to CLOB
and then uses TO_CHAR
to reconvert for printing by the DBMS_OUTPUT.PUT_LINE
procedure:
SQL> DECLARE 2 initial_string VARCHAR2(2000) := 'Not really required. :-)'; 3 BEGIN 4 dbms_output.put_line(TO_CHAR(TO_CLOB(initial_string))); 5 END; 6 / |
This is a handy function when you’re moving an array of strings into a CLOB
variable. It also lets you move NLS large object columns into a standard format for your programs.
TO_DATE
Function
Tutorial Material →
The tutorial shows you how to use the TO_DATE
function to create a valid DATE
data type value from a string by using different format masks.
The TO_DATE
function lets you convert strings to dates. The prototype for this is
TO_DATE(string_type [, format_make [, nls_param ]]) |
The following program demonstrates converting a string through implicit conversion:
SQL> DECLARE 2 target DATE; 3 BEGIN 4 target := '29-SEP-94'; 5 dbms_output.put_line( 6 'Back to a string ['||TO_CHAR(target)||']'); 7 END; 8 / |
The implicit cast works because the default format mask for a date is DD-MON-RR
or DD-MON-YYYY
. When the string or source is not in that format, you must provide a format mask to cast the string into a date.
The next example explicitly casts a string by providing a format mask:
SQL> DECLARE 2 target DATE; 3 BEGIN 4 target := TO_DATE('September 29, 1994 10:00 A.M.' 5 ,'Month DD, YYYY HH:MI A.M.'); 6 dbms_output.put_line( 7 'Back to a string ['||TO_CHAR(target)||']'); 8 END; 9 / |
The A.M. formatting option is a mask available in some National Language Support (NLS) languages, like American English. It is not supported in French because the appropriate format mask in that language is AM
. If you apply an unsupported format mask, you raise an ORA-01855
exception.
The following example demonstrates overriding the default of language:
SQL> DECLARE 2 target DATE; 3 BEGIN 4 target := TO_DATE('Septembre 29, 1994 10:00 AM' 5 ,'Month DD, YYYY HH:MI AM' 6 ,'NLS_DATE_LANGUAGE = French'); 7 dbms_output.put_line('Back to a string [' 8 || TO_CHAR(target 9 ,'Month DD, YYYY HH:MI AM' 10 ,'NLS_DATE_LANGUAGE = French')||']'); |
The nature of converting to a date from a string is a virtual mirror to reversing the process, as should be clearly seen in the example. The TO_DATE
function is frequently used in PL/SQL.
TO_NUMBER
Function
Tutorial Material →
The tutorial shows you how to convert a formatted string with the TO_NUMBER
function to a number.
The TO_NUMBER
function lets you convert an expression into a numeric value. The expression can be a BINARY_DOUBLE
, CHAR
, NCHAR
, NVARCHAR2
, or VARCHAR2
data type. You can also use the NLS_NUMERIC_CHARACTERS
parameter or NLS_CURRENCY
parameter for National Language Support (NLS).
The prototype for this is
TO_NUMBER(expression [, format_mask [, nls_param ]]) |
The example converts a formatted string to a number by using a format mask:
SQL> DECLARE 2 source VARCHAR2(38) := '$9,999.90'; 3 BEGIN 4 dbms_output.put_line(TO_NUMBER(source,'$9,999.99')); 5 END; 6 / |
The program prints a number without a hundredth placeholder:
9999.9 |
You can also use NLS formatting like that shown in the TO_CHAR
function examples, or use it this way:
SQL> DECLARE 2 source VARCHAR2(38) := '9,999.90EUR'; 3 BEGIN 4 dbms_output.put_line( 5 TO_NUMBER(source,'9G999D99L','nls_currency = EUR')); 6 END; 7 / |
It also prints
9999.9 |
The G
stands for comma, D
for decimal point (or period), and L
for string qualifying the currency format. There is no dollar symbol leading a currency expression when you use an ISO currency string such as USA
, JPY
, or EUR
. The string provided as the value of nls_currency must also match the value in the original string.