Oracle. Correct offset in timestamp with timezone - sql

I'm executing this select now
SELECT FROM_TZ(to_timestamp('2015-08-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'),'Asia/Singapore') AT TIME ZONE 'UTC'
FROM DUAL
I'm interesting in time, in this case time is 02:00 AM, because Singapore has difference between UTC in 8 hours.Everything is ok, but, if I'm changing month from 08 to 01, I'm expecting to get 03:00 AM, because it was winter time in Singapore, but I get 02:00 AM again. So the question is, how could I get the correct result with correct offset?

Asia/Singapore does not have any Daylight saving times, see here: Singapore Standard Time
Crazy, Oracle implemented this list properly:
SELECT FROM_TZ(TO_TIMESTAMP('2015-01-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'), 'Asia/Singapore') AS TS FROM DUAL;
TS
----------------------------------------
08.01.2015 10:00:00.000000000 +08:00
1 row selected.
SELECT FROM_TZ(TO_TIMESTAMP('1970-01-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'), 'Asia/Singapore') AS TS FROM DUAL;
TS
----------------------------------------
08.01.1970 10:00:00.000000000 +07:30
1 row selected.
SELECT FROM_TZ(TO_TIMESTAMP('1943-01-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'), 'Asia/Singapore') AS TS FROM DUAL;
TS
----------------------------------------
08.01.1943 10:00:00.000000000 +09:00
1 row selected.
SELECT FROM_TZ(TO_TIMESTAMP('1940-01-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'), 'Asia/Singapore') AS TS FROM DUAL;
TS
----------------------------------------
08.01.1940 10:00:00.000000000 +07:20
1 row selected.
SELECT FROM_TZ(TO_TIMESTAMP('1920-01-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'), 'Asia/Singapore') AS TS FROM DUAL;
TS
----------------------------------------
08.01.1920 10:00:00.000000000 +07:00
1 row selected.

Related

I have a table containing in_time and out_time in the format 'HH12:MI'(varchar2).I want to calculate the total time in hours between this column

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>

Oracle Sql adding hours and minute to a to_date while insert statment

