1
0
mirror of synced 2024-09-18 17:26:08 +00:00

Fixed: Daylight Saving Time dependant on the target date instead of the current settings

This commit is contained in:
Mark van Renswoude 2016-04-15 13:37:58 +00:00
parent 29c1cab091
commit e9f864d150
3 changed files with 168 additions and 29 deletions

View File

@ -72,6 +72,11 @@ type
function WriteInteger(const AName: String; AValue: Integer): Boolean; override;
function WriteString(const AName: String; const AValue: String): Boolean; override;
procedure GetKeys(const ADest: TStrings); override;
procedure GetSections(const ADest: TStrings); override;
procedure DeleteKey(const AName: string); override;
procedure DeleteSection(const AName: string); override;
property Output: IPersistTestOutput read FOutput write FOutput;
end;
@ -191,7 +196,7 @@ var
testFiler: TX2UtPersistTestFiler;
begin
testFiler := TX2UtPersistTestFiler.Create(AIsReader);;
testFiler := TX2UtPersistTestFiler.Create(AIsReader);
testFiler.Output := Self.Output;
Result := testFiler;
@ -266,6 +271,24 @@ begin
end;
procedure TX2UtPersistTestFiler.GetKeys(const ADest: TStrings);
begin
end;
procedure TX2UtPersistTestFiler.GetSections(const ADest: TStrings);
begin
end;
procedure TX2UtPersistTestFiler.DeleteKey(const AName: string);
begin
end;
procedure TX2UtPersistTestFiler.DeleteSection(const AName: string);
begin
end;
{ TPersistTestOutput }
constructor TPersistTestOutput.Create();
begin

View File

