2008-11-13 82 views
5

在Oracle中有沒有辦法選擇夏令時切換到我的區域的日期?在Oracle中,如何檢測夏時制開始/結束的日期?

東西隱約相當於這將是很好:

SELECT CHANGEOVER_DATE 
FROM SOME_SYSTEM_TABLE 
WHERE DATE_TYPE = 'DAYLIGHT_SAVINGS_CHANGEOVER' 
    AND TO_CHAR(CHANGEOVER_DATE,'YYYY') = TO_CHAR(SYSDATE,'YYYY'); -- in the current year 

編輯:我所期待的,不會要求改變國會調整DST的法律解決方案,因爲他們在2007年發佈的解決方案確實會工作,但是。

回答

3

我們使用以下兩個函數來計算任何給定年份(2007年後,美國)的開始日期和結束日期。

Function DaylightSavingTimeStart (p_Date IN Date) 
Return Date Is 
    v_Date  Date; 
    v_LoopIndex Integer; 
Begin 
    --Set the date to the 8th day of March which will effectively skip the first Sunday. 
    v_Date := to_date('03/08/' || to_char(p_Date,'YYYY') || '02:00:00 AM','MM/DD/YYYY HH:MI:SS PM'); 
    --Advance to the second Sunday. 
    FOR v_LoopIndex IN 0..6 LOOP 
     If (RTRIM(to_char(v_Date + v_LoopIndex,'DAY')) = 'SUNDAY') Then 
     Return v_Date + v_LoopIndex; 
     End If; 
    END LOOP; 
End; 

Function DaylightSavingTimeEnd (p_Date IN Date) 
Return Date Is 
    v_Date  Date; 
    v_LoopIndex Integer; 
Begin 
    --Set Date to the first of November this year 
    v_Date := to_date('11/01/' || to_char(p_Date,'YYYY') || '02:00:00 AM','MM/DD/YYYY HH:MI:SS PM'); 
    --Advance to the first Sunday 
    FOR v_LoopIndex IN 0..6 LOOP 
     If (RTRIM(to_char(v_Date + v_LoopIndex,'DAY')) = 'SUNDAY') Then 
     Return v_Date + v_LoopIndex; 
     End If; 
    END LOOP; 
End; 

有可能是一個更簡單的方法來做到這一點,但這些都爲我們工作。當然,這個查詢並不知道是否遵守夏令時。爲此,您將需要location data

1

在美國,夏令時2007年後

我定義爲在三月第二個星期日開始,並在十一月的第一個星期日結束,對於觀察DST的地區,年不要以爲從Oracle獲取這些信息是一種簡單的方法,但是基於標準定義,您應該能夠編寫一個存儲過程,使用Doomsday Algorithm來計算開始和結束日期。

1

這是一種使用ORACLE內部知識來了解時區是否遵守夏令時和是否確定其開始和結束時間的方法。除了它的複雜性和一般的陌生性外,它還要求兩個時區在夏令時無效時和夏時制不同時有相同的時間。因此,當夏令時發生時(假定您的數據庫與修補程序保持同步),它對國會的變化具有彈性,但對影響時區關閉的區域變更不具有彈性。有了這些警告,這就是我所擁有的。

ALTER SESSION SET time_zone='America/Phoenix'; 
DROP TABLE TimeDifferences; 
CREATE TABLE TimeDifferences(LocalTimeZone TIMESTAMP(0) WITH LOCAL TIME ZONE); 
INSERT INTO TimeDifferences 
(
    SELECT to_date('01/01/' || to_char(sysdate-365,'YYYY') || '12:00:00','MM/DD/YYYYHH24:MI:SS')+rownum-1 
    FROM dual CONNECT BY rownum<=365 
); 
COMMIT; 

ALTER SESSION SET time_zone='America/Edmonton'; 
SELECT LocalTimeZone-1 DaylightSavingTimeStartAndEnd 
FROM 
(
    SELECT LocalTimeZone, 
     to_char(LocalTimeZone,'HH24') Hour1, 
     LEAD(to_char(LocalTimeZone,'HH24')) OVER (ORDER BY LocalTimeZone) Hour2 
    FROM TimeDifferences 
) 
WHERE Hour1 <> Hour2; 

我告訴過你這很奇怪。代碼只能計算出變更的當天,但可以增強以顯示小時。目前它返回08年3月9日和08年2月2日。它對運行的一年中的時間也很敏感,這就是爲什麼我必須做-365 ... + 365。總而言之,我不推薦這個解決方案,但調查很有趣。也許別人有更好的東西。

2

代替循環獲取下一個星期日,您還可以使用oracle的next_day(date,'SUN')函數。

0

這是我的以上版本。它的優點是不需要第二個「更改會話設置時區」,並且可以更容易地從應用程序中使用。 您創建存儲的功能,然後您只需使用: ALTER SESSION SET time_zone ='Asia/Jerusalem'; select GetDSTDates(2012,1)DSTStart,GetDSTDates(2012,2)DSTEnd,SessionTimeZone TZ from dual;

這將返回指定年份的dst開始日期,dst結束日期,時區。

create or replace function GetDSTDates 
(
    year integer, 
    GetFrom integer 
) 
return Date 
as 
    cursor c is 
    select 12-to_number(to_char(LocalTimeZone at time zone '+00:00','HH24')) offset, 
    min(to_char(LocalTimeZone at time zone '+00:00','DD/MM/YYYY')) fromdate, 
    max(to_char(LocalTimeZone at time zone '+00:00','DD/MM/YYYY')) todate 
     from (
     SELECT cast((to_date('01/01/'||to_char(year)||'12:00:00','MM/DD/YYYYHH24:MI:SS')+rownum-1) as timestamp with local time zone) LocalTimeZone 
     FROM dual CONNECT BY rownum<=365 
     ) 
    group by 12-to_number(to_char(LocalTimeZone at time zone '+00:00','HH24')); 
    dstoffset integer; 
    offset integer; 
    dstfrom date; 
    dstto date; 
begin 
    offset := 999; 
    dstoffset := -999; 
    for rec in c 
    loop 
    if rec.offset<offset 
    then 
     offset := rec.offset; 
    end if; 
    if rec.offset>dstoffset 
    then 
     dstoffset := rec.offset; 
     dstfrom := to_date(rec.fromdate,'DD/MM/YYYY'); 
     dstto :=to_date(rec.todate,'DD/MM/YYYY'); 
    end if; 
    end loop; 
    if (offset<999 and dstoffset>-999 and offset<>dstoffset) 
    then 
    if GetFrom=1 
    then 
     return dstfrom; 
    else 
     return dstto; 
    end if; 
    else 
    return null; 
    end if; 
end; 
/
ALTER SESSION SET time_zone='Asia/Jerusalem'; 
select GetDSTDates(2012,1) DSTStart, 
     GetDSTDates(2012,2) DSTEnd, 
     SessionTimeZone TZ from dual; 
4

爲了提高利Riffel的答案,這是具有相同的邏輯更加簡單:

Function DaylightSavingTimeStart (p_Date IN Date) 
Return Date Is 
Begin 
    Return NEXT_DAY(TO_DATE(to_char(p_Date,'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') + 7; 
End; 

Function DaylightSavingTimeEnd (p_Date IN Date) 
Return Date Is 
Begin 
    Return NEXT_DAY(TO_DATE(to_char(p_Date,'YYYY') || '/11/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN'); 
End;