2010-08-10 85 views
8

我需要以某種方式確定某些TDateTime值是否在我的時區的夏令時範圍內(在C#中同樣是DateTime.IsDaylightSavingTime()方法)。德爾福 - 相當於C#DateTime.IsDaylightSavingTime()方法需要

我知道在德爾福沒有類似的功能,因爲德爾福TDateTime不包含有關時區的信息,但我想有一些方法如何使用Win32 API做到這一點。

我已經看過Win32 API GetTimeZoneInformationGetTimeZoneInformationForYear函數,但我不太明白如何使用它們,所以我想問你一些幫助。預先感謝任何提示。

編輯:

例子:

在我的時區(中歐)夏令時 3月28日開始,今年在凌晨2點和凌晨3點結束於2010年10月31日。

我需要一個函數頭:

function IsDaylightSavingTime(input: TDateTime): boolean; 

,將返回true如果輸入的日期是3點和false如果不是2010年3月2:00和2010年10月31日28日之間。

(這個例子僅僅是2010年,但我需要它爲所有以上工作。)

再次,我知道,單獨保存在TDateTime類型的信息是不夠的,但我認爲,隨着一些Win32 API函數我應該能夠得到例如有關Windows設置中當前時區的信息。

回答

3

這是不那麼容易,因爲它的聲音,這是因爲:

1)開關,在日期DST與標準時間之間的差異並非所有國家都相同

2)DST和標準時間之間的切換日期與所有年份的同一個國家/地區的算法不同(例如,在中歐,它以前是第一個四月的星期天,IIRC,現在是三月的最後一個星期日)。美國從四月的第一個星期天改爲2007年三月的第二個星期天。

所以 - 一個簡單的日期是不夠的,你還需要一個地理位置。

但是,如果你可以用這一事實生活中,你限制自己可以從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; 
+0

爲什麼不從xml或數據庫更新?也許網絡服務? – none 2010-10-10 08:52:14

0

至於你說的你自己,這些信息沒有保存捆綁在Delphi日期時間,所以你不能簡單的端口這一點。每個處理tdatetime的例程都必須添加此信息,而Delphi中不是這種情況。

也許你應該更多地解釋你真正努力去做,並說明問題少類推

+0

好的,我加了一個例子,我希望現在有點更清楚了...... – 2010-08-10 13:20:15

+0

這個信息並沒有捆綁在.Net中,只是.Net類型具有使它看起來像存儲更多信息的方法。該方法只是使用相同的基礎Windows API,這就是爲什麼.Net方法在Windows XP上表現不同的原因。 – 2010-08-10 21:53:11

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; 
} 
0

您可以找到JclDateTime.pas單元的這個在JEDI Code Library(開源)爲例,在LocalDateTimeToDateTime功能。夏令時時間信息被檢索並用於轉換爲UTC時間和從UTC時間轉換。

1

Ondra 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內部或外部。瞧!沒有微軟的API--只是一個簡單的表格查詢或if/else case block!

'希望可以幫到..pSM

0

我知道這不是您的問題的答案,但您可能會對以下兩個功能感興趣:SystemTimeToTzSpecificLocalTime()TzSpecificLocalTimeToSystemTime()。第一個將世界時間轉換爲指定時區的相應時間(其中nil表示當地時區)。另一個則反過來,但只包含在Windows XP及更高版本中,就像Borland Help所說的那樣。如果你打算只做一個時區相關的時間轉換,它們應該是適合你的。很高興知道他們檢查給定的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; 
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中。