Oracle: subtract millisecond from a datetime - sql

I thought it was really simple but it isn't.
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY')
- 1/(24*50*60*1000) data
FROM dual;
It simply doesn't work.
Other details:
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY') -
NUMTODSINTERVAL(1/(24*50*60*1000),'HOUR') data
FROM dual;
doesn't work..
The right seems to be
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY') -
NUMTODSINTERVAL(1/(24*25*60*1000),'HOUR') data
FROM dual;
Why? How does it work?

For adding or subtracting an amount of time expressed as a literal you can use INTERVAL.
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY')
- INTERVAL '0.001' SECOND
FROM dual;
As well there are now standard ways to express date and time literals and avoid the use of various database specific conversion functions.
SELECT TIMESTAMP '2012-10-08 00:00:00'
- INTERVAL '0.001' SECOND DATA
FROM dual;
For your original question the time part of a day is stored in fractional days. So one second is:
1 / (hours in day * minutes in hour * seconds in a minute)
Divide by 1000 to get milliseconds.
1 / (24 * 60 * 60 * 1000)

SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY') - NUMTODSINTERVAL(1/(24*50*60*1000),'HOUR') data
FROM dual;
OUTPUT
DATA
---------------------------------
09/AUG/12 11:59:59.999950000 PM
1 row selected.

The answer posted above subtracts a tenth of a millisecond from the date. I think what you want is the following:
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY')-NUMTODSINTERVAL(1/1000,'SECOND')
FROM dual;
Output:
DATA
---------------------------------------------------------------------------
09-AUG-12 11.59.59.999000000 PM
^^^
|||
tenths|thousandths
|
hundredths
The following NUMTODSINTERVAL(1/(24*25*60*1000),'HOUR') seems to work only because 24*25 = 600. But that number is wrong because 1/(600*60*1000) of an hour is a tenth of a millisecond, not a millisecond. If you want to use 'HOUR' in NUMTODSINTERVAL() you should use 1/(60*60*1000) (sixty minutes in an hour, sixty seconds in a minute, 1000 ms in a second).

This is correct (with a millisecond being 1000th of a second):-
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY') - NUMTODSINTERVAL(1/1000,'SECOND') data FROM dual;
DATA
-----------------------------
09-AUG-12 23.59.59.999000000
As to why the other code isn't working it's because you aren't calculating a millisecond correctly. An hour must be divided by 60 to give minutes and again by 60 to given seconds then by 1000 to give a millisecond, thus if you must use HOUR as the interval then it is:-
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY') - NUMTODSINTERVAL(1/(60*60*1000),'HOUR') as data FROM dual;
DATA
---------------------------------------------------------------------------
09-AUG-12 23.59.59.999000000

select TO_CHAR(TO_TIMESTAMP('10.05.2012', 'DD.MM.YYYY') -
NUMTODSINTERVAL(1/1000, 'SECOND'), 'DD.MM.YYYY HH24:MI:SS:FF3') Res
from dual;
RES
-----------------------------
09.05.2012 23:59:59.999

Related

Interval Date to days [duplicate]

