Oracle - Extract Year from date result in 0 [duplicate] - oracle

This question already has an answer here:
Trying to export a Oracle via PL/SQL gives a date of 0000-00-00
(1 answer)
Closed 1 year ago.
Duplicate of Trying to export a Oracle via PL/SQL gives a date of 0000-00-00 as mentioned by #AlexPoole
I have a table with different dates in it, however, when I try to extract year, a single row returns an incorrect result.
This incorrect result happens when I execute the following (note that TO_DATE and FROM_DATE are both of data_type DATE):
select
TO_DATE
,EXTRACT(YEAR FROM TO_DATE) as "TO_YEAR"
,EXTRACT(MONTH FROM TO_DATE) as "TO_MONTH"
,EXTRACT(DAY FROM TO_DATE) as "TO_DAY"
,FROM_DATE
,EXTRACT(YEAR FROM FROM_DATE) as "FROM_YEAR"
,EXTRACT(MONTH FROM FROM_DATE) as "FROM_MONTH"
,EXTRACT(DAY FROM FROM_DATE) as "FROM_DAY"
,DUMP(FROM_DATE, 1016) as FROM_DUMP
,to_char(FROM_DATE, 'SYYYY-MM-DD HH24:MI:SS') FROM_STRING
from SomeTable
The incorrect result is (date format is YY-MM-DD):
TO_DATE TO_YEAR TO_MONTH TO_DAY FROM_DAT FROM_YEAR FROM_MONTH FROM_DAY FROM_DUMP FROM_STRING
-------- ---------- ---------- ---------- -------- ---------- ---------- ---------- ----------------------------- --------------------
00-02-01 2000 2 1 01-02-01 0 2 1 Typ=12 Len=7: 64,64,2,1,1,1,1 00000-00-00 00:00:00
My question is why does FROM_YEAR return a zero and not 2001?

A DATE is stored in 7-bytes using:
century + 100
year-of-century + 100
month + 0
day + 0
hour + 1
minute + 1
second + 1
Looking at the output of DUMP, which is Typ=12 Len=7: 64,64,2,1,1,1,1 then you have:
Century = -36
Year-of-century = -36
Month = February
Day = 1
Hour = 0
Minute = 0
Year = 0
Which would make your date midnight of 1st February 3636 BC.
Unless you intended to use dates from ancient history then it would suggest that somewhere in your application some corrupted data has been stored.
However, something else appears to be going on as that is a valid date that can be stored and TO_CHAR should work.
CREATE TABLE table_name ( dt ) AS
SELECT DATE '-3636-02-01'FROM DUAL;
SELECT TO_CHAR( dt, 'SYYYY-MM-DD HH24:MI:SS' ) AS dt_string,
DUMP( dt )
FROM table_name;
Outputs:
DT_STRING
DUMP(DT)
-3636-02-01 00:00:00
Typ=12 Len=7: 64,64,2,1,1,1,1
db<>fiddle here
and the DUMP matches your data but the TO_CHAR output is valid whereas yours is zeros.

Related

Oracle - Calculate the difference between dates and extract days and hours only in Oracle

I have a query that calculates the difference between two dates and returns a decimal date. I would just like to extract days and hours from the final calculated date.
This is my query.
select sysdate - (to_date('24/AUG/2021 14:00:00', 'DD/MON/YYYY HH24:MI:SS')) as FinalDate from dual;
FinalDate
162.013252314814814814814814814814814815
How do I get my desired output:?
Desired output
| Days | Hours |
| -------- | ------|
|162 |0.24 |
A little bit of arithmetic.
SQL> with temp (finaldate) as
2 (select sysdate - (to_date('24/AUG/2021 14:00:00', 'DD/MON/YYYY HH24:MI:SS')) from dual)
3 select trunc(finaldate) as days,
4 round((finaldate - trunc(finaldate)) * 24, 2) as hours
5 from temp;
DAYS HOURS
---------- ----------
162 6,49
SQL>
Why your and my hours don't match? Because of time difference; it's
SQL> select sysdate from dual;
SYSDATE
-------------------
02.02.2022 20:30:04
SQL>
over here.

DATE DIFF IN YEARS

