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年所做的那样。但是,已发布的解决方案可行。

有帮助吗?

解决方案

我们使用以下两个函数来计算任何给定年份的开始和结束日期(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;

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

其他提示

为了改进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;

您可以使用oracle的next_day(日期,'SUN')函数,而不是循环来获取下一个星期日。

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

我认为从Oracle获取此信息的方法并不简单,但基于标准定义,您应该能够使用世界末日算法

这是一种使用Oracles内部知识的方法,即时区是否遵守夏令时来确定时间的开始和结束。除了它的复杂性和一般的陌生性之外,它需要知道两个时区在夏令时没有生效时具有相同的时间,并且在时间不同的时候。因此,当夏令时发生时(假设您的数据库是最新的补丁),它对国会的变化具有弹性,但对于影响关键时区的区域变化不具有弹性。有了这些警告,这就是我所拥有的。

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的原因。总而言之,我不推荐这种解决方案,但调查很有趣。也许别人有更好的东西。

这是我上面的版本。它的优点是它不需要第二个“更改会话设置时区”,并且可以更轻松地从应用程序中使用。 您创建存储的函数,然后您只需使用: ALTER SESSION SET time_zone ='Asia / Jerusalem'; 从双重选择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;
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top