I have two timestamp columns: arrTime and depTime.
I need to find the number of munites the bus is late.
I tried the following:
SELECT RouteDate, round((arrTime-depTime)*1440,2) time_difference
FROM ...
I get the following error: inconsistent datatype . expected number but got interval day to second
How can i parse the nuber of minutes?
If i simply subtract: SELECT RouteDate, arrTime-depTime)*1440 time_difference
The result is correct but not well formatted:
time_difference
+00000000 00:01:00 0000000
The result of timestamp arithmetic is an INTERVAL datatype. You have an INTERVAL DAY TO SECOND there...
If you want the number of minutes one way would be to use EXTRACT(), for instance:
select extract( minute from interval_difference )
+ extract( hour from interval_difference ) * 60
+ extract( day from interval_difference ) * 60 * 24
from ( select systimestamp - (systimestamp - 1) as interval_difference
from dual )
Alternatively you can use a trick with dates:
select sysdate + (interval_difference * 1440) - sysdate
from (select systimestamp - (systimestamp - 1) as interval_difference
from dual )
The "trick" version works because of the operator order of precedence and the differences between date and timestamp arithmetic.
Initially the operation looks like this:
date + ( interval * number ) - date
As mentioned in the documentation:
Oracle evaluates expressions inside parentheses before evaluating those outside.
So, the first operation performed it to multiply the interval by 1,440. An interval, i.e. a discrete period of time, multiplied by a number is another discrete period of time, see the documentation on datetime and interval arithmetic. So, the result of this operation is an interval, leaving us with:
date + interval - date
The plus operator takes precedence over the minus here. The reason for this could be that an interval minus a date is an invalid operation, but the documentation also implies that this is the case (doesn't come out and say it). So, the first operation performed is date + interval. A date plus an interval is a date. Leaving just
date - date
As per the documentation, this results in an integer representing the number of days. However, you multiplied the original interval by 1,440, so this now represented 1,440 times the amount of days it otherwise would have. You're then left with the number of seconds.
It's worth noting that:
When interval calculations return a datetime value, the result must be an actual datetime value or the database returns an error. For example, the next two statements return errors:
The "trick" method will fail, rarely but it will still fail. As ever it's best to do it properly.
SELECT (arrTime - depTime) * 1440 time_difference
FROM Schedule
WHERE ...
That will get you the time difference in minutes. Of course, you can do any rounding that you might need to to get whole minutes....
Casting to DATE first returns the difference as a number, at least with the version of Oracle I tried.
round((cast(arrTime as date) - cast(depTime as date))*1440)
You could use TO_CHAR then convert back to a number. I have never tested the performance compared to EXTRACT, but the statement works with two dates instead of an interval which fit my needs.
Seconds:
(to_char(arrTime,'J')-to_char(depTime,'J'))*86400+(to_char(arrTime,'SSSSS')-to_char(depTime,'SSSSS'))
Minutes:
round((to_char(arrTime,'J')-to_char(depTime,'J'))*1440+(to_char(arrTime,'SSSSS')-to_char(depTime,'SSSSS'))/60)
J is julian day and SSSSS is seconds in day. Together they give an absolute time in seconds.

Query to fetch records only form previous half hour with time stamp in unix epoch format

I want SQL query to fetch/select records which are taken only from previous half an hour only. For example if my scheduler ran at 2 pm, and then again in 2:30, during the 2:30 run it should only pick rows from between 2pm and 2:30pm and not earlier, using the column created_timestamp which stores the time as unix epoch format eg:
|created_timestamp|
|1497355750350 |
|1497506182344 |
We can do arithmetic with Oracle dates. Subtracting one date from another gives the interval as a fractional number. Multiplying by 86400 gives us the number of seconds. So this is the current unix expoch:
(sysdate - date '1970-01-01') * 86400
This means your query will be something like
select * from your_table
where created_timestamp >= (:last_run_time - date '1970-01-01') * 86400
The trick is that your scheduler needs to pass in the time of the previous run - last_run_time - to pick up all the records which have been added since then.
You can do Flashback query
SELECT * FROM TABLE
AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '30' MINUTE);

Oracle database: getting time from total amount of minutes from the beginning of a day