How to find date diff in years in 'YYYY-MM-DD' format .
I am using below two querues:
SELECT TRUNC(TO_NUMBER(SYSDATE - TO_DATE('1994-08-13')) / 365.25) AS AGE FROM DUAL;
ORA-01861: literal does not match format string
SELECT (TO_DATE(SYSDATE, 'YYYY-MM-DD') - TO_NUMBER('1994-08-13', 'YYYY-MM-DD')) FROM DUAL;
O/P-: -727738
Desired o/p: 26
I suggest to use "months_between" function because it takes leap years into account (months_between wants 2 dates as parameters):
select months_between(sysdate, to_date('1994-08-13', 'YYYY-MM-DD'))/12 from dual;
26,4729751904122
of course, if you need to truncate:
select trunc(months_between(sysdate, to_date('1994-08-13', 'YYYY-MM-DD'))/12) from dual;
26
Just subtract one date from the other:
sysdate - date '1994-08-13'
Or
sysdate - to_date('1994-08-13', 'yyyy-mm-dd')
returns the number of days between the two dates.
So rewrite your first query to:
select (sysdate - date '1994-08-13') / 365.25 as age
from dual
So, extract years from these dates and subtract them:
SQL> select extract (year from sysdate) - extract(year from date '1994-08-13') diff
2 from dual;
DIFF
----------
27
SQL>
Because,
SQL> select 2021 - 1994 diff from dual;
DIFF
----------
27
SQL>

Use EPOCH time for timestamp to get records within 1 minute