@ -10,12 +10,16 @@ type
published
procedure TestIsValidXMLChar;
procedure TestGetValidXMLText;
procedure TestXMLToDateTime;
procedure TestXMLToDate;
procedure TestDateTimeToXML;
procedure TestDateToXML;
end;
implementation
uses
XMLDataBindingUtils;
XMLDataBindingUtils, DateUtils, SysUtils;
{ TXMLDataBindingUtilsTest }
@ -33,6 +37,64 @@ begin
end;
procedure TXMLDataBindingUtilsTest.TestXMLToDateTime;
var
dateInWintertime, dateInSummerTime : TDateTime;
begin
// Local Time
dateInWintertime := EncodeDateTime(2016, 2, 2, 00, 59, 59, 0);
dateInSummerTime := EncodeDateTime(2016, 4, 9, 00, 59, 59, 0);
// Wintertijd
CheckEquals(dateInWintertime, XMLToDateTime('2016-02-01T23:59:59Z', xdtDateTime));
CheckEquals(IncMilliSecond(dateInWintertime, 678), XMLToDateTime('2016-02-01T23:59:59.678Z', xdtDateTime));
CheckEquals(dateInWintertime, XMLToDateTime('2016-02-02T00:59:59+01:00', xdtDateTime));
CheckEquals(IncMilliSecond(dateInWintertime, 678), XMLToDateTime('2016-02-02T00:59:59.678+01:00', xdtDateTime));
// Zomertijd
CheckEquals(dateInSummerTime, XMLToDateTime('2016-04-08T22:59:59Z', xdtDateTime));
CheckEquals(IncMilliSecond(dateInSummerTime, 678), XMLToDateTime('2016-04-08T22:59:59.678Z', xdtDateTime));
CheckEquals(dateInSummerTime, XMLToDateTime('2016-04-09T00:59:59+02:00', xdtDateTime));
CheckEquals(IncMilliSecond(dateInSummerTime, 678), XMLToDateTime('2016-04-09T00:59:59.678+02:00', xdtDateTime));
end;
procedure TXMLDataBindingUtilsTest.TestXMLToDate;
begin
CheckEquals(EncodeDate(2016, 2, 2), XMLToDateTime('2016-02-02', xdtDate));
CheckEquals(EncodeDate(2016, 4, 9), XMLToDateTime('2016-04-09', xdtDate));
end;
procedure TXMLDataBindingUtilsTest.TestDateTimeToXML;
var
dateInWintertime, dateInSummerTime : TDateTime;
begin
dateInWintertime := EncodeDateTime(2016, 2, 1, 14, 59, 59, 0);
dateInSummerTime := EncodeDateTime(2016, 4, 8, 14, 59, 59, 0);
// Wintertijd
CheckEquals('2016-02-01T13:59:59+01:00', DateTimeToXML(XMLToDateTime('2016-02-01T12:59:59Z', xdtDateTime), xdtDateTime, [xtfTimezone]));
CheckEquals('2016-02-01T13:59:59.678+01:00', DateTimeToXML(IncMilliSecond(XMLToDateTime('2016-02-01T12:59:59Z', xdtDateTime), 678), xdtDateTime, [xtfTimezone, xtfMilliseconds]));
CheckEquals('2016-02-01T14:59:59+01:00', DateTimeToXML(dateInWintertime, xdtDateTime, [xtfTimezone]));
CheckEquals('2016-02-01T14:59:59.678+01:00', DateTimeToXML(IncMilliSecond(dateInWintertime, 678), xdtDateTime, [xtfTimezone, xtfMilliseconds]));
// Zomertijd
CheckEquals('2016-04-08T14:59:59+02:00', DateTimeToXML(XMLToDateTime('2016-04-08T12:59:59Z', xdtDateTime), xdtDateTime, [xtfTimezone]));
CheckEquals('2016-04-08T14:59:59.678+02:00', DateTimeToXML(IncMilliSecond(XMLToDateTime('2016-04-08T12:59:59Z', xdtDateTime), 678), xdtDateTime, [xtfTimezone, xtfMilliseconds]));
CheckEquals('2016-04-08T14:59:59+02:00', DateTimeToXML(dateInSummerTime, xdtDateTime, [xtfTimezone]));
CheckEquals('2016-04-08T14:59:59.678+02:00', DateTimeToXML(IncMilliSecond(dateInSummerTime, 678), xdtDateTime, [xtfTimezone, xtfMilliseconds]));
end;
procedure TXMLDataBindingUtilsTest.TestDateToXML;
begin
CheckEquals('2016-02-02', DateTimeToXML(EncodeDate(2016, 2, 2), xdtDate));
CheckEquals('2016-04-09', DateTimeToXML(EncodeDate(2016, 4, 9), xdtDate));
end;
initialization
RegisterTest(TXMLDataBindingUtilsTest.Suite);

View File