Given - number of minutes (number oracle type) from the beginning of a day, e.g. 480. Need to get standard oracle time, e.g. - 08:00:00 AM. Is there any good functions to do such operation?
Better use INTERVAL 'minutes' MINUTE to add the number of minutes. Easy to understand.
your_date_time + INTERVAL '480' MINUTE
For example,
SQL> SELECT TRUNC(SYSDATE), TRUNC(SYSDATE) + INTERVAL '480' MINUTE tmstamp FROM dual;
TRUNC(SYSDATE) TMSTAMP
------------------- -------------------
11/19/2015 00:00:00 11/19/2015 08:00:00
In fact, another way which is independent of NLS settings when you have to pass the date as literal. Thus, instead of using TO_DATE, use the ANSI Date literal which uses a fixed format 'YYYY-MM-DD' and is NLS independent.
SQL> SELECT DATE '2015-11-19' curr_date, DATE '2015-11-19' + INTERVAL '480' MINUTE tmstamp
2 FROM dual;
CURR_DATE TMSTAMP
------------------- -------------------
11/19/2015 00:00:00 11/19/2015 08:00:00
UDPATE
Given - number of minutes (number oracle type) from the beginning of a day, e.g. 480
If the minutes value is not static in SQL to be hard-coded, but a PL/SQL variable, then as #AlexPoole mentioned you need to use NUMTODSINTERVAL.
For example,
NUMTODSINTERVAL(480, 'MINUTE')
Having said that,
The Oracle PL/SQL NUMTODSINTERVAL function converts an input number to its specified Interval Day to Second Unit equivalent. The allowed interval units can be DAY, HOUR, MINUTE, or SECOND.
The return type of the function is INTERVAL.
For example,
SQL> SELECT NUMTODSINTERVAL(480, 'MINUTE') intrvl FROM DUAL;
INTRVL
---------------------------------------------------------------------
+000000000 08:00:00.000000000
Just add minutes/1440 to the date, e.g.
select to_date('1.1.2015','dd.mm.yyyy')+480/1440 from dual;

oracle sql date format to only seconds [duplicate]

This question already has answers here:
How to find difference b/w TIMESTAMP format values in Oracle?
(3 answers)
Closed 8 years ago.
So I'm trying to convet a timestamp to seconds.
I read that you could do it this way
to_char(to_date(10000,'sssss'),'hh24:mi:ss')
But turns out this way you can't go over 86399 seconds.
This is my date format: +000000000 00:00:00.000000
What's the best approach to converting this to seconds? (this is the result of subtracting two dates to find the difference).
You could convert timestamp to date by adding a number (zero in our case).
Oracle downgrade then the type from timestamp to date
ex:
select systimestamp+0 as sysdate_ from dual
and the difference in secondes between 2 timestamp:
SQL> select 24*60*60*
((SYSTIMESTAMP+0)
-(TO_TIMESTAMP('16-MAY-1414:10:10.123000','DD-MON-RRHH24:MI:SS.FF')+0)
)
diff_ss from dual;
DIFF_SS
----------
15140
It looks like you're trying to find the total number of seconds in an interval (which is the datatype returned when you subtract two timestamps). In order to convert the interval to seconds, you need to extract each component and convert them to seconds. Here's an example:
SELECT interval_value,
(EXTRACT (DAY FROM interval_value) * 24 * 60 * 60)
+ (EXTRACT (HOUR FROM interval_value) * 60 * 60)
+ (EXTRACT (MINUTE FROM interval_value) * 60)
+ EXTRACT (SECOND FROM interval_value)
AS interval_in_sec
FROM (SELECT SYSTIMESTAMP - TRUNC (SYSTIMESTAMP - 1) AS interval_value
FROM DUAL)
If you want the number of seconds between two dates (or timestamps),
select floor(
(to_date(to_char(timestamp1, 'yyyy-mm-dd HH24:MI:ss'),'yyyy-mm-dd HH24:MI:ss')
- to_date(to_char(timestamp2, 'yyyy-mm-dd HH24:MI:ss'),'yyyy-mm-dd HH24:MI:ss')
)
* 24 -- hours per day
* 60 -- minutes per hour
* 60 -- seconds per minute
)
etc

Display correct subtraction of two timestamps in create view

