В Oracle, как я могу определить дату начала / окончания летнего времени? - PullRequest
5 голосов
/ 13 ноября 2008

Есть ли в 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

Редактировать: Я надеялся на решение, которое не потребовало бы изменений, когда Конгресс корректирует законы о летнем времени, как они это делали в 2007 году. Однако опубликованные решения будут работать.

Ответы [ 6 ]

5 голосов
/ 12 декабря 2014

Чтобы улучшить ответ Ли Риффеля, это намного проще с той же логикой:

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;
3 голосов
/ 13 ноября 2008

Мы используем следующие две функции для вычисления даты начала и окончания для любого данного года (после 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;

Вероятно, есть более простой способ сделать это, но они сработали для нас. Конечно, этот запрос не знает, наблюдается ли переход на летнее время для того места, где вы находитесь. Для этого вам понадобятся данные о местоположении .

2 голосов
/ 31 мая 2010

Вместо зацикливания для получения следующего воскресенья вы также можете использовать функцию оракула next_day (date, 'SUN').

1 голос
/ 14 ноября 2008

Вот способ использования внутреннего знания 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;  

Я же говорил, это было странно. Код только вычисляет день изменения, но может быть улучшен, чтобы показать час. В настоящее время возвращается 09-Март-08 и 02-Ноябрь-08. Он также чувствителен к времени года, когда он запускается, поэтому мне пришлось сделать -365 ... + 365. В общем, я не рекомендую это решение, но было интересно исследовать. Может быть, у кого-то есть что-то лучше.

1 голос
/ 13 ноября 2008

В Соединенных Штатах летнее время определяется как начинающееся во второе воскресенье марта и заканчивающееся в первое воскресенье ноября для районов, где наблюдается летнее время, в течение лет после 2007 года.

Я не думаю, что есть простой способ получить эту информацию из Oracle, но на основе стандартного определения вы сможете написать хранимую процедуру, которая вычисляет дату начала и окончания, используя Алгоритм Судного дня .

0 голосов
/ 24 мая 2012

Вот моя версия выше. Его преимущество заключается в том, что ему не требуется второй «часовой пояс для изменения набора сеансов», и его проще использовать в приложении. Вы создаете сохраненную функцию, а затем просто используете: ALTER SESSION SET time_zone = 'Азия / Иерусалим'; выберите GetDSTDates (2012,1) DSTStart, GetDSTDates (2012,2) DSTEnd, SessionTimeZone TZ из двойного;

, которая будет возвращать дату начала 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;
...