@ -18,7 +18,7 @@ type
TXMLDateTimeFormat = (xdtDateTime, xdtDate, xdtTime);
TXMLTimeFragment = (xtfMilliseconds, xtfTimezone);
TXMLTimeFragments = set of TXMLTimeFragment;
TDateConvert = (dcToUtc, dcToLocal);
IXSDValidate = interface
['{3BFDC851-7459-403B-87B3-A52E9E85BC8C}']
@ -197,12 +197,79 @@ begin
end;
function InDSTSpan(ADate: TDateTime; ATimeZoneInfo: TTimeZoneInformation): boolean;
var
lowerDayLight: TDateTime;
upperDayLight: TDateTime;
day: TDate;
days: Integer;
function GetDay(AYear, AMonth, ADay, ADayOfWeek: Integer): TDate;
var
I, Counter : Integer;
begin
Result := 0;
Counter := 0;
days := DaysInAMonth(AYear, AMonth);
for I := 1 to days do
begin
Result := EncodeDate(AYear, AMonth, I);
// Delphi DayOfWeek 1 = Sunday
// TimeZoneInfo.wDayOfWeek 0 = Sunday
if DayOfWeek(Result) -1 = ADayOfWeek then
begin
inc(Counter);
if (counter = ADay) or ((Counter < Aday) and (I >= days - 6)) then
break;
end;
end;
end;
begin
with ATimeZoneInfo.DaylightDate do
begin
day := GetDay(wYear + YearOf(ADate), wMonth, wDay, wDayOfWeek);
lowerDayLight := day + EncodeTime(wHour, wMinute, wSecond, wMilliseconds);
end;
with ATimeZoneInfo.StandardDate do
begin
day := GetDay(wYear + YearOf(ADate), wMonth, wDay, wDayOfWeek);
upperDayLight := day + EncodeTime(wHour, wMinute, wSecond, wMilliseconds);
end;
Result := (ADate >= lowerDayLight) and (ADate <= upperDayLight);
end;
function ConvertDate(ADate: TDateTime; ADateconvert: TDateConvert): TDateTime;
var
timeZone: TTimeZoneInformation;
timeZoneID: Cardinal;
localOffset: Integer;
begin
FillChar(timeZone, SizeOf(TTimeZoneInformation), #0);
timeZoneID := GetTimeZoneInformation(timeZone);
if timeZoneID in [TIME_ZONE_ID_STANDARD, TIME_ZONE_ID_DAYLIGHT] then
localOffset := -timeZone.Bias - IfThen(InDSTSpan(ADate, timeZone), timeZone.DaylightBias, timeZone.StandardBias)
else
localOffset := 0;
if ADateconvert = dcToUtc then
localOffset := localOffset * -1;
Result := IncMinute(ADate, localOffset);
end;
function DateTimeToXML(ADate: TDateTime; AFormat: TXMLDateTimeFormat; ATimeFragments: TXMLTimeFragments): string;
var
formatSettings: TFormatSettings;
timeZone: TTimeZoneInformation;
timeOffset: Integer;
utcDate: TDateTime;
offsetMinutes: Integer;
begin
formatSettings := GetDefaultFormatSettings;
@ -213,21 +280,16 @@ begin
if xtfMilliseconds in ATimeFragments then
Result := Result + FormatDateTime(XMLMsecsFormat, ADate);
if xtfTimezone in ATimeFragments then
if (xtfTimezone in ATimeFragments) then
begin
FillChar(timeZone, SizeOf(TTimeZoneInformation), #0);
if GetTimeZoneInformation(timeZone) <> TIME_ZONE_ID_INVALID then
begin
timeOffset := -timeZone.Bias;
utcDate := ConvertDate(ADate, dcToUtc);
offsetMinutes := MinutesBetween(ADate, utcDate);
if timeOffset = 0 then
Result := Result + XMLTimezoneZulu
else
Result := Result + Format(XMLTimezoneFormat,
[XMLTimezoneSigns[timeOffset > 0],
Abs(timeZone.Bias div 60),
Abs(timeZone.Bias mod 60)]);
end;
if offsetMinutes = 0 then
Result := Result + XMLTimezoneZulu
else
Result := Result + Format(XMLTimezoneFormat,
[XMLTimezoneSigns[offsetMinutes > 0], offsetMinutes div 60, offsetMinutes mod 60]);
end;
end;
end;
@ -252,8 +314,6 @@ var
msec: Integer;
hasTimezone: Boolean;
xmlOffset: Integer;
timeZone: TTimeZoneInformation;
localOffset: Integer;
begin
Result := 0;
@ -326,7 +386,6 @@ begin
if Length(time) > 0 then
begin
hasTimezone := False;
xmlOffset := 0;
if time[1] = XMLTimezoneZulu then
begin
@ -343,18 +402,13 @@ begin
if time[1] = XMLTimezoneSigns[False] then
xmlOffset := -xmlOffset;
Result := IncMinute(Result, - xmlOffset);
end;
end;
if hasTimezone then
begin
FillChar(timeZone, SizeOf(TTimeZoneInformation), #0);
if GetTimeZoneInformation(timeZone) <> TIME_ZONE_ID_INVALID then
begin
localOffset := -timeZone.Bias;
Result := IncMinute(Result, localOffset - xmlOffset);
end;
end;
Result := ConvertDate(Result, dcToLocal);
end;
end;
end;