How Oracle internally deduces the differece between dates - oracle

select (current_date - TO_DATE('20210817124015','YYYYMMDDHH24MISS')) from dual;
Outputs:
0.1229282407407407407407407407407407407407
I want to know how oracle internally achieves this value.
ps: the current_date and the hardcoded date are same, only time is the difference.

CURRENT_DATE returns the current date and time in the user's session time zone.
TO_DATE('20210817124015','YYYYMMDDHH24MISS') returns the date 2021-08-17T12:40:15.
Note: A DATE data type always has year, month, day, hour, minute and second components. However, the user interface you are using may chose not to show all the components.
Subtracting one date from another returns the number of days between the two values.
0.1229282407407407407407407407407407407407 days is:
2.950277778 hours; or
177.016666667 minutes; or
10621 seconds; or
2 hours 57 minutes and 1 second.
So your current date was 2021-08-17T12:40:15 + 10621 seconds or 2021-08-17T15:37:16.
For example:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD"T"HH24:MI:SS';
ALTER SESSION SET TIME_ZONE = 'Asia/Samarkand';
SELECT CURRENT_DATE,
TO_DATE('20210817124015','YYYYMMDDHH24MISS') As other_date,
CURRENT_DATE - TO_DATE('20210817124015','YYYYMMDDHH24MISS') as difference,
(CURRENT_DATE - TO_DATE('20210817124015','YYYYMMDDHH24MISS')) DAY TO SECOND
as interval_difference
FROM DUAL;
Outputs:
CURRENT_DATE
OTHER_DATE
DIFFERENCE
INTERVAL_DIFFERENCE
2021-08-17T15:40:01
2021-08-17T12:40:15
.124837962962962962962962962962962962963
+00 02:59:46.000000
db<>fiddle here

Subtracting two dates returns a difference in days.
0.1229282407407407407407407407407407407407 days is
2.9502777777768 hours
177.016666666608 minutes
10621 seconds
Or, put another way, current_date is returning a date value that is 2 hours 57 minutes and 1 second after the hard-coded date. Since the hard-coded date has a time of 12:40:51, that means that current_date has a time of 15:37:52.

Related

Oracle DB to_date with year - to_date(2017,'YYYY') unexpected return

While writing few queries I needed to return only those rows that have date column set in this year (2017) , that's not my problem I know how to write this query in couple of diffrent ways, but I came across something strange and unexpected for me. Can anyone explain why Oracle db 11.2 is behaving this way?
select sysdate from dual
returns:
2017/12/05 09:22:27
select to_date(2017,'YYYY'),trunc(sysdate,'YYYY') from dual
returns :
2017/12/01 00:00:00 2017/01/01 00:00:00
select to_date(2017,'YYYY'),trunc(sysdate,'YYYY') from dual
where trunc(sysdate,'YYYY') = to_date(2017,'YYYY')
no rows returned
Why does to_date(2017,'YYYY') returns 2017/12/01, will it return 2017/01/01 next month? Why does it work that way? I would expect it to always return 2017/01/01 no matter the current month (if month part is indeed changing depending on sysdate).
In Oracle, TO_DATE will assume that:
If you do not specify the year then it is the current year;
If you do not specify the month then it is the current month;
If you do not specify the day then it is the first day of the month;
If you do not specify the hours then it is the midnight hour (0);
If you do not specify the minutes then it is 0 minutes past the hour; and
If you do not specify the seconds then it is 0 seconds into the minute.
You are specifying only the year (2017) so it will be:
Zero minutes and seconds past midnight of the first day of the current month of the year you specify (2017).
If you want the first day of the year then specify the month (and preferably the rest of the date):
select to_date( '201701','YYYYMM'),
trunc(sysdate,'YYYY')
from dual
where trunc(sysdate,'YYYY') = to_date( '201701','YYYYMM' )
Or use a date literal:
select DATE '2017-01-01',
trunc(sysdate,'YYYY')
from dual
where trunc(sysdate,'YYYY') = DATE '2017-01-01'

How do I update the time in oracle sql?

I need a query to update a time in an appointment date by keeping the date but changing the time.
For example
10-Feb-2016 09:00:00
and i want to change it to 10-Feb-2016 10:00:00.
Update Appointment
set vdate = '10:00:00'
where vdate= '10-Feb-2016'
I get the "0 row has been updated'. Not sure if i'm missing something.
Thanks in advance.
You can use trunc() which sets the time part of a DATE (or TIMESTAMP) to 00:00:00, then add the 10 hours to it:
Update Appointment
set vdate = trunc(vdate) + interval '10' hour
where trunc(vdate) = DATE '2016-02-10'
This would change all rows that have a date 2016-02-10. If you only want to do that for those that are at 09:00 (ignoring the minutes and seconds) then just add one hour to those rows
Update Appointment
set vdate = trunc(vdate) + interval '1' hour
where trunc(vdate, 'hh24') = timestamp '2016-02-10 09:00:00'
trunc(vdate, 'hh24') will set the minutes and seconds of the date value to 00:00, so that the comparison with 2016-02-10 09:00:00 works properly.
Unrelated, but: do not rely on implicit data type conversion. '10-Feb-2016' is a string value, not a DATE literal. To specify a date either use an ANSI DATE literal (as I have done in the above statement) or use the to_date() function with a format mask to convert a string literal to a proper date value.
Your statement is subject to the evil implicit data type conversion and will fail if the SQL client running the statement uses a different NLS setting (it will fail on my computer for example)
If what you want to do is add an hour to a date, then you can do:
Update Appointment
set vdate = vdate + 1/24
where vdate = to_date('10/02/2016 09:00', 'dd/mm/yyyy hh24:mi');
since in Oracle, date differences are measured in number of days, and an hour is 1/24th of a day.
If what you want to do is specify an exact time (e.g. to 10:25:48), then you could do the following instead:
Update Appointment
set vdate = trunc(vdate) + 10/24 + 25/(24*60) + 48/(24*60*60)
where vdate = to_date('10/02/2016 09:00', 'dd/mm/yyyy hh24:mi');
Bear in mind that these updates will update all rows that have a date of 10th Feb 2016 at 9am. You'd need to change your query's where clause if you wanted to specify a more specific row or set of rows.
Try like this.
UPDATE MyTable
SET MyDate = DATEADD(HOUR, 4, CAST(CAST(MyDate AS DATE) AS DATETIME))
or
UPDATE MyTable
SET MyDate = DATEADD(HOUR, 4, CAST(FLOOR(CAST(MyDate AS FLOAT)) AS DATETIME))

