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年所做的那样。但是,已发布的解决方案可行。
答案 0 :(得分: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;
答案 1 :(得分: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;
可能有一种更简单的方法,但这些对我们有用。当然,此查询不知道是否在您所在的位置观察到夏令时。为此,您需要location data。
答案 2 :(得分:2)
您可以使用oracle的next_day(日期,'SUN')函数,而不是循环来获取下一个星期日。
答案 3 :(得分:1)
在美国,夏令时被定义为从3月的第二个星期日开始,到11月的第一个星期日结束,对于观察夏令时的地区,在2007年之后的几年。
我认为从Oracle获取此信息的方法并不简单,但基于标准定义,您应该能够使用Doomsday Algorithm编写一个计算开始和结束日期的存储过程。
答案 4 :(得分:1)
这是一种使用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的原因。总而言之,我不推荐这种解决方案,但调查很有趣。也许别人有更好的东西。
答案 5 :(得分:0)
这是我上面的版本。它的优点是它不需要第二个“更改会话设置时区”,并且可以更轻松地从应用程序中使用。 您创建存储的函数,然后您只需使用: 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;
答案 6 :(得分: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