Componentes.Terceros.jcl/official/2.1.1/source/windows/JclTimeZones.pas
2010-01-18 16:51:36 +00:00

825 lines
30 KiB
ObjectPascal

{**************************************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
{ you may not use this file except in compliance with the License. You may obtain a copy of the }
{ License at http://www.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
{ and limitations under the License. }
{ }
{ The Original Code is TimeZones.pas. }
{ }
{ The Initial Developer of the Original Code is Rik Barker }
{ <rik dott barker att visionsoft dott com>. }
{ Portions created by Rik Barker are Copyright (C) Rik Barker. All Rights Reserved. }
{ }
{ Contributors: }
{ }
{**************************************************************************************************}
{ }
{ Routines and classes for working with Timezones and UTC dates. }
{ }
{**************************************************************************************************}
{ }
{ Last modified: $Date:: 2009-09-13 11:57:20 +0200 (dim., 13 sept. 2009) $ }
{ Revision: $Rev:: 3009 $ }
{ Author: $Author:: outchy $ }
{ }
{**************************************************************************************************}
{**************************************************************************************************}
{ Important: }
{ Information on Timezones is pretty sparse and often vague. }
{ According to MSDN GetTimeZoneInformation will return TIME_ZONE_ID_DAYLIGHT if }
{ }
{ "the system is operating in the range covered by the DaylightDate }
{ member of the TIME_ZONE_INFORMATION structure." }
{ }
{ What it fails to mention is that it will take liberties with the TIME_ZONE_INFORMATION structure.}
{ Unless "Automatically Adjust clock for Daylight savings changes" is enabled, DayLightDate }
{ will contain StandardDate and DayLightName will contain StandardName. }
{ So you know you're supposed to be in daylight savings, but you can't find out when it started or }
{ what the Daylight Name is. }
{ }
{ Where possible use the functions/classes that read the data direct from the registry, }
{ these will always return the correct daylight savings date and name }
{**************************************************************************************************}
unit JclTimeZones;
{$I jcl.inc}
{$I windowsonly.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
Windows, SysUtils, Contnrs, Classes;
type
// Contents of the TZI value in the Time Zones section of the registry
TJclTZIValueInfo = record
Bias: Longint;
StandardBias: Integer;
DaylightBias: Integer;
StandardDate: TSystemTime;
DaylightDate: TSystemTime;
end;
// All other Time Zone values in the registry
PJclTimeZoneRegInfo = ^TJclTimeZoneRegInfo;
TJclTimeZoneRegInfo = record
DisplayDesc: string;
StandardName: string;
DaylightName: string;
SortIndex: Integer;
MapID: string;
TZI: TJclTZIValueInfo;
end;
type
// Callback prototype for EnumTimeZones
TJclTimeZoneCallBackFunc = function(const TimeZoneRec: TJclTimeZoneRegInfo): Boolean of object;
type
TJclTimeZoneInfo = class(TObject)
private
FStandardName: string;
FDaylightName: string;
FTZDescription: string;
FSortIndex: Integer; // Order to sort the timezones into
FMapID: string; // Coordinates for the Date/Time properties map
FBiasInfo: TJclTZIValueInfo; // Bias information from the registry
function GetActiveBias: Integer;
function GetCurrentDateTime: TDateTime;
function GetDaylightSavingsStartDate: TDateTime;
function GetGMTOffset: string;
function GetStandardStartDate: TDateTime;
function GetSupportsDaylightSavings: Boolean;
function GetTimeZoneType(TZI: TJclTZIValueInfo): Cardinal;
public
procedure Assign(Source: TJclTimeZoneRegInfo);
procedure ApplyTimeZone;
function DayLightSavingsPeriod: string;
function DateTimeIsInDaylightSavings(ADateTime: TDateTime):Boolean;
function StandardStartDateInYear(const AYear: Integer): TDateTime;
function DaylightStartDateInYear(const AYear: Integer): TDateTime;
// These are all the values we want to be able to stream
property ActiveBias: Integer read GetActiveBias;
property CurrentDateTime: TDateTime read GetCurrentDateTime;
property DaylightName: string read FDaylightName;
property DaylightSavingsStartDate: TDateTime read GetDaylightSavingsStartDate;
property DisplayDescription: string read FTZDescription;
property GMTOffset: string read GetGMTOffset;
property MapID: string read FMapID;
property SortIndex: Integer read FSortIndex;
property StandardName: string read FStandardName;
property StandardStartDate: TDateTime read GetStandardStartDate;
property SupportsDaylightSavings: Boolean read GetSupportsDaylightSavings;
end;
TJclTimeZones = class(TObject)
private
FActiveTimeZoneIndex: Integer;
FTimeZones: TObjectList;
FAutoAdjustEnabled: Boolean;
function GetAutoAdjustEnabled: Boolean;
function GetActiveTimeZoneInfo: TJclTimeZoneInfo;
function GetCount: Integer;
function GetItem(Index: Integer): TJclTimeZoneInfo;
procedure LoadTimeZones;
function TimeZoneCallback(const TimeZoneRec: TJclTimeZoneRegInfo): Boolean;
public
constructor Create;
destructor Destroy; override;
function SetDateTime(DateTime: TDateTime): Boolean;
procedure SetAutoAdjustEnabled(Value: Boolean);
property Count: Integer read GetCount;
property Items[Index: Integer]: TJclTimeZoneInfo read GetItem; default;
property ActiveTimeZone: TJclTimeZoneInfo read GetActiveTimeZoneInfo;
property AutoAdjustEnabled: Boolean read FAutoAdjustEnabled write FAutoAdjustEnabled;
end;
type
EDaylightSavingsNotSupported = class(Exception);
EAutoAdjustNotEnabled = class(Exception);
// Enumerate all time zones from the registry
function EnumTimeZones(CallBackFunc: TJclTimeZoneCallBackFunc): Boolean;
// Functions that read from the current time zone
function IsAutoAdjustEnabled: Boolean;
function CurrentTimeZoneSupportsDaylightSavings: Boolean;
function DateCurrentTimeZoneClocksChangeToStandard: TDateTime;
function DateCurrentTimeZoneClocksChangeToDaylightSavings: TDateTime;
function GetCurrentTimeZoneDescription: string;
function GetCurrentTimeZoneDaylightSavingsPeriod: string;
function GetCurrentTimeZoneGMTOffset: string;
function GetCurrentTimeZoneUTCBias: Integer;
// Misc UTC related functions
function GetWMIScheduledJobUTCTime(Time: TDateTime): string;
function UTCNow: TDateTime;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jcl.svn.sourceforge.net/svnroot/jcl/tags/JCL-2.1-Build3536/jcl/source/windows/JclTimeZones.pas $';
Revision: '$Revision: 3009 $';
Date: '$Date: 2009-09-13 11:57:20 +0200 (dim., 13 sept. 2009) $';
LogPath: 'JCL\source\windows';
Extra: '';
Data: nil
);
{$ENDIF UNITVERSIONING}
implementation
uses
Messages,
JclResources,
JclDateTime, JclRegistry;
const
cAutoAdjustKey = '\System\CurrentControlSet\Control\TimeZoneInformation';
cAutoAdjustValue = 'DisableAutoDaylightTimeSet';
const
UTCDays: array [0..6] of PChar =
('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
UTCDaysOffset: array [1..5] of PChar =
('first', 'second', 'third', 'fourth', 'last');
UTCMonths: array [1..12] of string =
('January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December');
//--------------------------------------------------------------------------------------------------
// Callback enumerator of timezone information. Return false to stop enumeration
function EnumTimeZones(CallBackFunc: TJclTimeZoneCallBackFunc): Boolean;
var
TimeZoneRootName: string;
TimeZones: TStringList;
I: Integer;
TimeZoneRegInfo: TJclTimeZoneRegInfo;
begin
Result := True;
if not Assigned(CallBackFunc) then
raise Exception.Create(RsENoCallbackFunc);
TimeZones := TStringList.Create;
try
if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
TimeZoneRootName := '\Software\Microsoft\Windows\CurrentVersion\Time Zones\'
else
TimeZoneRootName := '\Software\Microsoft\Windows NT\CurrentVersion\Time Zones\';
if not RegGetKeyNames(HKEY_LOCAL_MACHINE, TimeZoneRootName, TimeZones) then
Exit;
for I := 0 to TimeZones.Count - 1 do
begin
FillChar(TimeZoneRegInfo, SizeOf(TJclTimeZoneRegInfo), #0);
TimeZoneRegInfo.DisplayDesc := RegReadString(HKEY_LOCAL_MACHINE, TimeZoneRootName + TimeZones[I], 'Display');
TimeZoneRegInfo.StandardName := RegReadString(HKEY_LOCAL_MACHINE, TimeZoneRootName + TimeZones[I], 'Std');
TimeZoneRegInfo.DaylightName := RegReadString(HKEY_LOCAL_MACHINE, TimeZoneRootName + TimeZones[I], 'Dlt');
TimeZoneRegInfo.SortIndex := RegReadIntegerDef(HKEY_LOCAL_MACHINE, TimeZoneRootName + TimeZones[I], 'Index', -1);
TimeZoneRegInfo.MapID := RegReadStringDef(HKEY_LOCAL_MACHINE, TimeZoneRootName + TimeZones[I], 'MapID', '');
RegReadBinary(HKEY_LOCAL_MACHINE, TimeZoneRootName + TimeZones[I], 'TZI', TimeZoneRegInfo.TZI,
SizeOf(TJclTZIValueInfo));
// Allow the callback function to stop the enumeration
if not CallBackFunc(TimeZoneRegInfo) then
Break;
end;
finally
TimeZones.Free;
end;
end;
//--------------------------------------------------------------------------------------------------
// Translates dates like "the third thursday in March" into the actual date
function CalculateTransitionDate(TransitionInfo: TSystemTime; CalculateForYear: Integer= -1): TDateTime;
var
CheckDate, FirstOfMonth: TDateTime;
MonthStartDOW: Integer;
TransitionDOW: Word;
TransitionDay: Word;
PartialDays: Integer;
I, MonthDays: Integer;
Year, Month, Day: Word;
ActiveYear: Integer;
begin
if CalculateForYear= -1 then
ActiveYear:=YearOfDate(Now)
else
ActiveYear:=CalculateForYear;
// If the date is empty, we've been passed a system date for a timezone that has no daylight support
if (TransitionInfo.wYear = 0) and (TransitionInfo.wMonth = 0) and (TransitionInfo.wDay = 0) then
begin
Result := 0;
Exit;
end;
// Work out what day the first of the month is
FirstOfMonth := EncodeDate(ActiveYear, TransitionInfo.wMonth, 1);
// System time is 0 based, Day of Week is 1 based
MonthStartDOW := DayOfWeek(FirstOfMonth);
TransitionDOW := TransitionInfo.wDayOfWeek + 1;
if MonthStartDOW > TransitionDOW then
PartialDays := 7 - (MonthStartDOW - TransitionDOW) + 1
else
PartialDays := (TransitionDOW - MonthStartDOW) + 1;
// According to MSDN, wDay member is a value in the range 1 through 5.
// Using this notation, the first Sunday in April can be specified,
// as can the last Thursday in October (5 is equal to "the last").
TransitionDay := 1;
case TransitionInfo.wDay of
1:
TransitionDay := PartialDays;
2, 3, 4:
TransitionDay := (7 * (TransitionInfo.wDay - 1)) + PartialDays;
5:
begin
// Work out the date of the last X day in the month
MonthDays := DaysInMonth(FirstOfMonth);
DecodeDate(FirstOfMonth, Year, Month, Day);
for I := MonthDays downto MonthDays - 7 do
begin
CheckDate := EncodeDate(Year, Month, I);
if DayOfWeek(CheckDate) = TransitionDOW then
begin
TransitionDay := I;
Break;
end;
end;
end;
end;
Result := EncodeDate(ActiveYear, TransitionInfo.wMonth, TransitionDay);
end;
//--------------------------------------------------------------------------------------------------
//Returns the date range in friendly format "From the 1st sunday in March to the last Sunday in October"
function GetDayLightSavingsPeriod(StandardDate, DayLightDate: TSystemTime): string;
begin
if (DaylightDate.wMonth = 0) and (StandardDate.wMonth = 0) then
Result := '' // There is no daylight savings period for this timezone
else
begin
if (DaylightDate.wMonth = StandardDate.wMonth) and (DaylightDate.wDay = StandardDate.wDay) then
Result := '' // AutoAdjust for Daylight savings is not enabled, so Windows returns the entire year
else
begin
Result := 'From the ' + UTCDaysOffset[DaylightDate.wDay] + ' ' + UTCDays[DaylightDate.wDayOfWeek] + ' of ' +
UTCMonths[DaylightDate.wMonth] +
' to the ' + UTCDaysOffset[StandardDate.wDay] + ' ' + UTCDays[StandardDate.wDayOfWeek] + ' of ' +
UTCMonths[StandardDate.wMonth];
end;
end;
end;
//--------------------------------------------------------------------------------------------------
// Functions relating to the current time zone
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// Returns true if the current timezone supports daylight savings - regardless of whether
// AutoAdjust is enabled or not
function CurrentTimeZoneSupportsDaylightSavings: Boolean;
var
TimeZoneInfo: TTimeZoneInformation;
begin
Result := False;
FillChar(TimeZoneInfo, SizeOf(TimeZoneInfo), #0);
case GetTimeZoneInformation(TimeZoneInfo) of
TIME_ZONE_ID_STANDARD:
Result := TimeZoneInfo.StandardDate.wMonth <> 0;
TIME_ZONE_ID_DAYLIGHT:
Result := True;
end;
end;
//-----------------------------------------------------------------------------
// Returns the exact date that clocks switch to daylight savings in the current
// year for the current time zone
function DateCurrentTimeZoneClocksChangeToDaylightSavings: TDateTime;
var
TimeZoneInfo: TTimeZoneInformation;
begin
// The daylight savings time is only returned if AutoAdjust is enabled and the
// timezone supports Daylight savings.
if not CurrentTimeZoneSupportsDaylightSavings then
raise EDaylightSavingsNotSupported.Create(RsEDaylightSavingsNotSupported);
if not IsAutoAdjustEnabled then
raise EAutoAdjustNotEnabled.Create(RsEAutoAdjustNotEnabled);
Result := 0;
FillChar(TimeZoneInfo, SizeOf(TimeZoneInfo), #0);
if GetTimeZoneInformation(TimeZoneInfo) in [TIME_ZONE_ID_DAYLIGHT, TIME_ZONE_ID_STANDARD] then
Result := CalculateTransitionDate(TimeZoneInfo.DaylightDate);
end;
//-----------------------------------------------------------------------------
// Returns the exact date that clocks switch to standard time in the current year
// for the current time zone
function DateCurrentTimeZoneClocksChangeToStandard: TDateTime;
var
TimeZoneInfo: TTimeZoneInformation;
begin
if not CurrentTimeZoneSupportsDaylightSavings then
raise EDaylightSavingsNotSupported.Create(RsEDaylightSavingsNotSupported);
Result := 0;
FillChar(TimeZoneInfo, SizeOf(TimeZoneInfo), #0);
if GetTimeZoneInformation(TimeZoneInfo) in [TIME_ZONE_ID_DAYLIGHT, TIME_ZONE_ID_STANDARD] then
Result := CalculateTransitionDate(TimeZoneInfo.StandardDate);
end;
//----------------------------------------------------------------------------
// Returns the name of the current time zone
function GetCurrentTimeZoneDescription: string;
var
TimeZoneInfo: TTimeZoneInformation;
begin
FillChar(TimeZoneInfo, SizeOf(TimeZoneInfo), #0);
case GetTimeZoneInformation(TimeZoneInfo) of
TIME_ZONE_ID_INVALID:
Result := '';
TIME_ZONE_ID_STANDARD:
Result := TimeZoneInfo.StandardName;
TIME_ZONE_ID_UNKNOWN:
Result := TimeZoneInfo.StandardName;
TIME_ZONE_ID_DAYLIGHT:
Result := TimeZoneInfo.DaylightName;
end;
end;
//-----------------------------------------------------------------------------
// Returns the date range in friendly format for the current timezone
function GetCurrentTimeZoneDaylightSavingsPeriod: string;
var
TimeZoneInfo: TTimeZoneInformation;
begin
Result := '';
if not IsAutoAdjustEnabled then
Exit;
FillChar(TimeZoneInfo, SizeOf(TimeZoneInfo), #0);
if GetTimeZoneInformation(TimeZoneInfo) = TIME_ZONE_ID_INVALID then
Exit;
Result := GetDaylightSavingsPeriod(TimeZoneInfo.StandardDate, TimeZoneInfo.DaylightDate);
end;
//-----------------------------------------------------------------------------
// Returns the current offset from GMT as a string "GMT+03:00"
function GetCurrentTimeZoneGMTOffset: string;
var
Bias: Integer;
Hours, Minutes: Integer;
begin
Bias := GetCurrentTimeZoneUTCBias;
Hours := Bias div 60;
Minutes := Abs(Bias) mod 60;
if Bias >= 0 then
Result := Format('GMT+%.2d:%.2d', [Hours, Minutes])
else
// (rom) not GMT- here?
Result := Format('GMT%.2d:%.2d', [Hours, Minutes]);
end;
//-----------------------------------------------------------------------------
// Returns the Current Time Zone UTC Bias from GMT in minutes
function GetCurrentTimeZoneUTCBias: Integer;
var
TimeZoneInfo: TTimeZoneInformation;
begin
Result := 0;
FillChar(TimeZoneInfo, SizeOf(TimeZoneInfo), #0);
case GetTimeZoneInformation(TimeZoneInfo) of
TIME_ZONE_ID_STANDARD:
Result := -(TimeZoneInfo.Bias + TimeZoneInfo.StandardBias);
TIME_ZONE_ID_UNKNOWN:
Result := -(TimeZoneInfo.Bias + TimeZoneInfo.StandardBias);
TIME_ZONE_ID_DAYLIGHT:
Result := -(TimeZoneInfo.Bias + TimeZoneInfo.DaylightBias);
end;
end;
//-----------------------------------------------------------------------------
// Miscellaneous UTC functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Returns the current UTC time
function UTCNow: TDateTime;
var
SystemTime: TSystemTime;
begin
GetSystemTime(SystemTime);
Result := EncodeDate(SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay) +
EncodeTime(SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond, SystemTime.wMilliseconds);
end;
//-----------------------------------------------------------------------------
// Returns Time in the UTC string format expected by root\cimv2\Win32_ScheduledJob
// TODO: This works for scheduling local jobs, but fails on remote jobs - looks
// like Windows does something a bit odd with remote scheduled jobs, it's not
// handling the time correctly at all. So much for UTC.
function GetWMIScheduledJobUTCTime(Time: TDateTime): string;
var
TimeBias: Integer;
Hour, Min, Sec, MSec: Word;
begin
Result := '';
TimeBias := GetCurrentTimeZoneUTCBias;
DecodeTime(Time, Hour, Min, Sec, MSec);
Result := '********' + Format('%.2d%.2d%.2d.000000', [Hour, Min, Sec]);
if TimeBias >= 0 then
Result := Result + Format('+%.3d', [TimeBias])
else
Result := Result + Format('%.3d', [TimeBias]);
end;
//-----------------------------------------------------------------------------
// Returns true if "Automatically Adjust clock for daylight saving changes" is checked
function IsAutoAdjustEnabled: Boolean;
begin
Result := RegReadIntegerDef(HKEY_LOCAL_MACHINE, cAutoAdjustKey, cAutoAdjustValue, 0) = 0;
end;
//=== { TJclTimeZoneInfo } ====================================================
procedure TJclTimeZoneInfo.ApplyTimeZone;
var
TimeZoneInfo: TTimeZoneInformation;
begin
TimeZoneInfo.Bias := FBiasInfo.Bias;
StringToWideChar(FStandardName, TimeZoneInfo.StandardName, 32);
TimeZoneInfo.StandardDate := FBiasInfo.StandardDate;
TimeZoneInfo.StandardBias := FBiasInfo.StandardBias;
StringToWideChar(FDaylightName, TimeZoneInfo.DaylightName, 32);
TimeZoneInfo.DaylightDate := FBiasInfo.DaylightDate;
TimeZoneInfo.DaylightBias := FBiasInfo.DaylightBias;
if not SetTimeZoneInformation({$IFDEF FPC}@{$ENDIF FPC}TimeZoneInfo) then
RaiseLastOSError;
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);
end;
procedure TJclTimeZoneInfo.Assign(Source: TJclTimeZoneRegInfo);
begin
FStandardName := Source.StandardName;
FDaylightName := Source.DaylightName;
FTZDescription := Source.DisplayDesc;
FSortIndex := Source.SortIndex;
FMapID := Source.MapID;
FBiasInfo := Source.TZI;
end;
function TJclTimeZoneInfo.GetActiveBias: Integer;
begin
// Return the active bias (including any skew derived from auto-adjust being enabled)
Result := 0;
case GetTimeZoneType(FBiasInfo) of
TIME_ZONE_ID_STANDARD:
Result := -(FBiasInfo.Bias + FBiasInfo.StandardBias);
TIME_ZONE_ID_UNKNOWN:
Result := -(FBiasInfo.Bias + FBiasInfo.StandardBias);
TIME_ZONE_ID_DAYLIGHT:
// the bias is different if autoadjust is turned off
if IsAutoAdjustEnabled then
Result := -(FBiasInfo.Bias + FBiasInfo.DaylightBias)
else
Result := -(FBiasInfo.Bias + FBiasInfo.StandardBias);
end;
end;
//-----------------------------------------------------------------------------
// Returns the exact date that clocks switch to daylight savings in the current
// year for the specified standard date
function TJclTimeZoneInfo.DayLightSavingsPeriod: string;
begin
Result := GetDayLightSavingsPeriod(FBiasInfo.StandardDate, FBiasInfo.DayLightDate);
end;
function TJclTimeZoneInfo.GetDaylightSavingsStartDate: TDateTime;
begin
Result := CalculateTransitionDate(FBiasInfo.DaylightDate);
end;
//-----------------------------------------------------------------------------
// Returns the offset from GMT as a string "GMT+03:00"
function TJclTimeZoneInfo.GetGMTOffset: string;
var
Hours, Minutes: Integer;
begin
Hours := -FBiasInfo.Bias div 60;
Minutes := Abs(FBiasInfo.Bias) mod 60;
if Hours >= 0 then
Result := Format('GMT+%.2d:%.2d', [Hours, Minutes])
else
// (rom) No GMT- here?
Result := Format('GMT%.2d:%.2d', [Hours, Minutes]);
end;
function TJclTimeZoneInfo.GetStandardStartDate: TDateTime;
begin
Result := CalculateTransitionDate(FBiasInfo.StandardDate);
end;
function TJclTimeZoneInfo.GetSupportsDaylightSavings: Boolean;
begin
Result := False;
// TODO: Check this is correct on 9x
case GetTimeZoneType(FBiasInfo) of
TIME_ZONE_ID_STANDARD:
Result := FBiasInfo.StandardDate.wMonth <> 0;
TIME_ZONE_ID_DAYLIGHT:
Result := True;
end;
end;
function TJclTimeZoneInfo.GetCurrentDateTime: TDateTime;
begin
// Return the current date time in this time zone
Result := UTCNow + (GetActiveBias / 1440);
end;
// Returns the TimeZone type based on StandardDate and DaylightDate
function TJclTimeZoneInfo.GetTimeZoneType(TZI: TJclTZIValueInfo): Cardinal;
var
StandardDate: TDateTime;
DaylightSavingsDate: TDateTime;
begin
StandardDate := CalculateTransitionDate(TZI.StandardDate);
DaylightSavingsDate := CalculateTransitionDate(TZI.DaylightDate);
if (StandardDate = 0) and (DayLightSavingsDate = 0) then
Result := TIME_ZONE_ID_UNKNOWN
else
begin
// In places like Australia, Daylight savings is after Standard, in the UK it's the other way round
if DayLightSavingsDate > StandardDate then
begin
if (Now >= StandardDate) and (Now < DaylightSavingsDate) then
Result := TIME_ZONE_ID_STANDARD
else
Result := TIME_ZONE_ID_DAYLIGHT;
end
else
begin
if (Now >= DaylightSavingsDate) and (Now < StandardDate) then
Result := TIME_ZONE_ID_DAYLIGHT
else
Result := TIME_ZONE_ID_STANDARD;
end;
end;
end;
//=== { TJclTimeZones } =======================================================
function TimeZoneSort(Item1, Item2: Pointer): Integer;
begin
Result := TJclTimeZoneInfo(Item1).SortIndex - TJclTimeZoneInfo(Item2).SortIndex;
if (Result = 0) or (TJclTimeZoneInfo(Item1).SortIndex = -1) or (TJclTimeZoneInfo(Item2).SortIndex = -1) then
Result := CompareText(TJclTimeZoneInfo(Item1).DisplayDescription, TJclTimeZoneInfo(Item2).DisplayDescription);
end;
constructor TJclTimeZones.Create;
begin
inherited Create;
FAutoAdjustEnabled := GetAutoAdjustEnabled;
FTimeZones := TObjectList.Create(True);
LoadTimeZones;
end;
destructor TJclTimeZones.Destroy;
begin
FreeAndNil(FTimeZones);
inherited Destroy;
end;
function TJclTimeZones.GetAutoAdjustEnabled: Boolean;
begin
Result := IsAutoAdjustEnabled;
end;
function TJclTimeZones.GetActiveTimeZoneInfo: TJclTimeZoneInfo;
begin
Result := GetItem(FActiveTimeZoneIndex);
end;
function TJclTimeZones.GetCount: Integer;
begin
Result := FTimeZones.Count;
end;
function TJclTimeZones.GetItem(Index: Integer): TJclTimeZoneInfo;
begin
if (Index >= 0) or (Index < FTimeZones.Count) then
Result := TJclTimeZoneInfo(FTimeZones[Index])
else
Result := nil;
end;
procedure TJclTimeZones.LoadTimeZones;
var
CurrentTimeZoneDesc: string;
I: Integer;
begin
EnumTimeZones(TimeZoneCallback);
FTimeZones.Sort(@TimeZoneSort);
CurrentTimeZoneDesc := GetCurrentTimeZoneDescription;
FActiveTimeZoneIndex := -1;
for I := 0 to FTimeZones.Count - 1 do
if (TJclTimeZoneInfo(FTimeZones[I]).StandardName = CurrentTimeZoneDesc) or
(TJclTimeZoneInfo(FTimeZones[I]).DayLightName = CurrentTimeZoneDesc) then
begin
FActiveTimeZoneIndex := I;
Break;
end;
end;
procedure TJclTimeZones.SetAutoAdjustEnabled(Value: Boolean);
begin
// TODO: PC isn't being notified correctly of the change
if FAutoAdjustEnabled <> Value then
begin
if not Value then
RegWriteInteger(HKEY_LOCAL_MACHINE, cAutoAdjustKey, cAutoAdjustValue, 1)
else
RegDeleteEntry(HKEY_LOCAL_MACHINE, cAutoAdjustKey, cAutoAdjustValue);
FAutoAdjustEnabled := Value;
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, LPARAM(PChar('intl')));
SendMessage(HWND_TOPMOST, WM_TIMECHANGE, 0, 0);
end;
end;
function TJclTimeZones.TimeZoneCallback(const TimeZoneRec: TJclTimeZoneRegInfo): Boolean;
var
TimeZone: TJclTimeZoneInfo;
begin
Result := True;
TimeZone := TJclTimeZoneInfo.Create;
TimeZone.Assign(TimeZoneRec);
FTimeZones.Add(TimeZone)
end;
function TJclTimeZones.SetDateTime(DateTime: TDateTime): Boolean;
var
SystemTime: TSystemTime;
begin
// The date time we have is local. Convert it to UTC
DateTime := LocalDateTimeToDateTime(DateTime);
DecodeDate(DateTime, SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay);
DecodeTime(DateTime, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond, SystemTime.wMilliSeconds);
Result := SetSystemTime(SystemTime);
if not Result then
RaiseLastOSError;
SendMessage(HWND_TOPMOST, WM_TIMECHANGE, 0, 0);
end;
function TJclTimeZoneInfo.DateTimeIsInDaylightSavings(ADateTime: TDateTime): Boolean;
var
dsStartDate: TDateTime;
stdStartDate: TDateTime;
Year: Integer;
begin
//Return whether the specified date time is DaylightSavings or not
Result:=False;
if not SupportsDaylightSavings then
Exit;
Year:=YearOfDate(ADateTime);
dsStartDate := CalculateTransitionDate(FBiasInfo.DaylightDate, Year);
stdStartDate:= CalculateTransitionDate(FBiasInfo.StandardDate, Year);
// In places like Australia, Daylight savings is after Standard, in the UK it's the other way round
if dsStartDate > stdStartDate then
begin
if (ADateTime >= stdStartDate) and (ADateTime < dsStartDate) then
Result := False
else
Result := True;
end
else
begin
if (ADateTime >= dsStartDate) and (ADateTime < stdStartDate) then
Result := True
else
Result := False;
end;
end;
function TJclTimeZoneInfo.DaylightStartDateInYear(
const AYear: Integer): TDateTime;
begin
Result:= CalculateTransitionDate(FBiasInfo.DaylightDate, AYear);
end;
function TJclTimeZoneInfo.StandardStartDateInYear(
const AYear: Integer): TDateTime;
begin
Result:= CalculateTransitionDate(FBiasInfo.StandardDate, AYear);
end;
{$IFDEF UNITVERSIONING}
initialization
RegisterUnitVersion(HInstance, UnitVersioning);
finalization
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.