By using normal minus '-' function between two timestamps, the answer given from oracle is incorrect.
This is what i want to do:
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT='DD-MON-RR HH24:MI TZR';
Created table:
CREATE TABLE TEST (
StartTime timestamp with time zone
,EndTime timestamp with time zone
,Science varchar2(7)
);
I create the column data type as timestamp with time zone. This is value I have inserted:
INSERT INTO TEST
VALUES('05-OCT-2013 01:00 +08:00'
,'05-OCT-2013 23:00 +06:00'
,'SCIENCE');
INSERT INTO TEST
VALUES('05-OCT-2013 12:00 +08:00'
,'05-OCT-2013 15:00 -12:00'
,'Maths');
Attempted for rounding time:
CREATE VIEW TESTRECRDS AS
SELECT (Extract(hour FROM(ENDTIME- STARTTIME)) || 'Hours' ||
Extract(minute FROM(ENDTIME- STARTTIME))>=60 Then (Extract(hour FROM(ENDTIME- STARTTIME)) + Extract(minute FROM(ENDTIME- STARTTIME))/60 ELSE 0 END || 'Minutes' AS DURATION,
Science
FROM Test;
Now i have two questions regarding on the calculation and rounding off the minutes to nearest hours.
First let's say the endtime is 1535 +0600 and starttime is 01:50 +0800
So when i deduct endtime - starttime:
the formula should be:
2135 - 0950 = 2085 - 0950
= 1135
But if i use my successful attempt answer to calculate, it is not the correct exact answer. The oracle answer would be 15 hours 45 minutes.
In your last CREATE VIEW statement you try to multiply text, which cannot work:
SELECT To_Char(STARTTIME - ENDTIME, 'HH24:MI TZR')*24 AS DURATION
*24 is operating on the text to_char() returns.
You have to multiply the interval before converting to text.
You define the column Science varchar2(6), then you insert 'SCIENCE', a 7-letter word?
I also fixed a syntax error in your INSERT statement: missing '.
About your comment:
"I would like to insert timestamp with timezone during creation of my tables. Can DATE data type do that too?
Read about data types in the manual.
The data type date does not include time zone information.
If by "timezone difference" you mean the difference between the timezone modifiers, use this to calculate:
SELECT EXTRACT(timezone_hour FROM STARTTIME) AS tz_modifier FROM tbl
Keywords here are timezone_hour and is timezone_minute. Read more in the manual.
But be aware that these numbers depend on the daylight saving hours and such shenanigans. Very uncertain territory!
Get it in pretty format - example:
SELECT to_char((EXTRACT (timezone_hour FROM STARTTIME) * 60
+ EXTRACT (timezone_minutes FROM STARTTIME))
* interval '1 min', 'HH:MI')
In PostgreSQL you would have the simpler EXTRACT (timezone FROM STARTTIME), but I don't think Oracle supports that. Can't test now.
Here is a simple demo how you could round minutes to hours:
SELECT EXTRACT(hour FROM (ENDTIME - STARTTIME))
+ CASE WHEN EXTRACT(minute FROM (ENDTIME - STARTTIME)) >= 30 THEN 1 ELSE 0 END
FROM Test;
I'm not sure what number you're trying to calculate, but when you subtract two dates in Oracle, you get the difference between the dates in units of days, not a DATE datatype
SELECT TO_DATE('2011-01-01 09:00', 'yyyy-mm-dd hh24:mi') -
TO_DATE('2011-01-01 08:00', 'yyyy-mm-dd hh24:mi') AS diff
FROM dual
DIFF
----------
.041666667
In this case 8am and 9am are 0.41667 days apart. This is not a date object, this is a scalar number, so formatting it as HH24:MI doesn't make any sense.
To round you will need to do a bit of more math. Try something like:
TO_DATE(ROUND((ENDTIME - STARTTIME) * 96) / 96, 'HH24:MI')
The difference between dates is in days. Multiplying by 96 changes the measure to quarter hours. Round, then convert back to days, and format. It might be better to use a numeric format want to format, in which case you would divide by 4 instead of 96.
Timezone is not particularly relevant to a time difference. You will have to adjust the difference from UTC to that timezone to get the right result with Timezone included.