Domanda

Esiste un modo in Oracle per selezionare la data in cui l'ora legale passerà al mio paese?

Qualcosa di vagamente equivalente a questo sarebbe bello:

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

Modifica: speravo in una soluzione che non avrebbe richiesto cambiamenti quando il Congresso modifica le leggi dell'ora legale, come hanno fatto nel 2007. Le soluzioni pubblicate funzioneranno, comunque.

È stato utile?

Soluzione

Utilizziamo le seguenti due funzioni per calcolare le date di inizio e fine per un dato anno (post 2007, Stati Uniti).

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'è probabilmente un modo più semplice per farlo, ma questi hanno funzionato per noi. Naturalmente questa query non è in grado di sapere se si osserva l'ora legale per dove ti trovi. Per questo avrai bisogno di dati sulla posizione .

Altri suggerimenti

Per migliorare la risposta di Leigh Riffel, questo è molto più semplice con la stessa logica:

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;

Invece di eseguire il loop per ottenere la domenica successiva, puoi anche utilizzare la funzione oracolo next_day (date, 'SUN').

Negli Stati Uniti, l'ora legale è definita come inizia la seconda domenica di marzo e termina la prima domenica di novembre, per le aree che osservano l'ora legale, per anni dopo il 2007.

Non credo che ci sia un modo semplice per ottenere queste informazioni da Oracle, ma in base alla definizione standard, dovresti essere in grado di scrivere una procedura memorizzata che calcola la data di inizio e di fine usando Alomsith Doomsday .

Ecco un modo per usare la conoscenza interna di Oracoli sul fatto che un fuso orario osservi l'ora legale o meno per determinarne l'inizio e la fine. A parte la complessità e la stranezza generale di esso, richiede due fusi orari per sapere che hanno orari identici in cui l'ora legale non è in vigore e diverse volte quando lo è. In quanto tale, è resistente alle modifiche congressuali quando si verifica l'ora legale (supponendo che il database sia aggiornato con le patch), ma non è resistente alle modifiche regionali che incidono sui fusi orari. Con questi avvertimenti, ecco quello che ho.

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;  

Ti ho detto che era strano. Il codice determina solo il giorno della modifica, ma potrebbe essere migliorato per mostrare l'ora. Attualmente restituisce 09-MAR-08 e 02-NOV-08. È anche sensibile al periodo dell'anno in cui viene eseguito, motivo per cui ho dovuto fare il -365 ... + 365. Tutto sommato, non consiglio questa soluzione, ma è stato divertente indagare. Forse qualcun altro ha qualcosa di meglio.

Ecco la mia versione di cui sopra. Il vantaggio è che non necessita di un secondo "fuso orario di modifica della sessione" e può essere utilizzato più facilmente da un'applicazione. Si crea la funzione memorizzata e quindi si utilizza semplicemente: ALTER SESSION SET time_zone = 'Asia / Gerusalemme'; seleziona GetDSTDates (2012,1) DSTStart, GetDSTDates (2012,2) DSTEnd, SessionTimeZone TZ da dual;

che restituirà la prima data di inizio, la data di fine, il fuso orario per l'anno specificato.

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;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top