5

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 年所做的那样。不过,发布的解决方案将起作用。

4

7 回答 7

5

为了改进 Leigh 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;
于 2014-12-12T19:50:15.847 回答
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;

可能有一种更简单的方法可以做到这一点,但这些方法对我们有用。当然,此查询不知道您所在的位置是否遵守夏令时。为此,您将需要位置数据

于 2008-11-13T18:51:10.027 回答
2

除了循环获取下一个星期日,您还可以使用 oracle 的 next_day(date, 'SUN') 函数。

于 2010-05-31T08:55:29.673 回答
1

在美国,夏令时定义为从 3 月的第二个星期日开始,到 11 月的第一个星期日结束,对于遵守 DST 的地区,在 2007 年之后的几年中。

我不认为有一种简单的方法可以从 Oracle 获取这些信息,但是根据标准定义,您应该能够编写一个使用Doomsday Algorithm计算开始和结束日期的存储过程。

于 2008-11-13T18:20:08.183 回答
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;  

我告诉过你这很奇怪。该代码仅计算更改的日期,但可以增强以显示小时。目前它返回 09-MAR-08 和 02-NOV-08。它对一年中运行的时间也很敏感,这就是为什么我必须使用 -365...+365。总而言之,我不推荐这种解决方案,但调查起来很有趣。也许别人有更好的东西。

于 2008-11-14T04:05:22.403 回答
0

这是我上面的版本。它的优点是它不需要第二个“更改会话设置时区”,并且可以更轻松地从应用程序中使用。您创建存储函数,然后您只需使用: ALTER SESSION SET time_zone='Asia/Jerusalem'; 选择 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;
于 2012-05-24T19:18:34.957 回答
0

老问题,但这里有一个新答案。使用 08-MAR 作为第一个日期,因为它跳过了第一周

--Start of DST
select next_day(to_date('08-MAR-' || to_char(sysdate, 'YYYY')), 'SUN') from dual

--End of DST
select next_day(to_date('01-NOV-' || to_char(sysdate, 'YYYY')), 'SUN') from dual
于 2021-03-16T17:02:16.743 回答