This question already has answers here:
Calculate difference between 2 date / times in Oracle SQL
(21 answers)
Closed 9 years ago.
I have 2 dates with following format:
ST_DT = Sun Dec 29 11:55:29 EST 2013
ED_DT = Tue Dec 30 20:21:34 EST 2013
I want to find the difference between these 2 dates in HH:MM:SS format. Now my problem is that i don't know how to parse the above date format in Oracle.
Are the dates in varchar2 type? Then, you can first convert it into timestamp format. Since it has timezone also, use the to_timestamp_tz function.
SQL> select to_timestamp_tz('Sun Dec 29 11:55:29 EST 2013','Dy Mon dd hh24:mi:ss TZR yyyy') from dual;
TO_TIMESTAMP_TZ('SUNDEC2911:55:29EST2013','DYMONDDHH24:MI:SSTZRYYYY')
---------------------------------------------------------------------------
29-DEC-13 11.55.29.000000000 AM EST
Once the dates are in timestamp type, subtracting them will give you the difference in interval day to second type.
SQL> select to_timestamp_tz ('Mon Dec 30 20:21:34 EST 2013','Dy Mon dd hh24:mi:ss TZR yyyy')
2 - to_timestamp_tz ('Sun Dec 29 11:55:29 EST 2013','Dy Mon dd hh24:mi:ss TZR yyyy') from dual;
TO_TIMESTAMP_TZ('MONDEC3020:21:34EST2013','DYMONDDHH24:MI:SSTZRYYYY')-TO_TI
---------------------------------------------------------------------------
+000000001 08:26:05.000000000
Then use extract to get the individual components from the interval.
SQL> select extract(day from intrvl) as dd,
2 extract(hour from intrvl) as hh24,
3 extract(minute from intrvl) as mi,
4 extract(second from intrvl) as ss
5 from (
6 select to_timestamp_tz ('Mon Dec 30 20:21:34 EST 2013','Dy Mon dd hh24:mi:ss TZR yyyy')
7 - to_timestamp_tz ('Sun Dec 29 11:55:29 EST 2013','Dy Mon dd hh24:mi:ss TZR yyyy') as intrvl
8 from dual
9 );
DD HH24 MI SS
---------- ---------- ---------- ----------
1 8 26 5
I figured out one solution but it will work only for same time zone dates.
with tab as (
select to_date(replace(substr('Sun Dec 28 23:59:59 EST 2013', 4), 'EST '), 'Mon DD HH24:MI:SS RRRR') start_date,
to_date(replace(substr('Tue Dec 30 20:21:34 EST 2013', 4), 'EST '), 'Mon DD HH24:MI:SS RRRR') end_date
from dual),
tab_sec as
(select ((end_date - start_date) * 24 * 60 * 60) sec from tab)
select lpad(trunc(sec / (60*60)), 2, '0')||':'||
lpad(trunc((sec - (trunc(sec / (60*60)) * 60 * 60))/60), 2, '0')||':'||
lpad(mod(sec, 60), 2, '0') diff
from tab_sec;
You can use NUMTODSINTERVAL
For example
with x as (
select to_date('01/01/2014 10:00:00','dd/mm/yyyy hh24:mi:ss') d1 ,
to_date('01/01/2014 12:00:00','dd/mm/yyyy hh24:mi:ss') d2
from dual
union all
select to_date('02/01/2014 10:00:00','dd/mm/yyyy hh24:mi:ss') d1 ,
to_date('01/01/2014 12:00:00','dd/mm/yyyy hh24:mi:ss') d2
from dual
union all
select to_date('01/01/2014 10:30:00','dd/mm/yyyy hh24:mi:ss') d1 ,
to_date('01/01/2014 12:00:00','dd/mm/yyyy hh24:mi:ss') d2
from dual
union all
select to_date('01/01/2014 10:00:30','dd/mm/yyyy hh24:mi:ss') d1 ,
to_date('01/01/2014 12:00:00','dd/mm/yyyy hh24:mi:ss') d2
from dual
union all
select to_date('01/01/2014 10:00:30','dd/mm/yyyy hh24:mi:ss') d1 ,
to_date('02/01/2014 12:20:10','dd/mm/yyyy hh24:mi:ss') d2
from dual
)
select d1 , d2 , numtodsinterval(d2 - d1, 'day') as interval_diff
from x
D1 D2 INTERVAL_DIFF
------------------- ------------------- ---------------------------------
01/01/2014 10:00:00 01/01/2014 12:00:00 +000000000 02:00:00.000000000
02/01/2014 10:00:00 01/01/2014 12:00:00 -000000000 22:00:00.000000000
01/01/2014 10:30:00 01/01/2014 12:00:00 +000000000 01:30:00.000000000
01/01/2014 10:00:30 01/01/2014 12:00:00 +000000000 01:59:30.000000000
01/01/2014 10:00:30 02/01/2014 12:20:10 +000000001 02:19:39.999999999
Related
How to display CST or CDT after the date & time, like shown below.
Sep 09 2022 10:30:32 PM CDT
Thanks.
Start with a TIMESTAMP WITH TIME ZONE data type of TIMESTAMP '2022-09-09 22:30:32 US/CENTRAL' and then format it using TO_CHAR and the format model 'Mon DD YYYY HH12:MI:SS AM TZD':
SELECT TO_CHAR(
TIMESTAMP '2022-09-09 22:30:32 US/CENTRAL',
'Mon DD YYYY HH12:MI:SS AM TZD',
'NLS_DATE_LANGUAGE=American'
) AS ts
FROM DUAL;
Which outputs:
TS
Sep 09 2022 10:30:32 PM CDT
If you have a DATE then cast it to a timestamp and use FROM_TZ to give it the US/CENTRAL time zone and then convert it to a string as above:
SELECT TO_CHAR(
FROM_TZ(
CAST(
DATE '2022-09-09' + INTERVAL '22:30:32' HOUR TO SECOND
AS TIMESTAMP
),
'US/CENTRAL'
),
'Mon DD YYYY HH12:MI:SS AM TZD',
'NLS_DATE_LANGUAGE=American'
) AS ts
FROM DUAL;
Which also outputs:
TS
Sep 09 2022 10:30:32 PM CDT
fiddle
Try something like this...
SELECT
To_Char(To_TimeStamp_TZ(To_Char(SYSDATE, 'Mon dd yyyy hh24:mi:ss'), 'Mon dd yyyy hh24:mi:ss ' || 'TZH:TZM'), 'Mon dd yyyy hh24:mi:ss ') ||
CASE To_Char(To_TimeStamp_TZ(To_Char(SYSDATE, 'Mon dd yyyy hh24:mi:ss'), 'Mon dd yyyy hh24:mi:ss ' || 'TZH:TZM'), 'TZH:TZM')
WHEN '-05:00' THEN 'CDT'
WHEN '-06:00' THEN 'CST'
-- ... ...
WHEN '+01:00' THEN 'CET'
WHEN '+02:00' THEN 'EET'
-- ... ... ...
ELSE
To_Char(To_TimeStamp_TZ(To_Char(SYSDATE, 'Mon dd yyyy hh24:mi:ss'), 'Mon dd yyyy hh24:mi:ss ' || 'TZH:TZM'), 'Mon dd yyyy hh24:mi:ss TZH:TZM')
END "DTM"
FROM DUAL
.... which in my case results as
-- DTM
-- ---------------------------
-- Oct 13 2022 19:19:03 EET
... OR if I exclude WHEN '+02:00' from case expresssion (forcing result to ELSE part)
-- DTM
-- ---------------------------
-- Oct 13 2022 19:22:07 +02:00
More about To_TimeStamp_TZ() function at: https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions194.htm
Time zone definitions at: https://greenwichmeantime.com/time-zone/definition/
Regards...
select 24 * ROUND(
to_date(OUT_TIME, 'YYYY-MM-DD hh24:mi')
- to_date(IN_TIME, 'YYYY-MM-DD hh24:mi'),
2
) diff_hours
from attendances
where employee_id = 1001;
select 24 * ROUND(
to_date('2009-07-07 23:44', 'YYYY-MM-DD hh24:mi')
- to_date('2009-07-07 19:30', 'YYYY-MM-DD hh24:mi'),
2
) diff_hours
from dual;
If you stored (date)time values as strings (which is a bad idea), you'll have to use that format with to_date. Using sample data you posted:
SQL> with test (employee, in_time, out_time) as
2 (select 'Palash', '08:45 PM', '08:45 PM' from dual union all
3 select 'Palash', '07:40 PM', '07:45 PM' from dual union all
4 select 'Sagor' , '08:10 PM', '08:43 PM' from dual
5 )
6 select employee, in_time, out_time,
7 round((to_date(out_time, 'hh:mi pm') - to_date(in_time, 'hh:mi pm')) * 24, 2) diff
8 from test;
EMPLOY IN_TIME OUT_TIME DIFF
------ -------- -------- ----------
Palash 08:45 PM 08:45 PM 0
Palash 07:40 PM 07:45 PM .08
Sagor 08:10 PM 08:43 PM .55
SQL>
I have this value:
Thu Jan 01 01:00:00 UZT 1970
I need this value from the above value:
Thu Jan 1970
Need Substring from this value.
Use TO_DATE then TO_CHAR:
SELECT TO_CHAR(
TO_DATE(
'Thu Jan 01 01:00:00 UZT 1970',
'Dy Mon DD HH24:MI:SS "UZT" YYYY'
),
'Dy Mon YYYY'
) AS substring
FROM DUAL
| SUBSTRING |
| :----------- |
| Thu Jan 1970 |
db<>fiddle here
If you want it as DD-MM-YYYY format then change the format model used in TO_CHAR to 'DD-MM-YYYY' but if you are going to be storing it in the database it would be better to just store it as a date value rather than as a formatted string.
I have some columns "UZT" and some "UZST" then getting error on "UZST"
If those are the only time zones and you are not worried if there is an hours difference between the two zones then you can use:
SELECT TO_CHAR(
TO_DATE(
REPLACE( your_column, 'UZST', 'UZT' ),
'Dy Mon DD HH24:MI:SS "UZT" YYYY'
),
'Dy Mon YYYY'
) AS substring
FROM your_table
or if you need to account for the time zones (replace the time zone values as appropriate):
SELECT TO_CHAR(
TO_TIMESTAMP_TZ(
REPLACE(
REPLACE(
your_column,
'UZST',
'+05:00'
),
'UZT',
'+05:00'
),
'Dy Mon DD HH24:MI:SS TZH:TZM YYYY'
) AT TIME ZONE '+05:00',
'Dy Mon YYYY'
) AS substring
FROM your_table
Looks like a string. If that's so, here's one (actually, two) options:
SQL> with test (col) as
2 (select 'Thu Jan 01 01:00:00 UZT 1970' from dual)
3 select substr(col, 1, instr(col, ' ', 1, 2)) || substr(col, -4) result,
4 regexp_substr(col, '(\w+ ){2}') || regexp_substr(col, '\d+$') result2
5 from test;
RESULT RESULT2
------------ ------------
Thu Jan 1970 Thu Jan 1970
SQL>
[EDIT]
How to convert it to a different format? By applying TO_DATE (to convert "Thu Jan 1970", which is a string, to a valid DATE format), and then TO_CHAR with appropriate format mask to get what you want.
SQL> with test (col) as
2 (select 'Thu Jan 01 01:00:00 UZT 1970' from dual),
3 inter as
4 (select substr(col, 1, instr(col, ' ', 1, 2)) || substr(col, -4) result,
5 regexp_substr(col, '(\w+ ){2}') || regexp_substr(col, '\d+$') result2
6 from test
7 )
8 select to_char(to_date(result, 'dy mon yyyy', 'nls_date_language = english'), 'dd-mm-yyyy') new_result
9 from inter;
NEW_RESULT
----------
01-01-1970
SQL>
So I'm trying to select the distinct operators from a table with a time component that is formatted like this: 'MM/DD/YYYY HH:MI:SS AM.'
The logic is this:
(if C_StartTime >= date(C_StartTime) + 6:00:00 AM
AND C_StartTime < date(C_StartTime) + 5:59:59AM
then C_StartTime,'MM/DD/YYYY'
ELSE (C_StartTime,'MM/DD/YYYY')-1)
AS DateOnly
I can select distinct operators right now but they sign in and out a few times a day so the time is different. It should be noted that "Today" at this company is 3/13/19 6:00:00 AM to 3/14/19 5:59:59 AM.
Below is the final code I tried executing
SELECT
DISTINCT (
(
CASE WHEN C_StartTime >= date(C_StartTime) +.25
AND C_StartTime < date(C_StartTime) +.9999
THEN date(C_StartTime)
ELSE date(C_StartTime) -1
) as DateOnly,
C_operator,
C_operatorname,
C_WorkCentreName
FROM
OPERATORTABLE
WHERE
.....
EDIT>>>>>>
This is what I get.
This is what I need
I'm Looking for the operator number, the operator name, and the date only (with the knowledge that 1/4/2019 5:59:00 AM = 1/3/2019
I think you're probably after something like trunc(c_starttime - 6/24) + 6/24 to get the day to start at 6am instead of midnight:
WITH dts AS (SELECT to_date('13/03/2019 05:59:59', 'dd/mm/yyyy hh24:mi:ss') dt FROM dual UNION ALL
SELECT to_date('13/03/2019 06:00:00', 'dd/mm/yyyy hh24:mi:ss') dt FROM dual UNION ALL
SELECT to_date('13/03/2019 06:00:01', 'dd/mm/yyyy hh24:mi:ss') dt FROM dual UNION ALL
SELECT to_date('14/03/2019 05:59:59', 'dd/mm/yyyy hh24:mi:ss') dt FROM dual UNION ALL
SELECT to_date('14/03/2019 06:00:00', 'dd/mm/yyyy hh24:mi:ss') dt FROM dual)
SELECT dt,
dt - 6/24 adj_dt,
TRUNC(dt - 6/24) trunc_adj_dt,
TRUNC(dt - 6/24) + 6/24 adj_start_of_dt
FROM dts;
DT ADJ_DT TRUNC_ADJ_DT ADJ_START_OF_DT
------------------- ------------------- ------------------- -------------------
13/03/2019 05:59:59 12/03/2019 23:59:59 12/03/2019 00:00:00 12/03/2019 06:00:00
13/03/2019 06:00:00 13/03/2019 00:00:00 13/03/2019 00:00:00 13/03/2019 06:00:00
13/03/2019 06:00:01 13/03/2019 00:00:01 13/03/2019 00:00:00 13/03/2019 06:00:00
14/03/2019 05:59:59 13/03/2019 23:59:59 13/03/2019 00:00:00 13/03/2019 06:00:00
14/03/2019 06:00:00 14/03/2019 00:00:00 14/03/2019 00:00:00 14/03/2019 06:00:00
I've selected the adj_dt and trunc_adj_dt columns so that you can see how the adj_start_of_dt column was calculated from the original dt column.
You may not need to output 6am on the start date column, so you can skip that (i.e. it's trunc_adj_dt that is the column you'd be after in that case).
I have the following system information:
I use Oracle Database 10g
The SysTimeStamp is UTC
The SessionTimeZone is Europe/Athens
The dbTimeZone is +03:00
So, I have the column date_1 from tbl_1 table, with the following datetime:
date_1
-----------------
08.02.2017 10:00
08.02.2017 11:00
08.02.2017 12:00
-----------------
The results I want is like this:
date_2
-----------------
08.02.2017 13:00
08.02.2017 14:00
08.02.2017 15:00
For that I use:
SELECT TO_CHAR(date_1 + INTERVAL '3' HOUR, 'DD.MM.YYYY HH24:MI') as date_2
FROM tbl_1
WHERE date_1 >= TO_DATE('08.02.2017 10:00','DD.MM.YYYY HH24:MI')
AND date_1 <= TO_DATE('08.02.2017 12:00','DD.MM.YYYY HH24:MI')
My problem appear when the hour from March and October is changing because in the last Sunday from March we have 23 hours in a day and in the last Sunday from October we have 25 hours in a day.
Because of this I have to change my query 4 times/year (On summer time, on winter time, when we have 23 hour in March and when we have 25 hour in October)
Can you recommend a query in this select that solve this problem?
If you have a plain date or timestamp with no embedded time zone information, you can tell Oracle to treat it as being in a specific time zone with the from_tz() function. You can then convert that value - which now has data type 'timestamp with zone zone' rather than a plain 'timestamp' - to another zone with the at time zone datetime expression syntax, either using the session time zone as 'local' or with a specific named time zone:
alter session set nls_date_format='YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_format='YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_tz_format='YYYY-MM-DD HH24:MI:SS TZR';
alter session set time_zone = 'America/New_York';
with cte (ts) as (
select timestamp '2017-02-08 12:00:00' from dual
)
select ts,
from_tz(ts, 'UTC') as ts_utc,
from_tz(ts, 'UTC') at local as ts_local,
from_tz(ts, 'UTC') at time zone 'Europe/Athens' as ts_athens
from cte;
TS TS_UTC TS_LOCAL TS_ATHENS
------------------- ----------------------- ------------------------------------ ---------------------------------
2017-02-08 12:00:00 2017-02-08 12:00:00 UTC 2017-02-08 07:00:00 AMERICA/NEW_YORK 2017-02-08 14:00:00 EUROPE/ATHENS
If you're starting from a date then you have to convert it to a timestamp before calling from_tz():
with cte (dt) as (
select cast( timestamp '2017-02-08 12:00:00' as date) from dual
)
select dt,
from_tz(cast(dt as timestamp), 'UTC') as ts_utc,
from_tz(cast(dt as timestamp), 'UTC') at local as ts_local,
from_tz(cast(dt as timestamp), 'UTC') at time zone 'Europe/Athens' as ts_athens
from cte;
DT TS_UTC TS_LOCAL TS_ATHENS
------------------- ----------------------- ------------------------------------ ---------------------------------
2017-02-08 12:00:00 2017-02-08 12:00:00 UTC 2017-02-08 07:00:00 AMERICA/NEW_YORK 2017-02-08 14:00:00 EUROPE/ATHENS
So the data type of your original date_1 values matters, as does the nominal time zone it is supposed to represent. If it's a;ready a 'timestamp with time zone' or 'timestamp with local time zone' then it already has embedded time zone information, so you don't need the from_tz() part at all. If it's a date you need to convert it to a timestamp.
Assuming that date_1 is stored as a plain timestamp (maybe implied by your interval addition, but not by the column name and filters you used) and that it's nominally UTC, you could do:
from_tz(date_1, 'UTC') at time zone 'Europe/Athens'
... which will give you a 'timestamp with time zone' result; or you could use local to rely on your session time zone. If `date_1 is stored as a date you'd add the conversion to timestamp:
from_tz(cast(date_1 as timestamp), 'UTC') at time zone 'Europe/Athens'
As a demo, generating timestamps (not dates) in a CTE including some around the DST change for this year:
with tbl_1(date_1) as (
select timestamp '2017-02-08 10:00:00' from dual
union all select timestamp '2017-02-08 11:00:00' from dual
union all select timestamp '2017-02-08 12:00:00' from dual
union all select timestamp '2017-03-23 12:00:00' + numtodsinterval(level, 'day')
from dual connect by level <= 4
)
select date_1,
-- cast(from_tz(date_1, 'UTC') at time zone 'Europe/Athens' as timestamp) as date_2
to_char(from_tz(date_1, 'UTC') at time zone 'Europe/Athens',
'DD.MM.YYYY HH24:MI') as date_2
from tbl_1
order by date_1;
DATE_1 DATE_2
------------------- ----------------
2017-02-08 10:00:00 08.02.2017 12:00
2017-02-08 11:00:00 08.02.2017 13:00
2017-02-08 12:00:00 08.02.2017 14:00
2017-03-24 12:00:00 24.03.2017 14:00
2017-03-25 12:00:00 25.03.2017 14:00
2017-03-26 12:00:00 26.03.2017 15:00
2017-03-27 12:00:00 27.03.2017 15:00
You can see that an extra hour is added automatically after the clocks change on March 26th. But the results are out by an hour for your sample February data - so either your data isn't actually stored as UTC (but is -01:00, and you can change the from_tz() call to reflect that), or your expected results are wrong.
You can apply a case to the select:
select date_1 + case
when to_char(date_1 ,'MM') <= 3 then 2/24 -- Jan/Feb/Mar
when to_char(date_1,'MM') <= 10 then 3/24 -- Apr to Oct
else 2/24 -- Nov/Dec
end as date_2
from tbl_1
For USA timezone
SELECT SYSDATE,
NEXT_DAY ( TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') + 7 dst_start,
NEXT_DAY ( TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/11/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') dst_end,
CASE WHEN SYSDATE >= NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') + 7 AND SYSDATE < NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/11/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') THEN 'Y' ELSE 'N' END AS dst_check_usa,
NEW_TIME ( SYSDATE, CASE WHEN SYSDATE >= NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') + 7 AND SYSDATE < NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/11/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') THEN 'CDT' ELSE 'CST' END, 'GMT') AS current_time_gmt
FROM DUAL;
For Europe Timezone
SELECT SYSDATE,
NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') dst_start_uk,
NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/10/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') dst_end_uk,
CASE WHEN SYSDATE >= NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') AND SYSDATE < NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/10/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') THEN 'Y' ELSE 'N' END AS dst_check_uk
FROM DUAL;