【问题标题】:Delphi - equivalent to C# DateTime.IsDaylightSavingTime() method neededDelphi - 相当于需要 C# DateTime.IsDaylightSavingTime() 方法
【发布时间】:2010-08-10 12:39:19
【问题描述】:

我需要以某种方式确定某个 TDateTime 值是否在我的时区的夏令时范围内(在 C# 中,DateTime.IsDaylightSavingTime() 方法也是如此)。

我知道在 Delphi 中没有类似的功能,因为 Delphi TDateTime 不包含有关时区的信息,但我想有一些方法可以使用 Win32 API 来做到这一点。

我看过 Win32 API GetTimeZoneInformationGetTimeZoneInformationForYear 函数,但我不太明白如何使用它们,所以想请教一下。提前感谢您的任何提示。

编辑:

例子:

在我的时区(中欧)夏令时从今年开始 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 设置中有关当前时区的信息。

【问题讨论】:

    标签: delphi winapi delphi-6


    【解决方案1】:

    这并不像听起来那么容易,因为:

    1) DST 和标准时间之间的转换日期并非所有国家/地区都相同

    2) DST 和标准时间之间的转换日期对于同一国家/地区所有年份的算法都不相同(例如,在中欧,以前是 4 月的第一个星期日,IIRC,现在是 3 月的最后一个星期日)。美国从 2007 年起从 4 月的第一个星期日改为 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;
    

    【讨论】:

      【解决方案2】:

      也许对于您的特定应用程序来说这有点过头了,但是开源项目“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。

      【讨论】:

        【解决方案3】:

        昂德拉 C. -

        是的,你是对的。您需要:

        1. 将 Delphi TDateTime 变量设置为您希望的日期/时间

        2. 将其转换为 Windows SystemTime

        3. 调用GetTimeZoneInformation()获取TTimeZoneInformation

        4. 使用您的 TTimeZoneInformation 结构调用 GetTimeZoneInformationForYear(),以获取您的时区的 DST 信息(我不确定您从哪里获得某个任意时区的 TTimeZoneInformation - 但您应该能够在 MSDN 上找到它)。

        5. 计算您的系统时间是在 TTZI.StandardDate 之后(在这种情况下为标准时间)还是在 TTZI.DaylightDate 之后(在这种情况下为 DST)。

        或者...

        也许您可以将其转换为 Delphi 表:

        http://www.twinsun.com/tz/tz-link.htm

        对于任何时区的任何日期时间,只需查看给定的日期时间是在 DST 之内还是之外。瞧!没有 Microsoft API - 只是一个简单的表查找或 if/else case 块!

        '希望有帮助.. pSM

        【讨论】:

          【解决方案4】:

          正如您自己所说,该信息并未与 Delphi 中的日期时间捆绑在一起,因此您不能简单地移植它。每个产生 tdatetime 的例程都必须添加此信息,而 Delphi 中并非如此。

          也许你应该多解释你真正想做的事情,少用类比来描述问题

          【讨论】:

          • 该信息也未捆绑在 .Net 中。只是 .Net 类型具有使其看起来比实际存储更多信息的方法。该方法只是使用相同的底层 Windows API,这就是 .Net 方法在 Windows XP 上表现不同的原因。
          【解决方案5】:

          我用.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;
          }
          

          【讨论】:

            【解决方案6】:

            您可以在 LocalDateTimeToDateTime 函数的 JclDateTime.pas 单元中的 JEDI Code Library(开源)中找到一个示例。将检索夏时制时间信息并将其用于转换为 UTC 时间。

            【讨论】:

              【解决方案7】:

              我知道这不是您问题的答案,但您可能对以下两个函数感兴趣:SystemTimeToTzSpecificLocalTime()TzSpecificLocalTimeToSystemTime()。第一个将通用时间转换为指定时区的相应时间(其中 nil 表示您的本地时区)。另一种则相反,但正如 Borland Help 所说,仅包含在 Windows XP 及更高版本中。如果您只打算进行与时区相关的时间转换,那么它们应该适合您。很高兴知道他们检查给定的 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;
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-06-02
                • 1970-01-01
                • 2011-02-04
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多