Converting Time Interval to actual time in AM PM in Oracle

How can i convert the result of select statement of time interval field in respective time in Am/Pm format.
My Field is:
Interval Day(2) To Second(6)
I tried this:
select To_Char(Att_EntranceTime , 'HH:MI AM') From EMPLOYEEATTENDENCETABLE;
however this does not help me, i have also tried to add the basetime from systime to my interval field but that did not help.. can someone suggest me what to do?
Intervals can't be directly formatted, as you've discovered. You can add your interval to any date which has its time set to midnight, and then format the resulting date to show the time in your desired format. For example you could add it to today's date using trunc(sysdate):
to_char(trunc(sysdate) + my_interval, 'HH:MI AM')
You need to truncate it to set the time to midnight; otherwise the result will be your interval plus the current system time.
Or you can use any fixed date; here's an example with some dummy data set-up:
create table my_table (my_interval interval day(2) to second(6));
insert into my_table (my_interval) values (interval '0 12:34:56.78' day to second);
insert into my_table (my_interval) values (interval '99 01:02:03.456' day to second);
select my_interval, to_char(date '1970-01-01' + my_interval, 'HH:MI AM') as formatted
from my_table;
MY_INTERVAL FORMATTED
-------------------- ---------
+00 12:34:56.780000 12:34 PM
+99 01:02:03.456000 01:02 AM
The second value shows a potential problem. Your interval is defined to allow a two-digit day number, which means the interval can span anything less than 100 days. If you only extract the time portion you lose that information about the number of days. That may be what you want to happen though. If the interval is supposed to be representing a time of day, which wanting to show AM/PM implies - and it's unusual to store an actual time separate from its date - then having or allowing a number of days seems strange.

Calculate the week ending date in oracle using Saturday as the week end date

Given a field in Oracle that contains dates, how would you calculate what the week ending date is using Sun thru Sat as your week. For example, if the date is 1/26/2015 (which is a Monday), the query should return 1/31/2015 (which is a Saturday. If the date is 1/31/2015, then the query should return 1/31/2015.
Given any particular date / time value, this expression will return midnight of the preceding Sunday.
TRUNC(whatever_time,'DAY')
So, you can do stuff like this:
SELECT TRUNC(whatever_time,'DAY') week_starting,
TRUNC(whatever_time,'DAY') + 6 week_ending,
SUM(sales)
FROM table
GROUP BY TRUNC(whatever_time,'DAY')
and you'll get what you need.
Notice that TRUNC(whatever_time,'DAY') honors the Oracle session initialization parameter called “NLS_TERRITORY”. For example, in Europe Monday is considered the first business day of the week. Try this.
ALTER SESSION SET NLS_TERRITORY=GERMANY;
SELECT TRUNC( DATE '2013-12-31', 'DAY'),
TRUNC( DATE '2014-01-03', 'DAY')
FROM DUAL;
A complete writeup of this is here: http://www.plumislandmedia.net/sql-time-processing/using-sql-report-time-intervals-oracle/

Oracle ORA-01839: date not valid for month specified Leap Year

Oracle 11g
here is a quick one hopefully.
Below is part of a script that gets date only from from the next month
first day of next month to last day. But today 29th feb it thrown an error of
ORA-01839: date not valid for month specified
M.MS_DATE between trunc(sysdate + interval '1' month,'MM') and last_day(sysdate + interval '1' month)
Is there a way round this. Many thanks
I have seen this as well and I consider this a bug in Oracle.
The workaround is to use add_months() instead :
between trunc(add_months(sysdate,1),'MM') and last_day(add_months(sysdate,1));
I would probably use add_months() as a_horse_with_no_name suggests, but just as an alternative if you want to use intervals, you can move the point you do the truncation in the first expression, and include the same truncation in the second expression:
select trunc(sysdate, 'MM') + interval '1' month as first_day,
last_day(trunc(sysdate, 'MM') + interval '1' month) as last_day
from dual;
FIRST_DAY LAST_DAY
---------- ----------
2015-02-01 2015-02-28
This works because all months have a first day, so you don't trip over the documented caveat.
When interval calculations return a datetime value, the result must be an actual datetime value or the database returns an error

Resources