I was curious to see how in Oracle 12c you can take a timestamp datatype and convert the records into EPOCH time to make them a number and then use that number to find any records within that date column that are within 1 minute of each other (assuming the same day if needed, or simply any calculations within 1 minute).
I tried the following but got an ORA-01873: the leading precision of the interval is too small error.
select (sold_date - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS'))*86400 as epoch_sold_date from test1;
What is SOLD_DATE? For e.g. SYSDATE (function that returns DATE datatype), your code works OK.
SQL> select (sysdate
2 - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
3 ) * 86400 as epoch_sold_date
4 from dual;
EPOCH_SOLD_DATE
---------------
1600807918
SQL>
As SOLD_DATE is a timestamp, but - it appears that fractions of a second aren't or special interest to you, cast it to DATE:
select (cast (systimestamp as date) --> this
- to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
) * 86400 as epoch_sold_date
from dual;
Saying that you get the same result for all rows: well, I don't, and you shouldn't either if SOLD_DATE differs.
SQL> with test (sold_date) as
2 (select timestamp '2020-09-22 00:00:00.000000' from dual union all
3 select timestamp '2015-03-18 00:00:00.000000' from dual
4 )
5 select sold_date,
6 (cast (sold_date as date)
7 - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
8 ) * 86400 as epoch_sold_date
9 from test;
SOLD_DATE EPOCH_SOLD_DATE
------------------------------ ---------------
22.09.20 00:00:00,000000000 1600732800
18.03.15 00:00:00,000000000 1426636800
SQL>
One more edit: when you subtract two timestamps, result is interval day to second. If you extract minutes from it, you get what you wanted:
SQL> with test (sold_date) as
2 (select timestamp '2020-09-22 10:15:00.000000' from dual union all
3 select timestamp '2015-03-18 08:05:00.000000' from dual
4 )
5 select sold_date,
6 lead(sold_date) over (order by sold_date) next_sold_date,
7 --
8 lead(sold_date) over (order by sold_date) - sold_date diff,
9 --
10 extract(day from lead(sold_date) over (order by sold_date) - sold_date) diff_mins
11 from test
12 order by sold_date;
SOLD_DATE NEXT_SOLD_DATE DIFF DIFF_MINS
------------------------------ ------------------------------ ------------------------------ ----------
18.03.15 08:05:00,000000000 22.09.20 10:15:00,000000000 +000002015 02:10:00.000000000 2015
22.09.20 10:15:00,000000000
SQL>
In your case, you'd check whether extracted minutes value is larger than 1 (minute).
If you just want to see how many minutes are there between two timestamps, then
cast them to dates
subtract those dates (and you'll get number of days)
multiply it by 24 (as there are 24 hours in a day) and by 60 (as there are 60 minutes in an hour)
Something like this:
SQL> with test (date_1, date_2) as
2 (select timestamp '2020-09-22 10:15:00.000000',
3 timestamp '2020-09-22 08:05:00.000000' from dual
4 )
5 select (cast(date_1 as date) - cast(date_2 as date)) * 24 * 60 diff_minutes
6 from test;
DIFF_MINUTES
------------
130
SQL>
If you are just looking to compare dates and find rows that are within one minute of each other, you do not need to use epoch time. There are several solutions to this problem on this thread.

How to extract time from a column and subtract from a custom time in Oracle

I would like to compare two time values. The first time value is a custom time which reprsents the start time, for example the column name is Business_Start_time and set to 6:00:00 am. I would also like to extract the time only from a column in Oracle which is a date field that looks like '5/1/2019 12:57:19 PM' and is called 'Completed_Date_Time'. The purpose of this is to compare the businses start date to the time a file was completed. I've tried to convert the 'Completed_Date_Time' field to 'HH24:MI:SS' format which seems to change the datatype to a char(8) value which does not allow me to compare two timestamps.
CAST(TO_CHAR(Completed_Date_Time, 'HH:MI:SS AM') AS CHAR(8))
Convert the values to TIMESTAMP and then you can subtract the values from the values truncated to the start of the day to get an INTERVAL containing the time since midnight and to get the difference you can subtract.
Oracle Setup:
CREATE TABLE table_name ( Business_Start_time, Completed_Date_Time ) AS
SELECT '6:00:00 AM',
TO_DATE( '5/1/2019 12:57:19 PM', 'DD/MM/YYYY HH12:MI:SS AM' )
FROM DUAL
Query:
SELECT ( completed_time - TRUNC( completed_time ) ) -
( start_time - TRUNC( start_time ) ) AS time_difference
FROM (
SELECT TO_TIMESTAMP( business_start_time, 'HH12:MI:SS AM' ) AS start_time,
CAST( Completed_Date_Time AS TIMESTAMP ) AS completed_time
FROM table_name
)
Output:
| TIME_DIFFERENCE |
| :---------------------------- |
| +000000000 06:57:19.000000000 |
db<>fiddle here
Although you wrote both the question and a comment, I'm still not sure what you have and what you want to get. Sample case would help (create table & insert into).
Meanwhile, a few words about it: when subtracting two DATE datatype values, the result is number of days, which means that - if you want to display it in a format which is easier to read & understand - you have to do some calculations (a day has 24 hours; an hour has 60 mintues; and so forth).
Here's an example:
SQL> create table test
2 (business_Start_time date,
3 completed_date_Time date
4 );
Table created.
SQL> insert into test (business_start_time, completed_date_time) values
2 (to_date('05.01.2019 12:57:19', 'dd.mm.yyyy hh24:mi:ss'),
3 to_date('05.01.2019 18:58:20', 'dd.mm.yyyy hh24:mi:ss'));
1 row created.
Simply subtracted, you'd get
SQL> select completed_date_time - business_start_time result from test;
RESULT
----------
,250706019
SQL>
Here's a function which presents such a value in another format, dd:hh:mi (days:hours:minutes) (you can omit days by setting the second parameter to 0):
SQL> create or replace
2 function f_days2ddhhmi (par_broj_dana in number, par_cb_dd in number)
3 return varchar2
4 is
5 /* Converting number of days into dd:hh:mi format
6
7 Date from Date to Diff (days) Retval
8 -------------------- -------------------- -------------- ----------------------------------
9 20.11.2018. 07:00:00 - 20.11.2018. 13:45:00 0,28125 0:06:45 (6 hours 45 minutes)
10 23.10.2018. 07:00:00 - 25.10.2018. 22:12:00 2,63333 2:15:12 (2 daysa 15 hours 12 minutes)
11
12 PAR_BROJ_DANA: 0.28125
13 PAR_CB_DD : display number of days or not? 1 - yes --> 0:06:45
14 0 - no --> 06:45
15 */
16 l_broj_dana number := round (par_broj_dana, 15); -- to avoid 1.99999999999999 days = 1 day 24 hours
17 retval varchar2 (20);
18 begin
19 with podaci
20 as (select trunc (l_broj_dana) broj_dana,
21 round (mod (l_broj_dana * 24, 24), 2) broj_sati
22 from dual)
23 select decode (par_cb_dd,
24 1, lpad (p.broj_dana, 2, '0') || ':',
25 0, null)
26 || lpad (trunc (p.broj_sati), 2, '0')
27 || ':'
28 || lpad (round ( (p.broj_sati - trunc (p.broj_sati)) * 60),
29 2,
30 '0')
31 into retval
32 from podaci p;
33
34 return retval;
35 end f_days2ddhhmi;
36 /
Function created.
Applied to the test table, you'd get
SQL> select f_days2ddhhmi(completed_date_time - business_start_time, 0) result
2 from test;
RESULT
--------------------------------------------------------------------------------
06:01
which means that the difference is 6 hours and 1 minute.
If that's what you asked, see whether you can use it. Feel free to enhance it to seconds etc. if necessary.

How are dates stored in Oracle?

How are dates stored in Oracle? For example I know most systems use Epoch time to determine what time it is. By calculating how many seconds away from January 1st 1970. Does Oracle do this as well?
The reason I am asking this is I noticed if you take two dates in Oracle and subtract them you get a floating point of how many days are between.
Example
(Sysdate - dateColumn)
would return something like this (depending on the time)
3.32453703703703703703703703703703703704
Now is Oracle doing the conversion and spitting that format out, or does Oracle store dates with how many days it is away from a certain time frame? (Like Epoch time)
There are two types 12 and 13
http://oraclesniplets.tumblr.com/post/1179958393/my-oracle-support-oracle-database-69028-1
Type 13
select dump(sysdate) from dual;
Typ=13 Len=8: 220,7,11,26,16,41,9,0
The format of the date datatype is
Byte 1 - Base 256 year modifier : 220
2 - Base 256 year : 256 * 7 = 1792 + 220 = 2012
3 - Month : 11
4 - Day : 26
5 - Hours : 16
6 - Minutes : 41
7 - Seconds : 09
8 - Unused
2012-11-26 16:41:09
Type 12
select dump(begindate) from tab;
Typ=12 Len=7: 100,112,2,7,1,1,1
The format of the date datatype is
byte 1 - century (excess 100) 100 - 100 = 00
byte 2 - year (excess 100) 112 - 100 = 12
byte 3 - month = 2
byte 4 - day = 7
byte 5 - hour (excess 1) 1 - 1 = 0
byte 6 - minute (excess 1) 1 - 1 = 0
byte 7 - seconds (excess 1) 1 - 1 = 0
0012-02-07 00:00:00
From the manual at http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements001.htm#sthref151
For each DATE value, Oracle stores the following information: year, month, day, hour, minute, and second
So apparently it's not storing an epoch value which is also confirmed by this chapter of the manual:
The database stores dates internally as numbers. Dates are stored in fixed-length fields of 7 bytes each, corresponding to century, year, month, day, hour, minute, and second
How are dates stored in Oracle?
The two data types 12 and 13 are for two different purposes.
Type 12 - Dates stored in table
Type 13 - Date returned by internal date functions like SYSDATE/CURRENT_DATE, also when converting a string literal into date using TO_DATE or ANSI Date literal DATE 'YYYY-MM-DD'.
Test cases:
Basic table setup for type 12:
SQL> CREATE TABLE t(col DATE);
Table created.
SQL> INSERT INTO t SELECT SYSDATE FROM dual;
1 row created.
SQL> COMMIT;
Commit complete.
Check the different cases:
SQL> SELECT DUMP(col) FROM t;
DUMP(COL)
--------------------------------------------------------------------------------
Typ=12 Len=7: 120,116,3,17,18,6,55
SQL> SELECT DUMP(SYSDATE) FROM dual;
DUMP(SYSDATE)
--------------------------------------------------------------------------------
Typ=13 Len=8: 224,7,3,17,17,5,54,0
SQL> SELECT DUMP(CURRENT_DATE) FROM dual;
DUMP(CURRENT_DATE)
--------------------------------------------------------------------------------
Typ=13 Len=8: 224,7,3,17,17,14,20,0
SQL> SELECT DUMP(TO_DATE('17-DEC-1980 12:12:12','DD-MON-YYYY HH24:MI:SS')) FROM dual;
DUMP(TO_DATE('17-DEC-198012:12:12','
------------------------------------
Typ=13 Len=8: 188,7,12,17,12,12,12,0
Using ANSI Date literal, just like TO_DATE:
SQL> SELECT DUMP(DATE '2016-03-17') FROM dual;
DUMP(DATE'2016-03-17')
--------------------------------
Typ=13 Len=8: 224,7,3,17,0,0,0,0
SQL> INSERT INTO t SELECT to_date('17-DEC-1980 12:13:14','DD-MON-YYYY HH24:MI:SS') FROM dual;
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT DUMP(col) FROM t;
DUMP(COL)
--------------------------------------------------------------------------------
Typ=12 Len=7: 120,116,3,17,18,6,55
Typ=12 Len=7: 119,180,12,17,13,14,15
SQL>
As you can see, while storing a date in the table, it uses type 12. The second type 13 is used when converting a string literal into date using date functions or when date returned by internal date functions like SYSDATE/CURRENT_DATE.

Resources