2008-11-13 33 views
5

Có cách nào trong Oracle để chọn ngày tiết kiệm ánh sáng ban ngày sẽ chuyển sang miền địa phương của tôi không?Trong Oracle, làm thế nào tôi có thể phát hiện ngày mà thời gian tiết kiệm ánh sáng ban ngày bắt đầu/kết thúc?

Something mơ hồ tương đương với điều này sẽ được tốt đẹp:

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 

Edit: Tôi đã hy vọng cho một giải pháp mà sẽ không yêu cầu thay đổi khi Quốc hội điều chỉnh luật DST, như họ đã làm trong năm 2007. Các giải pháp được đăng sẽ công việc, mặc dù.

Trả lời

3

Chúng tôi sử dụng hai hàm sau để tính ngày bắt đầu và ngày kết thúc cho bất kỳ năm nào (năm 2007, Hoa Kỳ).

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; 

Có thể là cách đơn giản hơn để làm điều đó, nhưng chúng đã làm việc cho chúng tôi. Tất nhiên truy vấn này không biết liệu thời gian tiết kiệm ánh sáng ban ngày có được quan sát cho bạn ở đâu không. Cho rằng bạn sẽ cần location data.

1

Tại Hoa Kỳ, Daylight Savings Time được định nghĩa là đầu vào ngày Chủ Nhật thứ hai trong tháng Ba, và kết thúc vào ngày chủ nhật đầu tiên trong tháng Mười Một, cho khu vực quan sát DST, trong nhiều năm sau 2007.

tôi không nghĩ rằng có một cách dễ dàng để lấy thông tin này từ Oracle, nhưng dựa trên định nghĩa chuẩn, bạn sẽ có thể viết một thủ tục lưu sẵn để tính toán ngày bắt đầu và kết thúc bằng cách sử dụng Doomsday Algorithm.

1

Dưới đây là cách sử dụng kiến ​​thức nội bộ của Oracles về việc múi giờ có quan sát thời gian tiết kiệm ánh sáng ban ngày hay không để xác định thời điểm bắt đầu và kết thúc của nó. Ngoài sự phức tạp và sự kỳ lạ chung của nó, nó đòi hỏi hai múi giờ để được biết có thời gian giống nhau khi thời gian tiết kiệm ánh sáng ban ngày không có hiệu lực và thời điểm khác nhau khi nó được. Vì vậy, nó có khả năng phục hồi các thay đổi của quốc hội khi thời gian tiết kiệm ánh sáng ban ngày xảy ra (giả sử cơ sở dữ liệu của bạn cập nhật với các bản vá), nhưng không linh hoạt với các thay đổi khu vực ảnh hưởng đến các múi giờ bị khóa. Với những cảnh báo đó, đây là những gì tôi có.

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; 

Tôi đã nói với bạn điều đó thật lạ. Mã chỉ tìm ra ngày thay đổi, nhưng có thể được tăng cường để hiển thị giờ. Hiện tại nó trả về 09-MAR-08 và 02-NOV-08. Nó cũng nhạy cảm với thời gian trong năm nó được chạy, đó là lý do tại sao tôi phải làm -365 ... + 365. Tất cả trong tất cả tôi không khuyên bạn nên giải pháp này, nhưng nó là thú vị để điều tra. Có thể ai đó khác có cái gì đó tốt hơn.

2

Thay vì lặp lại để có được chủ nhật tiếp theo, bạn cũng có thể sử dụng hàm next_day (ngày, 'SUN') của oracle.

0

Đây là phiên bản của tôi ở trên. Đó là lợi thế là nó không cần một 'thời gian thiết lập phiên làm thay đổi' thứ hai, và có thể được sử dụng dễ dàng hơn từ một ứng dụng. Bạn tạo ra chức năng được lưu trữ, và sau đó bạn chỉ cần sử dụng: ALTER CÀI ĐẶT SET time_zone = 'Asia/Jerusalem'; chọn GetDSTDates (2012,1) DSTStart, GetDSTDates (2012,2) DSTEnd, SessionTimeZone TZ từ kép;

sẽ trả về ngày bắt đầu dst, ngày kết thúc dst, múi giờ cho năm được chỉ định.

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

Để cải thiện về câu trả lời Leigh Riffel, đây là đơn giản hơn nhiều với cùng logic:

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; 
Các vấn đề liên quan