2008-11-13 59 views
5

¿Hay alguna manera en Oracle de seleccionar la fecha en la que cambiará el horario de verano de mi localidad?En Oracle, ¿cómo puedo detectar la fecha en que comienza o finaliza el horario de verano?

algo vagamente equivalente a esto sería agradable:

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 

Editar: Tenía la esperanza de una solución que no requiera cambios cuando el Congreso se ajusta DST leyes, como lo hicieron en 2007. Las soluciones estén publicados trabajo, sin embargo.

Respuesta

3

Utilizamos las dos funciones siguientes para calcular las fechas de inicio y finalización para un año determinado (después de 2007, EE. UU.).

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; 

Probablemente haya una manera más simple de hacerlo, pero nos han funcionado. Por supuesto, esta consulta no sabe si se observa el horario de verano para el lugar donde se encuentra. Para eso, necesitará location data.

1

En los Estados Unidos, el horario de verano se define como inicio en el segundo domingo de marzo y termina en el primer domingo de noviembre, para las áreas que observan el horario de verano, durante años después de 2007.

I No creo que haya una manera fácil de obtener esta información de Oracle, pero según la definición estándar, debería poder escribir un procedimiento almacenado que calcule la fecha de inicio y finalización usando el Doomsday Algorithm.

1

Aquí hay una manera de utilizar el conocimiento interno de Oracles de si una zona horaria observa el horario de verano o no para determinar el inicio y el final de la misma. Aparte de la complejidad y la extrañeza general de la misma, se requieren dos zonas horarias para saber que tienen tiempos idénticos cuando el horario de verano no está en vigencia y en diferentes momentos cuando lo es. Como tal, es resistente a los cambios del Congreso en el momento en que se produce el horario de verano (suponiendo que su base de datos esté actualizada con los parches), pero no es resistente a los cambios regionales que afectan a las zonas horarias desactivadas. Con esas advertencias, esto es lo que tengo.

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; 

Te dije que era extraño. El código solo determina el día del cambio, pero podría mejorarse para mostrar la hora. Actualmente devuelve 09-MAR-08 y 02-NOV-08. También es sensible a la época del año en que se ejecuta, por lo que tuve que hacer el -365 ... + 365. En general, no recomiendo esta solución, pero fue divertido de investigar. Tal vez alguien más tiene algo mejor.

2

En lugar de hacer bucles para obtener el próximo domingo, también puede usar la función next_day (date, 'SUN') de oráculo.

0

Aquí está mi versión de la anterior. Su ventaja es que no necesita un segundo 'cambio de zona horaria configurada', y puede usarse más fácilmente desde una aplicación. Crea la función almacenada y luego simplemente usa: ALTER SESSION SET time_zone = 'Asia/Jerusalem'; seleccione GetDSTDates (2012,1) DSTStart, GetDSTDates (2012,2) DSTEnd, SessionTimeZone TZ desde dual;

que devolverá la fecha de inicio del dst, la fecha de finalización del dst, la zona horaria del año especificado.

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

para mejorar la respuesta de Leigh Riffel, esto es mucho más sencillo con la misma lógica:

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; 
Cuestiones relacionadas