{"id":73079,"date":"2017-02-02T16:00:14","date_gmt":"2017-02-02T16:00:14","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/uncategorized\/oracle-for-absolute-beginners-date-timestamp-and-interval\/"},"modified":"2021-07-14T13:06:56","modified_gmt":"2021-07-14T13:06:56","slug":"oracle-for-absolute-beginners-date-timestamp-and-interval","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/oracle-databases\/oracle-for-absolute-beginners-date-timestamp-and-interval\/","title":{"rendered":"Oracle for Absolute Beginners: Date, Timestamp and Interval"},"content":{"rendered":"<p>All databases stand on a tripod of datatypes: strings, numbers and dates. And though they might dress them in fancy clothing &#8211; <code>varchar2, clob, float, integer<\/code> &#8211; strings are really just strings, and numbers are really just numbers. But dates \u2014 dates are interesting.<\/p>\n<p>In this article I&#8217;ll talk to you about dates, about time, and about how both are captured and calculated in an Oracle database.\u00a0<\/p>\n<h5>The DATE Datatype<\/h5>\n<p><code>DATE<\/code> is the main &#8211; or rather, <em>original<\/em> &#8211; datatype used in Oracle for holding dates. Beneath the plainness of its name, it hides a little depth. \u00a0Firstly, for example, it doesn&#8217;t really hold a date, instead it records a date<em>time.<\/em> It&#8217;s a seven byte store of century, year, month, day\u00a0<em>and<\/em> hour, minute and second.\u00a0<\/p>\n<p>Oracle does not compel you to supply a time element each time you enter a date, but it&#8217;s probably worth bearing in mind that one is always recorded (the default time is midnight). Let me show you what I mean &#8211; and in the process we can chat about the\u00a0<em>to_date\u00a0<\/em>function.<\/p>\n<p>Let&#8217;s start by creating a table with a date column.<\/p>\n<pre>CREATE TABLE test_table (\r\ndate_col DATE\r\n);<\/pre>\n<p>Now that&#8217;s done, let me talk to you about a dilemma that dates present databases with. When you present Oracle with, for example, &#8217;21 January 2017&#8242;, how is it supposed to recognise that this is a date and not just a character string? Likewise, how&#8217;s it to know that &#8217;21\/01\/2017&#8242; isn&#8217;t just some complex piece of arithmetic?<\/p>\n<p>It resolves this conundrum with the <code>to_date<\/code> function. The <code>to_date<\/code> function accepts a character string that represents the date along with another one that tells it how to interpret that date. Here&#8217;s what I mean:<\/p>\n<pre>to_date('21 January 2017','DD Month YYYY')<\/pre>\n<pre>to_date('21\/01\/2017','DD\/MM\/YYYY')<\/pre>\n<p>It&#8217;s as simple as that (by the way, <a href=\"https:\/\/docs.google.com\/spreadsheets\/d\/1aRx-iU1GsjnOId0Tk_0a1k4DsRb_BmELWOK3M4t4G5c\/edit?usp=sharing\" target=\"_blank\">here&#8217;s a short list of the codes<\/a> you can use to describe the format of \u00a0your date string).<\/p>\n<p>Now let&#8217;s insert a date into our table and then query it right back to see what&#8217;s in its time component.<\/p>\n<pre>INSERT INTO test_table(date_col)\r\nVALUES (to_date('21\/01\/2017','DD\/MM\/YYYY'));<\/pre>\n<pre>SELECT to_char(date_col,'DD\/MM\/YYYY HH24:MI:SS') \"test date\"\r\nFROM test_table;\r\n\r\ntest date\r\n------------------------\r\n21\/01\/2017 00:00:00<\/pre>\n<p>So even though we did not specify a time when inserting the date, a time of midnight has been automatically appended to our date (interestingly, if all we do is insert a\u00a0<em>time<\/em> into our date column, Oracle will default the date component to the first day of the month).<\/p>\n<p>Personally, I&#8217;m a fan of the <code>to_date<\/code> function and always use it when I&#8217;m working with dates; however there&#8217;s another way to achieve the same result, and that&#8217;s to use something called the ANSI date literal. The date literal must be specified in the following format:<\/p>\n<pre>DATE 'YYYY-MM-DD'<\/pre>\n<p>And here&#8217;s a rewriting of our insert statement using a date literal:<\/p>\n<pre>INSERT INTO test_table (date_col)\r\nVALUES (DATE '2017-01-21');<\/pre>\n<p>Like I said, I never use date literals myself, but you might find them more convenient. There&#8217;s no accounting for taste.<\/p>\n<p>There are a number of simple operations you can carry out with dates. You can, for example, compare them. Oracle considers a later date to be greater than an earlier date. In other words, Oracle believes that tomorrow is greater than today. Which, I guess, makes them optimists.<\/p>\n<pre>TO_DATE('22.JAN.2017','DD.MON.YYYY') &gt; TO_DATE('22.JAN.2017','DD.MON.YYYY')<\/pre>\n<p>You can also subtract dates from each other. This will give you the number of days between those two dates.\u00a0<\/p>\n<pre>SELECT TO_DATE('21 JANUARY 2017','DD MONTH YYYY') - DATE '2017-01-01' \"date diff\"\r\n FROM DUAL;\r\n\r\ndate diff\r\n------------\r\n20<\/pre>\n<p>You cannot add two dates together. You also cannot slice them up and fry them in olive oil with chili and cumin. Which might sound like a ridiculous thing for me to say, but if you think about it, trying to add two dates together is just as ridiculous.\u00a0<\/p>\n<p>There is one last thing I&#8217;d like to mention about the <code>DATE<\/code> datatype, and that is the <code>sysdate<\/code> function. A call to <code>sysdate<\/code> will always return the current date.\u00a0<\/p>\n<p>And that in essence is everything you need to know about the <code>DATE<\/code> datatype. Got that? Great, now there&#8217;s one last thing I&#8217;d like you to do for me. Forget everything I&#8217;ve just taught you. Forget it all, because&#8230;<\/p>\n<h5>The TIMESTAMP Datatype<\/h5>\n<p>I&#8217;ve been an Oracle developer for close to 20 years now (I&#8217;ll pause and give you a chance to say\u00a0<em>no way, you don&#8217;t look old enough!<\/em>). Back when I started &#8211; a time when dinosaurs still roamed the plains and the earth was flat &#8211; the <code>date<\/code> datatype was all we had. However, as part of Oracle 9i we were gifted the <code>timestamp<\/code> datatype, and, in truth, I&#8217;m surprised that it hasn&#8217;t totally supplanted the <code>date<\/code> datatype by now. It should; it has everything <code>date<\/code> has and more.<\/p>\n<p>The <code>timestamp<\/code> datatype is made up of the century, year, month, day, hour, minute and second. But just when you start thinking,\u00a0<em>&#8220;Big deal, DATE did that too,&#8221;<\/em> it whips out it&#8217;s joker: it also records\u00a0<em>fractional seconds.<\/em><\/p>\n<p>Admittedly, not all processes are so time-critical that a millisecond here or there makes a difference, but since <code>timestamp<\/code> is no more cumbersome to use than <code>date<\/code> you might as well always use it.<\/p>\n<p>Here&#8217;s how you specify a <code>timestamp<\/code> column:<\/p>\n<p>TIMESTAMP [(<em>fractional_seconds_precision)<\/em>]<\/p>\n<p>The optional\u00a0<em>fractional_seconds_precision<\/em> is a number &#8211; from 0 to 9 &#8211; which tells Oracle how many digits you want to store your fractions of a second. \u00a0The default is 6. \u00a0<\/p>\n<p>Beyond the fractional seconds, the <code>timestamp<\/code> datatype is pretty much analogous to the <code>date<\/code> datatype. &#8220;Oh, you&#8217;ve got a <code>to_date<\/code> function?&#8221; it says. &#8220;Well I&#8217;ve got <code>to_timestamp<\/code>.&#8221;<\/p>\n<p>&#8220;And did I hear you boasting about your ANSI date literal? Well, check out this shiny new ANSI timestamp literal.&#8221;<\/p>\n<p>Let&#8217;s add a new column to our table and give this new datatype a spin.\u00a0<\/p>\n<pre>ALTER TABLE test_table \r\n ADD timestamp_col TIMESTAMP(2);<\/pre>\n<p>Let&#8217;s add a record using the <code>to_timestamp<\/code> function:<\/p>\n<pre>INSERT INTO test_table (timestamp_col)\r\nVALUES ( to_timestamp('21\/01\/2017 12:34:56.78','DD\/MM\/YYYY HH24:MI:SS.FF'));<\/pre>\n<p>As you&#8217;ve probably guessed, the\u00a0<em>FF<\/em> represents the fractional seconds.<\/p>\n<p>Let&#8217;s add another record using the timestamp literal. The format is as follows:<\/p>\n<pre>TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF'<\/pre>\n<p>So, to add that record we&#8217;ll need to say:<\/p>\n<pre>INSERT INTO test_table (timestamp_col)\r\n VALUES ( TIMESTAMP '2017-01-21 12:34:56.78');<\/pre>\n<h5>The TIMESTAMP WITH TIME ZONE Datatype<\/h5>\n<p><code>Timestamp<\/code> is impressive, but what if, in addition to that fractional second, you also want to record the\u00a0<em>timezone<\/em>? For that you&#8217;ll need to use the <code>TIMESTAMP WITH TIME ZONE<\/code> datatype which, if we&#8217;re being honest, is just <code>TIMESTAMP<\/code> with an extra little trick. Time zones are declared as offsets of the Greenwich Mean Time. Here, let me show you what I mean:<\/p>\n<p>TIMESTAMP [(<em>fractional_seconds_precision)<\/em>] WITH TIME ZONE<\/p>\n<pre>ALTER TABLE test_table\r\nADD timestamp_tz_col TIMESTAMP (2) WITH TIME ZONE;<\/pre>\n<pre>INSERT INTO test_table (timestamp_tz_col)\r\nVALUES (TIMESTAMP '2017-01-21 21:05:53.46 +02:00');<\/pre>\n<p>You can probably already see what the statements above do, but I&#8217;ll tell you anyway otherwise I won&#8217;t feel like I&#8217;m doing my job here. We&#8217;ve amended our table to add a new <code>TIMESTAMP WITH TIME ZONE<\/code> column (please note the syntax). Next we inserted a value into that column using a variation of the timestamp literal that we talked about earlier.\u00a0<\/p>\n<p>The <code>TIMESTAMP WITH TIME ZONE<\/code> data type has its own flavour of the <code>to_timestamp<\/code> function too. It&#8217;s called <code>to_timestamp_tz<\/code>. \u00a0Let&#8217;s rewrite our insert statement to use it:<\/p>\n<pre>INSERT INTO test_table (timestamp_tz_col)\r\nVALUES (to_timestamp_tz('2017-01-21 21:05:53.46 +02:00',\r\n                        'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM');<\/pre>\n<p>You&#8217;ve probably noticed the two new format codes: <em>TZH<\/em> and <em>TZM<\/em>. You&#8217;ve probably also guessed that they stand for Time Zone Hour and Time Zone Minute.\u00a0<\/p>\n<p>Before we move on from timestamps there&#8217;s one last function that I want to introduce you to: <code>systimestamp<\/code>. It&#8217;s analogous to <code>sysdate<\/code> and it returns the current timestamp with time zone.\u00a0<\/p>\n<p>I live just outside London (GMT) and it&#8217;s just past nine in the evening right now. When I look out of my window, the trees look naked and arthritic, because it is late January.<\/p>\n<pre>SELECT systimestamp\r\nFROM dual;\r\n\r\nsystimestamp\r\n-------------------------------------\r\n21-JAN-2017 21.32.48.269997 AM +00:00<\/pre>\n<h5>DATE ARITHMETIC<\/h5>\n<p>A little while ago I was making fun of you for wanting to add two dates together. Sorry, that was mean of me. There\u00a0<em>are<\/em> ways of adding to a date or a timestamp, and it&#8217;s important to know them. If, for instance, I wanted to find out what the date would be 2 days from today I could run the following query:<\/p>\n<pre>SELECT systimestamp + 2\r\nFROM dual;<\/pre>\n<p>Or, if I was wondering what the date was 10 days ago:<\/p>\n<pre>SELECT systimestamp - 10\r\nFROM dual;<\/pre>\n<p>And since we know that an hour is one-twenty-fourth of a day, we can add and subtract them too. If I want to know what the time will be 3 hours from now.<\/p>\n<pre>SELECT systimestamp + (3\/24)\r\nFROM dual;<\/pre>\n<p>We can do the same thing for minutes (1\/(24 * 60)) and for seconds (1\/(24*60*60)) too.<\/p>\n<p>You can also use the handy <code>ADD_MONTHS<\/code> function to add or subtract whole months. If, for instance, I want to know what the date will be 5 months from now:<\/p>\n<pre>SELECT add_months(systimestamp,5)\r\nFROM dual;<\/pre>\n<p>Or if I want to know what the date was 23 months ago:<\/p>\n<pre>SELECT add_months(systimestamp,-23)\r\nFROM dual;<\/pre>\n<p>Some other handy date functions are <code>LAST_DAY<\/code> and <code>NEXT_DAY<\/code>, which return the last date of the month of the parameterised date, and the date of the next day of the parameterised weekday after the parameterised date respectively.<\/p>\n<div style=\"width: 221px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" title=\"\" src=\"https:\/\/docs.oracle.com\/cd\/B19306_01\/server.102\/b14200\/img\/last_day.gif\" alt=\"\" width=\"211\" height=\"27\" \/><\/p>\n<p class=\"wp-caption-text\"><em>Source: <a href=\"https:\/\/docs.oracle.com\/cd\/B19306_01\/server.102\/b14200\/functions072.htm\" target=\"_blank\">Oracle<\/a><\/em><\/p>\n<\/div>\n<div style=\"width: 299px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" title=\"\" src=\"https:\/\/docs.oracle.com\/cd\/B19306_01\/server.102\/b14200\/img\/next_day.gif\" alt=\"\" width=\"289\" height=\"27\" \/><\/p>\n<p class=\"wp-caption-text\"><em>Source: <a href=\"https:\/\/docs.oracle.com\/cd\/B19306_01\/server.102\/b14200\/functions093.htm\" target=\"_blank\">Oracle<\/a><\/em><\/p>\n<\/div>\n<pre>SELECT last_day(TIMESTAMP '2017-01-21 21:32:48') \"last day\",\r\n\u00a0 \u00a0    next_day(TIMESTAMP '2017-01-21 21:32:48', 'FRIDAY') \"next day\"\r\nFROM dual;\r\n\r\nlast day      next day\r\n----------    --------------\r\n31\/01\/2017    27\/01\/2017<\/pre>\n<h5>INTERVAL<\/h5>\n<p>I described the <code>LAST_DAY<\/code> and <code>NEXT_DAY<\/code> functions as &#8220;handy&#8221;. I was lying. In truth, I don&#8217;t remember the last time I used either of them. <em>You<\/em> might need them, but I never have.\u00a0<\/p>\n<p>However, when I said that it is important to know how to add to a date that wasn&#8217;t a lie. Adding a duration of time &#8211; an\u00a0<em>interval\u00a0<\/em>&#8211; to a date is something developers are called on to do all the time. As you know, if we want to find out what the date will be in 1 &#8211; or even 100 &#8211; days one thing we could do is simply add 1 &#8211; or 100 &#8211; to <code>systimestamp<\/code>. \u00a0However, we could also use the <code>INTERVAL<\/code> data type.\u00a0<\/p>\n<p>Unlike <code>DATE<\/code> and <code>TIMESTAMP<\/code> which are records of pinpoints in time, the <code>INTERVAL<\/code> data type is a measure of an amount of time &#8211; 1 day, 100 days, 27 years or even 5 seconds.<\/p>\n<p>There are two INTERVAL data types:<\/p>\n<p><code>INTERVAL YEAR TO MONTH<\/code> &#8211; which records a duration of years and months<\/p>\n<p><code>INTERVAL DAY TO SECOND<\/code> &#8211; which records a duration of days, hours, minutes and seconds.<\/p>\n<p>Here&#8217;s how they&#8217;re specified:<\/p>\n<pre>INTERVAL YEAR [(year_precision)] TO MONTH<\/pre>\n<pre>INTERVAL DAY [(day_precision)] TO SECOND [(fractional_seconds_precision)]<\/pre>\n<p><em>Year_precision<\/em> is the number of digits in the year field, whereas <em>day_precision<\/em> is its analog for the day field.\u00a0<\/p>\n<p>Let&#8217;s add two <code>interval<\/code> columns to our table.<\/p>\n<pre>ALTER TABLE test_table\r\nADD (\r\n     int_y2d_col \u00a0INTERVAL YEAR(2) TO MONTH,\r\n     int_d2s_col INTERVAL DAY(2) TO SECOND\r\n    );<\/pre>\n<p>Intervals can be expressed as literals. If, for example, we wanted to insert an interval of 3 years and 11 months into <em>int_y2d_col<\/em> it would be:<\/p>\n<pre>INSERT INTO test_table (int_y2d_col)\r\nVALUES (INTERVAL '3-11' YEAR TO MONTH);<\/pre>\n<p>And if we wanted to insert an interval of 4 days, 3 hours, 2 minutes and 1 second into\u00a0<em>int_d2s_col<\/em>, we could use the following literal:<\/p>\n<pre>INSERT INTO test_table (int_d2s_col)\r\n VALUES (INTERVAL '4 3:02:01' DAY TO SECOND);<\/pre>\n<p>But if you&#8217;re like me and you prefer functions to literals, you might want to note these two: <code>TO_YMINTERVAL<\/code> and <code>TO_DSINTERVAL<\/code>. The first of the two converts a parameterised string to a <code>YEAR TO MONTH INTERVAL<\/code>, while the latter does the same for DAY TO SECOND INTERVALs.<\/p>\n<p>If, for instance, we wanted to know what the date would be 17 years and 3 months from today, we might say:<\/p>\n<pre>SELECT systimestamp + TO_YMINTERVAL('17-3') \"distant future\"\r\nFROM dual;\r\n\r\ndistant future\r\n----------------------------------------\r\n21-APR-2034 21.53.49.841207000 PM +00:00<\/pre>\n<p>But if we&#8217;re being a little more modest and only wanted to know what the date would be in, say, 12 days, 8 hours, and 4 minutes:<\/p>\n<pre>SELECT systimestamp + TO_DSINTERVAL('12 8:04:00') \"near future\"\r\nFROM dual;\r\n\r\nnear future\r\n----------------------------------------\r\n03-FEB-2017 06.00.12.210000000 AM +00:00\r\n<\/pre>\n<h5>CONCLUSION<\/h5>\n<p>As we said at the start of this article, time is one of the tripods upon which the Oracle database stands. It&#8217;s impossible to get very far without an understanding of how to measure and manipulate it. This article is by no means exhaustive, but I hope that it provides the foundation you might need to go on and learn more.\u00a0<\/p>\n<p>You&#8217;re welcome.\u00a0<\/p>\n","protected":false},"excerpt":{"rendered":"<p>All databases stand on a tripod of datatypes: strings, numbers and dates. And though they might dress them in fancy clothing &#8211; varchar2, clob, float, integer &#8211; strings are really just strings, and numbers are really just numbers. But dates \u2014 dates are interesting. In this article I&#8217;ll talk to you about dates, about time, and about how both ar&hellip;<\/p>\n","protected":false},"author":221907,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143533],"tags":[48365,48366,48392,48426,124952,48526],"coauthors":[48557],"class_list":["post-73079","post","type-post","status-publish","format-standard","hentry","category-oracle-databases","tag-beginner","tag-beginners-guide","tag-date","tag-interval","tag-redgate-deploy","tag-timestamp"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73079","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221907"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=73079"}],"version-history":[{"count":1,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73079\/revisions"}],"predecessor-version":[{"id":88952,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73079\/revisions\/88952"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=73079"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=73079"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=73079"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=73079"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}