我需要以某种方式确定某个TDateTime
值是否在我的时区的夏令时范围内(在C#中与DateTime.IsDaylightSavingTime()
方法相同)。
我知道在Delphi中没有类似的功能,因为Delphi TDateTime
不包含有关时区的信息,但我想有一些方法可以使用Win32 API来实现这一点。
我查看了Win32 API GetTimeZoneInformation
和GetTimeZoneInformationForYear
函数,但我不太明白如何使用它们,所以我想请求您的帮助。提前感谢任何提示。
修改
示例:
在我的时区(中欧)夏令时开始于今年 3月28日凌晨2点,结束于2010年10月31日凌晨3点。
我需要一个带标题的函数:
function IsDaylightSavingTime(input: TDateTime): boolean;
如果输入日期介于2010年3月28日2:00至2010年10月31日3:00和true
之间,则会返回false
。
(这个例子仅适用于2010年,但我需要它可以工作多年。)
再一次,我知道单独保存在TDateTime中的信息是不够的,但我认为通过一些Win32 API函数,我应该能够获得有关Windows设置中当前时区的信息。
答案 0 :(得分:3)
这并不像听起来那么容易,因为:
1)DST与标准时间之间的转换日期对于所有国家而言都不相同
2)DST和标准时间之间的转换日期与所有年份的同一国家的算法不同(例如,在中欧,它是4月的第一个星期日,IIRC,现在是3月的最后一个星期天)。美国从4月的第一个星期日变为2007年3月的第二个星期日。
所以 - 一个简单的日期是不够的,你还需要一个地理位置。
但是,如果您能够接受这样的事实,那么您将自己限制在可以根据 CURRENT 算法计算的 CURRENT 年份的切换日期 CURRENT 区域设置(国家/地区),并且这对于将来和过去的日期可能都是错误的,那么您可以使用TIME_ZONE_INFORMATION中的信息来计算切换日期:
USES Windows,SysUtils,DateUtils;
FUNCTION GetDaylightSavingsSwitchOverDates(Year : Cardinal ; VAR Start,Stop : TDateTime) : BOOLEAN;
VAR
TZ : TTimeZoneInformation;
FUNCTION DecodeSwitchOverDate(Year : Cardinal ; CONST Time : TSystemTime) : TDateTime;
VAR
I : Cardinal;
BEGIN
Result:=EncodeDateTime(Year,Time.wMonth,1,Time.wHour,Time.wMinute,Time.wSecond,0);
IF Time.wDay=5 THEN BEGIN
Result:=DateOf(EndOfTheMonth(Result))+TimeOf(Result);
WHILE PRED(DayOfWeek(Result))<>Time.wDayOfWeek DO
Result:=IncDay(Result,-1)
END
ELSE BEGIN
WHILE PRED(DayOfWeek(Result))<>Time.wDayOfWeek DO Result:=IncDay(Result);
FOR I:=1 TO PRED(Time.wDay) DO Result:=IncWeek(Result)
END
END;
BEGIN
IF GetTimeZoneInformation(TZ)=TIME_ZONE_ID_UNKNOWN THEN
Result:=FALSE
ELSE BEGIN
Start:=DecodeSwitchOverDate(Year,TZ.DaylightDate);
Stop:=DecodeSwitchOverDate(Year,TZ.StandardDate);
Result:=TRUE
END
END;
FUNCTION StartOfDST(Year : Cardinal) : TDateTime;
VAR
Stop : TDateTime;
BEGIN
IF NOT GetDaylightSavingsSwitchOverDates(Year,Result,Stop) THEN Result:=0
END;
FUNCTION EndOfDST(Year : Cardinal) : TDateTime;
VAR
Start : TDateTime;
BEGIN
IF NOT GetDaylightSavingsSwitchOverDates(Year,Start,Result) THEN Result:=0
END;
在我的电脑(中欧时区)上循环使用2000年至2020年,我得到以下日期:
DST in 2000: Sun 26 Mar 2000 02:00:00 through Sun 29 Oct 2000 03:00:00
DST in 2001: Sun 25 Mar 2001 02:00:00 through Sun 28 Oct 2001 03:00:00
DST in 2002: Sun 31 Mar 2002 02:00:00 through Sun 27 Oct 2002 03:00:00
DST in 2003: Sun 30 Mar 2003 02:00:00 through Sun 26 Oct 2003 03:00:00
DST in 2004: Sun 28 Mar 2004 02:00:00 through Sun 31 Oct 2004 03:00:00
DST in 2005: Sun 27 Mar 2005 02:00:00 through Sun 30 Oct 2005 03:00:00
DST in 2006: Sun 26 Mar 2006 02:00:00 through Sun 29 Oct 2006 03:00:00
DST in 2007: Sun 25 Mar 2007 02:00:00 through Sun 28 Oct 2007 03:00:00
DST in 2008: Sun 30 Mar 2008 02:00:00 through Sun 26 Oct 2008 03:00:00
DST in 2009: Sun 29 Mar 2009 02:00:00 through Sun 25 Oct 2009 03:00:00
DST in 2010: Sun 28 Mar 2010 02:00:00 through Sun 31 Oct 2010 03:00:00
DST in 2011: Sun 27 Mar 2011 02:00:00 through Sun 30 Oct 2011 03:00:00
DST in 2012: Sun 25 Mar 2012 02:00:00 through Sun 28 Oct 2012 03:00:00
DST in 2013: Sun 31 Mar 2013 02:00:00 through Sun 27 Oct 2013 03:00:00
DST in 2014: Sun 30 Mar 2014 02:00:00 through Sun 26 Oct 2014 03:00:00
DST in 2015: Sun 29 Mar 2015 02:00:00 through Sun 25 Oct 2015 03:00:00
DST in 2016: Sun 27 Mar 2016 02:00:00 through Sun 30 Oct 2016 03:00:00
DST in 2017: Sun 26 Mar 2017 02:00:00 through Sun 29 Oct 2017 03:00:00
DST in 2018: Sun 25 Mar 2018 02:00:00 through Sun 28 Oct 2018 03:00:00
DST in 2019: Sun 31 Mar 2019 02:00:00 through Sun 27 Oct 2019 03:00:00
DST in 2020: Sun 29 Mar 2020 02:00:00 through Sun 25 Oct 2020 03:00:00
但由于算法在列出的年份中已从我的区域设置更改,因此这些年中至少有一些是不正确的。
您的功能将是:
FUNCTION IsDaylightSavingTime(Input : TDateTime) : BOOLEAN;
VAR
Start,Stop : TDateTime;
BEGIN
Result:=GetDaylightSavingsSwitchOverDates(YearOf(Input),Start,Stop) AND (Input>=Start) AND (Input<Stop)
END;
答案 1 :(得分:3)
对于您的特定应用程序而言可能过度,但开源项目“Olson Time Zone Database for Delphi”允许访问tz database项目支持的所有时区。 tz数据库定期更新,包含最新的夏令时更改或修复。
TZDB可以在Delphi 6,7上编译, 8,9,10,2007,2009,2010和XE或 FreePascal 2.0及更高版本。 TZDB是 最好与Delphi XE一起使用 将TTimeZone类引入RTL。
答案 2 :(得分:1)
Ondra C. -
是的,你是对的。你需要:
将Delphi TDateTime变量设置为您希望的日期/时间
将其转换为Windows SystemTime
调用GetTimeZoneInformation()获取TTimeZoneInformation
使用您的TTimeZoneInformation结构调用GetTimeZoneInformationForYear()来获取您的时区的DST信息(我不知道您在某个任意时区获得TTimeZoneInformation的位置 - 但您应该能够在MSDN上找到它)。
执行算术以查看您的系统时间是否发生在TTZI.StandardDate之后(在这种情况下是标准时间),或者是在TTZI.DaylightDate之后(在这种情况下是DST)。
或者......
也许您可以将其转换为Delphi表:
http://www.twinsun.com/tz/tz-link.htm
对于任何时区的任何日期时间,只需查看给定的日期时间是否在DST内或在其之外。瞧!没有Microsoft API - 只是一个简单的表查找或if / else大小写块!
'希望有帮助.. pSM
答案 3 :(得分:0)
正如您自己所说,这些信息不会与Delphi中的日期时间捆绑在一起,所以您不能简单地移植它。每个执行tdatetime的例程都必须添加此信息,这在Delphi中不是这样。
也许你应该更多地解释你真正想做的事情,并通过类比来描述问题
答案 4 :(得分:0)
我使用.net反射器来查看.net中此函数的实现。它定义如下,也许你可以把数学转换成Delphi?如果你需要深入研究,我建议你自己打开Reflector。我认为它会对你有帮助!
public static bool IsDaylightSavingTime(DateTime time, DaylightTime daylightTimes)
{
return (CalculateUtcOffset(time, daylightTimes) != TimeSpan.Zero);
}
internal static TimeSpan CalculateUtcOffset(DateTime time, DaylightTime daylightTimes)
{
if (daylightTimes != null)
{
DateTime time4;
DateTime time5;
if (time.Kind == DateTimeKind.Utc)
{
return TimeSpan.Zero;
}
DateTime time2 = daylightTimes.Start + daylightTimes.Delta;
DateTime end = daylightTimes.End;
if (daylightTimes.Delta.Ticks > 0L)
{
time4 = end - daylightTimes.Delta;
time5 = end;
}
else
{
time4 = time2;
time5 = time2 - daylightTimes.Delta;
}
bool flag = false;
if (time2 > end)
{
if ((time >= time2) || (time < end))
{
flag = true;
}
}
else if ((time >= time2) && (time < end))
{
flag = true;
}
if ((flag && (time >= time4)) && (time < time5))
{
flag = time.IsAmbiguousDaylightSavingTime();
}
if (flag)
{
return daylightTimes.Delta;
}
}
return TimeSpan.Zero;
}
答案 5 :(得分:0)
您可以在JclDateTime.pas单元的JEDI Code Library(开源)中找到LocalDateTimeToDateTime函数中的示例。检索夏令时信息并用于转换为UTC时间。
答案 6 :(得分:0)
我知道这不是您问题的答案,但您可能对以下两个功能感兴趣:SystemTimeToTzSpecificLocalTime()
和TzSpecificLocalTimeToSystemTime()
。第一个将通用时间转换为指定时区的相应时间(其中nil表示您的本地时区)。另一个则反过来,但仅包含在Windows XP及更高版本中,正如Borland帮助所说。如果您只打算进行时区相关的时间转换,那么它们应该没问题。并且很高兴知道他们检查给定的UTC时间是DST还是标准时间。但是,我没有在任何地方阅读它。我自己检查过,所以如果我错了请纠正我。
请参阅以下功能。我不确定我是否会在任何地方使用它,因为我不确定它的正确性,但它可能值得一看。请不要告诉我这是一种愚蠢的方法,因为我知道: - )。
function IsDaylightSavingTime(lLocalTime: TDateTime): boolean;
var
lUniversalSystemTime: TSystemTime;
lLocalSystemTime: TSystemTime;
lTimeZoneInfo: TTimeZoneInformation;
begin
case GetTimeZoneInformation(lTimeZoneInfo) of
TIME_ZONE_ID_UNKNOWN:
begin
Result := False;
Exit;
end;
TIME_ZONE_ID_STANDARD,
TIME_ZONE_ID_DAYLIGHT: ;
else
//TIME_ZONE_ID_INVALID:
RaiseLastOSError();
end;
DateTimeToSystemTime(lLocalTime, lLocalSystemTime);
if not TzSpecificLocalTimeToSystemTime(nil, lLocalSystemTime, lUniversalSystemTime) then
RaiseLastOSError();
Result := SameTime(SystemTimeToDateTime(lUniversalSystemTime),
IncMinute(lLocalTime, lTimeZoneInfo.DaylightBias + lTimeZoneInfo.Bias));
end;