Repeating time (1 AM-1:59:59 AM) on first Sunday of November - Daylight Savings ending day - Oracle - sql

Good afternoon.
I understand that there is "each second of each minute of one specific hour" is repeated (1 AM - 1:59:59 AM) on first Sunday of November (Closing day of Daylight Saving time). So, duration from 0:00 AM (midnight) to 3 AM is 4 hours on that day.
SELECT TO_TIMESTAMP_TZ('2021-11-07 03:00:00 US/Mountain',
'yyyy-mm-dd hh24:mi:ss TZR')
- TO_TIMESTAMP_TZ('2021-11-07 00:00:00 US/Mountain', 'yyyy-mm-dd hh24:mi:ss TZR') FROM DUAL;
The above query is returning 4 hours as expected.
Here is my question -
I want to basically differentiate/represent the two occurrences of 1 AM (or any time between 1 AM, and 1:59:59 AM). How can I do? (I am using Oracle 12.1)
BTW, this following query is resulting 1 hour 30 minutes, so '2021-11-07 01:30:00 MST' represents the second instance of 1:30 AM. In the same manner, I was expecting '2021-11-07 01:30:00 MDT' to be the first instance, however it is resulting ORA-01882: timezone region not found. BTW, I prefer to have US/Mountain (or something like that) for region, rather MST vs. MDT
SELECT TO_TIMESTAMP_TZ('2021-11-07 03:00:00 MST', 'yyyy-mm-dd
hh24:mi:ss TZR')
- TO_TIMESTAMP_TZ('2021-11-07 01:30:00 MST', 'yyyy-mm-dd hh24:mi:ss TZR') FROM DUAL;
Sorry if I confused you. Please let me know if any questions.
Any help?
Thank you
Viswa
Added later:
I think I found the answer: We need to use TZD flag, and use MST/MDT values.
I did not like that as I prefer to use the region (such as US/Mountain).
So any enhancements will be appreciated.
SELECT TO_TIMESTAMP_TZ('2021-11-07 03:00:00 US/Mountain',
'yyyy-mm-dd hh24:mi:ss TZR')
- TO_TIMESTAMP_TZ('2021-11-07 00:00:00 US/Mountain', 'yyyy-mm-dd hh24:mi:ss TZR') FROM DUAL;
-- 4 hours:00 minutes, as expected
SELECT TO_TIMESTAMP_TZ('2021-11-07 03:00:00 MST', 'yyyy-mm-dd
hh24:mi:ss TZD')
- TO_TIMESTAMP_TZ('2021-11-07 01:30:00 MDT', 'yyyy-mm-dd hh24:mi:ss TZD') FROM DUAL;
-- 2:30 minutes
-- So any values ranging from 1:00 to 1:59:59 with a time zone of MDT are the first instance values.
SELECT TO_TIMESTAMP_TZ('2021-11-07 03:00:00 MST', 'yyyy-mm-dd
hh24:mi:ss TZD')
- TO_TIMESTAMP_TZ('2021-11-07 01:30:00 MST', 'yyyy-mm-dd hh24:mi:ss TZD') FROM DUAL;
-- 1 hour:30 minutes
-- So any values ranging from 1:00 to 1:59:59 with a time zone of MST are the second instance values.

Value TIMESTAMP '2021-11-07 01:00:00 US/Mountain' is ambiguous, it could be
2021-11-07 01:00:00-06:00 or 2021-11-07 01:00:00-07:00
If you don't specify the daylight-saving-time status then Oracle defaults to the standard time, (MST in your case)
You need to provide both, the timezone region and the Daylight Saving Time information, i.e. TO_TIMESTAMP_TZ('2021-11-07 01:00:00 US/Mountain MST', 'yyyy-mm-dd hh24:mi:ss TZR TZD') or TO_TIMESTAMP_TZ('2021-11-07 01:00:00 US/Mountain MDT', 'yyyy-mm-dd hh24:mi:ss TZR TZD')
Note, if you alter your session with ALTER SESSION SET ERROR_ON_OVERLAP_TIME = TRUE; then for ambiguous times like TIMESTAMP '2021-11-07 01:00:00 US/Mountain' Oracle does not default to standard time but raise an error:
ORA-01883: overlap was disabled during a region transition
Don't mistake "Time zone region" (TRZ) with "Daylight saving information" (TZD), however MST can mean both:
SELECT *
FROM V$TIMEZONE_NAMES
WHERE TZABBREV in ('MST', 'MDT');
Calculations of TIMESTAMP WITH TIME ZONE values are internally always performed on UTC times.
See Support for Daylight Saving Time

Related

Convert date oracle's timestamp to ISO-8601 date datatype [duplicate]

This question already has answers here:
Timezone date format in Oracle
(2 answers)
Closed 1 year ago.
I am looking for a query in Oracle 12c to convert a 18-12-2003 13:15:00 to 2003-12-18T13:15:00+01:00 in European time zone as datetime datatype.
Is that possible or am I missing something?
First you need to convert the string (I assume your input data is a string, rather than proper DATE or TIMESTAMP value), then you can attach the time zone.
There are several time zones in Europe, you must be more specific.
FROM_TZ(TO_TIMESTAMP('18-12-2003 13:15:00', 'DD-MM-YYYY HH24:MI:SS'), 'Europe/...')
Once you did that, you can output the result in arbitrary format:
TO_CHAR(
FROM_TZ(TO_TIMESTAMP('18-12-2003 13:15:00', 'DD-MM-YYYY HH24:MI:SS'), 'Europe/...'),
'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM'
)
If you want to convert all dates in the column to that format for the time zone CET and you are sure that the offset always is 1, then you could do this:
SELECT TO_CHAR(FROM_TZ(TIMESTAMP '2000-03-28 08:00:00', '1:00'),'YYYY"-"MM"-"DD"T"HH24":"MI":"SSTZR')
FROM DUAL;
However, the question is what to do with daylight savings time. Is the offset going to change when DST goes into effect ? There are a lot of considerations there - this question (credits to #Wernfried Domscheit) has a nice overview.
For example if you data is in UTC time zone and you want to display it in CET, then you could convert it like this:
Note there is 2 hours offset in summer and 1 in winter.
WITH dates (season,dt) AS
(
SELECT 'summer', TO_DATE('01-AUG-2021','DD-MON-YYYY') FROM DUAL UNION ALL
SELECT 'winter', TO_DATE('01-JAN-2021','DD-MON-YYYY') FROM DUAL
)
SELECT dt,
season,
TO_CHAR(
FROM_TZ( CAST( dt AS TIMESTAMP ), 'UTC' )
AT TIME ZONE 'CET',
'YYYY-MM-DD HH24:MI:SS TZH:TZM TZR'
) AS cet_timezone
FROM dates;
01-AUG-2021 summer 2021-08-01 02:00:00 +02:00 CET
01-JAN-2021 winter 2021-01-01 01:00:00 +01:00 CET

Show UTC in timestamp Oracle SQL

In Oracle SQL, this:
SELECT to_timestamp('2021-08-11 16:25:54', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL;
Returns:
11-AUG-21 04.25.54.000000000 PM
However, my input timestamp also contains UTC: 2021-08-11 16:25:54 UTC
Desired result:
11-AUG-21 04.25.54.000000000 PM UTC
The docs specify to include Z... However this does not work:
--Invalid date format
SELECT to_timestamp('2021-08-11 16:25:54 UTC', 'YYYY-MM-DD HH24:MI:SS Z') FROM DUAL;
First of all, you need to_timestamp_tz() instead of to_timestamp, and secondly you need to specify correct format: TZR instead of Z:
-- TZR = time zone region
SELECT to_timestamp_tz('2021-08-11 16:25:54 UTC', 'YYYY-MM-DD HH24:MI:SS TZR') FROM DUAL;
You can use at time zone
SELECT to_timestamp('2021-08-11 16:25:54', 'YYYY-MM-DD HH24:MI:SS') at time zone 'UTC' dt
FROM DUAL;
EDIT
Correct version, thanks to #WernfriedDomscheit
SELECT FROM_TZ(to_timestamp('2021-08-11 16:25:54', 'YYYY-MM-DD HH24:MI:SS'), 'UTC') ts
FROM DUAL;

Oracle SQL sysdate with time

I am using Oracle SQL. I'm trying to use sysdate with time to give the me query result. I have placed a time in the where clause, but I need it to be automatic and using the sysdate and converting to local time is the correct way. Any thoughts?
SELECT RESOURCE, AVG(SALES) AS SALES
FROM Z_HOURLY_RESOURCE
WHERE DATE_TIME_START BETWEEN to_date(to_char(FROM_TZ( CAST( (to_date('2018-08-02T05:00:00','yyyy-MM-dd"T"HH24:mi:ss') )AS TIMESTAMP ), 'America/Los_Angeles') AT TIME ZONE 'UTC', 'yyyy-MM-dd"T"HH24:mi:ss'),'yyyy-MM-dd"T"HH24:mi:ss')
AND to_date(to_char(FROM_TZ( CAST( (to_date('2018-08-02T13:00:00','yyyy-MM-dd"T"HH24:mi:ss')+1) AS TIMESTAMP ), 'America/Los_Angeles') AT TIME ZONE 'UTC', 'yyyy-MM-dd"T"HH24:mi:ss'),'yyyy-MM-dd"T"HH24:mi:ss')
It's slightly hard to work out quite what you need without sample data, but it sounds like you want to convert the time range 05:00 to 13:00 in your local session time zone (e.g. LA) to UTC to compare with the UTC-based timestamps in your table.
You can do that with:
WHERE DATE_TIME_START >= sys_extract_utc(cast(trunc(current_date) + 5/24 as timestamp with time zone))
AND DATE_TIME_START < sys_extract_utc(cast(trunc(current_date) + 13/24 as timestamp with time zone))
I've used >= and < rather than between on the assumption you really want up to 13:00, which is usually the case for time ranges. If you do want to include data form exactly 13:00:00 then change that < to <=, or go back to between.
TO explain what that is doing a but: current_date gives you the date/time in your session time zone. Truncating that sets the time to midnight (by default), so you can then add either 5 or 13 hours to get the times you want. That is still a date, so you can cast to timestamp with time zone so it represents that time in your session time zone again. You can then use sys_extract_utc() to get the UTC-equivalent timestamp.
To demonstrate those steps:
alter session set time_zone = 'America/Los_Angeles';
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF1';
alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS.FF1 TZH:TZM';
select sysdate as a,
current_date as b,
trunc(current_date) as c,
trunc(current_date) + 5/24 as d,
cast(trunc(current_date) + 5/24 as timestamp with time zone) as e,
sys_extract_utc(cast(trunc(current_date) + 5/24 as timestamp with time zone)) as f
from dual
union all
select sysdate as a,
current_date as b,
trunc(current_date) as c,
trunc(current_date) + 13/24 as d,
cast(trunc(current_date) + 13/24 as timestamp with time zone) as e,
sys_extract_utc(cast(trunc(current_date) + 13/24 as timestamp with time zone)) as f
from dual;
A B C D E F
------------------- ------------------- ------------------- ------------------- ---------------------------- ---------------------
2018-08-02 18:56:23 2018-08-02 10:56:23 2018-08-02 00:00:00 2018-08-02 05:00:00 2018-08-02 05:00:00.0 -07:00 2018-08-02 12:00:00.0
2018-08-02 18:56:23 2018-08-02 10:56:23 2018-08-02 00:00:00 2018-08-02 13:00:00 2018-08-02 13:00:00.0 -07:00 2018-08-02 20:00:00.0
First of all you don't have to cast a TIMESTAMP to a CHAR and then back again to a TIMESTAMP.
Assuming DATE_TIME_START is a TIMESTAMP and times are given in UTC you can make it simpler. When Oracle compares TIMESTAMP WITH TIME ZONE values then comparison are always done automatically at UTC time value. Your condition would be like this.
SELECT RESOURCE, AVG(SALES) AS SALES
FROM Z_HOURLY_RESOURCE
WHERE FROM_TZ(DATE_TIME_START, 'UTC')
BETWEEN TO_TIMESTAMP_TZ('2018-08-02T05:00:00 America/Los_Angeles', 'yyyy-MM-dd"T"HH24:mi:ss TZR')
AND TO_TIMESTAMP_TZ('2018-08-02T13:00:00 America/Los_Angeles', 'yyyy-MM-dd"T"HH24:mi:ss TZR')
However, due to function FROM_TZ(DATE_TIME_START, 'UTC') the performance might not be the best, it depends on your data.
If you need condition based on current time it would be like this:
SELECT RESOURCE, AVG(SALES) AS SALES
FROM Z_HOURLY_RESOURCE
WHERE FROM_TZ(DATE_TIME_START, 'UTC')
BETWEEN TRUNC(SYSTIMESTAMP)
AND TRUNC(SYSTIMESTAMP) + INTERVAL '1' DAY
Above query just illustrate time zone handling. You don't have to consider time zone of SYSTIMESTAMP, comparison will work in any case.

Different results for TO_TIMESTAMP_TZ and CAST AS TIMESTAMP WITH LOCAL TIME ZONE in Oracle DB

I have an Oracle 11g database and I'm trying to experiment with how DST is handled with TIMESTAMP datatypes (especially ones created from incoming data that doesn't have a time zone included). I'm seeing a difference in behavior when using TO_TIMESTAMP_TZ() vs CAST(DATE as TIMESTAMP WITH LOCAL TIME ZONE) that I can't explain. I would expect that the resulting datatype is a zoned timestamp regardless of how it is created and any operation on it would be identical but it does not appear that this is the case.
Any idea why when using the CAST option the "extra" hour created by the Fall DST change isn't being taken into account in the INTERVAL math? I would expect both rows of the query below to be identical.
Query:
select to_timestamp_tz('11/1/2015 1:00 AM US/Pacific', 'MM/DD/YYYY HH:mi AM TZR') + interval '2' hour as DST_PLUS_2, -- this is a post DST jump time
to_timestamp_tz('11/1/2015 12:59 AM US/Pacific', 'MM/DD/YYYY HH:mi AM TZR') + interval '2' hour as PRE_DST_PLUS_2, -- this is a pre DST jump time
to_char(to_timestamp_tz('11/1/2015 1:00 AM US/Pacific', 'MM/DD/YYYY HH:mi AM TZR'), 'TZR') as TZR
from dual
union all
select cast(to_date('11/1/2015 1:00 AM', 'MM/DD/YYYY HH:mi AM') as timestamp with local time zone) + interval '2' hour as DST_PLUS_2,
cast(to_date('11/1/2015 12:59 AM', 'MM/DD/YYYY HH:mi AM') as timestamp with local time zone) + interval '2' hour as PRE_DST_PLUS_2,
to_char(cast(to_date('11/1/2015 1:00 AM', 'MM/DD/YYYY HH:mi AM') as timestamp with local time zone), 'TZR') as TZR
from dual;
Results:
DST_PLUS_2 PRE_DST_PLUS_2 TZR
01-NOV-15 03.00.00.000000000 AM US/PACIFIC 01-NOV-15 01.59.00.000000000 AM US/PACIFIC US/PACIFIC
01-NOV-15 03.00.00.000000000 AM US/PACIFIC 01-NOV-15 02.59.00.000000000 AM US/PACIFIC US/PACIFIC
It looks like this comes down to the fact that TO_TIMESTAMP_TZ attempts to infer the TZD element of the TIMESTAMP from the date while CAST AS TIMESTAMP WITH LOCAL TIME ZONE will take the current TZD of the database regardless of when the date is. Or they just have different default behaviors for picking which of the 1AM times (pre or post time change) to choose. This difference in behavior is causing the resulting hour to be different by one.
Thanks ruudvan!
Query:
select to_char(to_timestamp_tz('11/1/2015 1:00 AM US/Pacific', 'MM/DD/YYYY HH:mi AM TZR'), 'TZD') as TZD_TO_TS_TZ_1,
to_char(cast(to_date('11/1/2015 1:00 AM', 'MM/DD/YYYY HH:mi AM') as timestamp with local time zone), 'TZD') as TZD_CAST_1,
to_char(to_timestamp_tz('11/1/2015 12:00 AM US/Pacific', 'MM/DD/YYYY HH:mi AM TZR'), 'TZD') as TZD_TO_TS_TZ_12,
to_char(cast(to_date('11/1/2015 12:00 AM', 'MM/DD/YYYY HH:mi AM') as timestamp with local time zone), 'TZD') as TZD_CAST_12
from dual;
Results:
TZD_TO_TS_TZ_1 TZD_CAST_1 TZD_TO_TS_TZ_12 TZD_CAST_12
PST PDT PDT PDT
Data type TIMESTAMP WITH TIME ZONE stores the time zone information. When you insert 11/1/2015 1:00 AM US/Pacific then that is what is stored and that is what you get when you select it.
Data type TIMESTAMP WITH TIME LOCAL ZONE also stores time zone information. However, the value is always shown in current user session time zone SESSIONTIMEZONE. In principle Oracle put the value always in ({TIMESTAMP WITH TIME ZONE} value) AT TIME ZONE SESSIONTIMEZONE
DATE does not store any time zone information. If you run CAST({DATE value} as timestamp with local time zone) then Oracle runs basically FROM_TZ({DATE value}, SESSIONTIMEZONE) as timestamp with local time zone). I.e. it attaches the current user session time zone (which could be different to US/Pacific) to DATE value and then it converts the time zone, if required.
So the result of your query really depends on your current session timezone SESSIONTIMEZONE. Note, this might be US/Pacific or +07:00 / +08:00. If it is set to US/Pacific then daylight saving times are taken into account. For UTC offsets like +07:00 or +08:00 the time zones are fixes, i.e. daylight saving times are not considered.

Convert sysdate into CET and CEST time with the help of UTC package functions in oracle

I have a requirement where sysdate appears like this 19/01/2015 13:22:05
and i want to convert the the sysdate into CET time which will be 19/01/2015 14:22:05
and again i want to convert the sysdate into CEST which will be 19/01/2015 15:22:05
select to_char(sysdate , 'DD/MM/YYYY HH24:MI:SS') actual ,
to_char(utc.get_local_date(sysdate ,'LOCATION') ,'DD/MM/YYYY HH24:MI:SS') cet,
to_char(utc.get_local_date(sysdate ,'LOCATION')+1/24 ,'DD/MM/YYYY HH24:MI:SS') cest
FROM dual
and here location is using Europe/Amsterdam timezone.
Please suggest is there anyway we can achieve CET and CEST timing without hardcoding +1/24 in the sql and by using UTC package functions.
Please correct me if i am wrong ..
I never heard anything from utc.get_local_date, maybe you mixed it with MySQL.
On Oracle you can do it like this:
SELECT
TO_CHAR(SYSTIMESTAMP , 'DD/MM/YYYY HH24:MI:SS') actual,
TO_CHAR(SYSTIMESTAMP AT TIME ZONE 'CET', 'DD/MM/YYYY HH24:MI:SS') AS cet,
TO_CHAR(SYSTIMESTAMP AT TIME ZONE 'CET' + INTERVAL '1' HOUR, 'DD/MM/YYYY HH24:MI:SS') AS cest
FROM dual;
Problem is CET does not cover any Daylight-Saving. In order to cover DST automatically you must use the long timezone name, e.g.
SELECT
TO_CHAR(SYSTIMESTAMP AT TIME ZONE 'Europe/Berlin', 'DD/MM/YYYY HH24:MI:SS') AS cet
FROM dual;
Oracle does not provide any function like "AT SUMMER TIME" for timestamps.
Either you use fixed time zones like CET or +01:00 or +02:00 or you use the time zone region, e.g. Europe/Amsterdam.
When you use time zone regions the Daylight-Saving settings are handled automatically, i.e. the output depends on the input day.
It is a bit tricky to say "Display 1st of January as it would be in Summer Time"
For example SELECT TZ_OFFSET('Europe/Amsterdam') FROM dual; will always return +01:00, no matter if you run this query in summer or winter times.