Hello I am trying to make an insert statement which includes adding 2 hours and 10 minutes to a to_date .
But i do not know which function i can use.
this is my statement so far
insert into xyz values (TO_DATE('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM'))
but i want to add 2 hours and 10 minutes to the above while inserting.
Use date arithmetics. In Oracle, you can add decimal values to a date (1 stands for "1 day"):
insert into xyz
values (
to_date('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM')
+ 2/24 + 10 / 60 / 24
)
Or:
insert into xyz
values (
to_date('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM')
+ interval '2' hour + interval '10' minute
)
Or (with credits to Wernfried Domscheit):
insert into xyz
values (
to_date('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM')
+ interval '2:10' hour to minute
)
You can use interval as well,
insert into xyz values (TO_DATE('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM') + interval '130' minute);
select TO_DATE('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM') + interval '130' minute from dual;
TO_DATE('22-OCT-202
-------------------
22-10-2020 01:10 PM
select TO_DATE('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM') + interval '1' day from dual;
TO_DATE('22-OCT-202
-------------------
23-10-2020 11:00 AM

Oracle SQL: Handle with Daylight Saving Time

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;

Wrong conversion of date-string to date

When running these statements:
select to_date('201103270100', 'YYYYMMDDHH24MI') from dual;
select to_date('201103270130', 'YYYYMMDDHH24MI') from dual;
I get these results:
27/3/2011 1:00:00
27/3/2011 1:30:00
which are correct.
But when running:
select to_date('201103270200', 'YYYYMMDDHH24MI') from dual
I get a wrong hour as result
27/3/2011 3:00:00
More samples:
select to_date('201103270215', 'YYYYMMDDHH24MI') from dual
select to_date('201103270245', 'YYYYMMDDHH24MI') from dual
select to_date('201103270300', 'YYYYMMDDHH24MI') from dual
select to_date('201103270330', 'YYYYMMDDHH24MI') from dual
27/3/2011 3:15:00
27/3/2011 3:45:00
27/3/2011 3:00:00
27/3/2011 3:30:00
When the date is 20110326, I get the correct result.
This is crazy... Is Oracle SQL bullying me with this conversion?
Help is welcome!
Is Oracle SQL bullying me with this conversion?
Oracle is too dumb to bully a developer, unless the developer tries to bully it ;-)
There can't be different outputs without any reason. In Europe, in the year 2011, the day light saving was with effect from 27th March, 2011 at 2:00 AM. Source is wikipedia.
So, keeping in mind the day light saving, let's see the results for the datetime after 2:00 AM:
SQL> SELECT to_char(to_date('201103270100', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 01:00:00
SQL> SELECT to_char(to_date('201103270130', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 01:30:00
SQL> SELECT to_char(to_date('201103270200', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 02:00:00
SQL> SELECT to_char(to_date('201103270215', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 02:15:00
SQL> SELECT to_char(to_date('201103270245', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 02:45:00
SQL> SELECT to_char(to_date('201103270300', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 03:00:00
SQL> SELECT to_char(to_date('201103270330', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 03:30:00
SQL>
If your provided data and examples are correct, then this should be the reason.
So, you should see the time portion exactly at 2:00 AM to shift to 3:00 AM. Similarly, 2:15 AM would be 3:15 AM. Just add an hour to the time for those which are after 2:00 AM.

Credit for sales report filing time

I have two date_time fields. The first is the date_time of the sale and the second is the date_time the sales report is filed.
In order for the salesperson to obtain credit, the sales report has to be filed by midnight on the date after the sale was made.
I'm currently calculating the difference, and know that anything over 24 hours could be out of a qualifying time period. Also, there are times when the sales report is filed prior to the date_time of the sale.
I searched through previous answers and couldn't find anything similar.
You need something like:
select to_char(report_time, 'mm/dd/yyyy hh24:mi:ss') report_time,
to_char(sales_time, 'mm/dd/yyyy hh24:mi:ss') sales_time,
round((report_time - sales_time) * 24, 6) diff_in_hours,
case when report_time < trunc(sales_time) + 2 then 'Y' else 'N' end decision
from sales
Function trunc() cuts hours, minutes and seconds from sales_time. For instance for date 01/15/2015 11:59:00 it returns 01/15/2015 00:00:00.
Next we add 2 days to this result. Report_date has to be lower then this value. So:
report_time < trunc(sales_time) + 2
Test with sample data:
with sales as (select
to_date('01/15/2015 11:59:00', 'mm/dd/yyyy hh24:mi:ss') report_time,
to_date('01/15/2015 11:45:00', 'mm/dd/yyyy hh24:mi:ss') sales_time from dual
union all select
to_date('01/16/2015 23:59:00', 'mm/dd/yyyy hh24:mi:ss'),
to_date('01/15/2015 12:45:00', 'mm/dd/yyyy hh24:mi:ss') from dual
union all select
to_date('01/17/2015 00:00:01', 'mm/dd/yyyy hh24:mi:ss'),
to_date('01/15/2015 23:59:00', 'mm/dd/yyyy hh24:mi:ss') from dual)
select to_char(report_time, 'mm/dd/yyyy hh24:mi:ss') report_time,
to_char(sales_time, 'mm/dd/yyyy hh24:mi:ss') sales_time,
round((report_time - sales_time) * 24, 6) diff_in_hours,
case when report_time < trunc(sales_time) + 2 then 'Y' else 'N' end decision
from sales
Output:
REPORT_TIME SALES_TIME DIFF_IN_HOURS DECISION
------------------- ------------------- ------------- --------
01/15/2015 11:59:00 01/15/2015 11:45:00 0,233333 Y
01/16/2015 23:59:00 01/15/2015 12:45:00 35,233333 Y
01/17/2015 00:00:01 01/15/2015 23:59:00 24,016944 N