На этот вопрос уже отвечено, но я добавлю кое-что, что я закончил в аналогичном случае, который у меня был. Из блока XSBuiltIns я нашел метод
function XMLTimeToDateTime(const XMLDateTime: InvString; AsUTCTime: Boolean = False): TDateTime;
что казалось тем, чего я хотел. Я хотел иметь возможность анализировать все различные временные строки XML, определенные здесь:
http://www.w3schools.com/schema/schema_dtypes_date.asp
Сюда входят строки, содержащие только дату, только время или дату и время, и все это с параметрами указанного часового пояса или времени UTC или локального для исходной строки, а также возвращаемое значение в качестве местного времени. Кроме того, когда дано только время, я хотел, чтобы оно всегда находилось в пределах «нулевого дня», то есть после операции вся часть возвращенного TDateTime (приведенного к действительному числу) была равна нулю.
Наконец, я хотел, чтобы функция возвращала DateTime.MinValue при ошибочном вводе (в основном, когда дана пустая строка).
Я не уверен, использовал ли я эту функцию иначе, чем указано, но, по крайней мере, в некоторых местах она, к сожалению, не сработала. Я закончил тем, что сделал свою собственную функцию вокруг этого, которая покрыла все случаи, с которыми я столкнулся, и теперь я в порядке, продвигаясь вперед. Можно утверждать, что, возможно, мне лучше было бы написать весь анализ самостоятельно, так как он не мог быть намного более сложным, чем обходной путь, который я в конечном итоге делал, но по крайней мере в настоящее время Я собираюсь использовать то, что у меня есть, и решил опубликовать это здесь также на тот случай, если кто-то еще сочтет это полезным.
Основные проблемы (я уже могу забыть некоторые):
- В результате пустой строки DateTime соответствует дате в 1-м году, а MinDateTime в Delphi - в 100-м году.
- Строки, содержащие только дату, всегда считаются UTC независимо от наличия или отсутствия «Z» или явного определения часового пояса.
- Строки, содержащие только время, ошибочно идентифицируются как строки даты и неправильно анализируются.
- Модификаторы часовых поясов применяются ТОЛЬКО, если они явно определены, в противном случае предполагается, что все они обозначены как UTC, даже если «Z» отсутствует.
- Дробные секунды не поддерживаются, скорее, миллисекунды всегда конвертируются в 0.
- Поскольку строки only-Time не поддерживаются, мне пришлось добавить в них фиктивную дату, а затем убедиться, что это текущая дата (чтобы охватить проблемы перехода на летнее время при конвертации в / из UTC, что, в свою очередь, пришлось сделать из-за ошибочные соображения UTC) и затем в конце снова вычтите его из результата, и, наконец, в этих случаях обеспечьте требование дня-нуля для строк only-Time.
Конечный результат представляет собой функцию из примерно 100 строк (включая комментарии и т. Д.), Которая использует изрядное количество вспомогательных функций (которые должны быть довольно понятными и которые не являются темой этого сообщения :)). Я скопировал соответствующие биты кода в отдельный файл и юнит-тесты, которые я использовал, чтобы проверить это в другом, я включаю оба ниже. Не стесняйтесь использовать и комментировать по мере необходимости. Обратите внимание, что форма и связанные с ней операции и т. Д. - это как раз то, что Delphi поместил в демонстрационный проект, в котором я ее выполнил, они ни в чем не нужны.
unit Main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, XSBuiltIns, Math, DateUtils;
const
EPSILON = 10e-9;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
{Returns whether the given variable represents negative infinity.}
function IsNegInf(AValue : extended) : boolean;
{Returns whether the given variable represents positive infinity.}
function IsPosInf(AValue : extended) : boolean;
{Checks the less than relation of the given real numbers (R1 < R2), up to
precision EPSILON.}
function RealLessThan(R1, R2 : double) : boolean;
{Checks the greater than or equal to relation of the given real numbers (R1 >= R2),
up to precision EPSILON.}
function RealGreaterThanOrEqualTo(R1, R2 : double) : boolean;
{Checks the less than or equal to relation of the given real numbers (R1 <= R2),
up to precision EPSILON.}
function RealLessThanOrEqualTo(R1, R2 : double) : boolean;
{Return the floor of R, up to precision EPSILON. If Frac(R) < EPSILON, return R.}
function RealFloor(R : extended) : extended;
{Return the floor of R as integer, up to precision EPSILON. If Frac(R) < EPSILON, return R.}
function RealFloorInt(R : extended) : integer;
{Round the value X (properly) to an integer.}
function RoundProper(X : extended) : integer; overload;
function UtcTimeToLocalTime(AUtcTime: TDateTime): TDateTime;
function LocalTimeToUtcTime(ALocalTime: TDateTime): TDateTime;
function CountOccurrences(const SubText: string; const Text: string): Integer;
// Returns a count of the number of occurences of SubText in Text
function XMLTimeStamp2DateTime(TimeStamp : String): TDateTime;
// Parses an XML time stamp string to a TDateTime. All returned times are in
// local time. If time stamp string contains no time stamp definition (either
// explicit time zone info or UTC flag), the time is assumed to be in local time.
// Otherwise the time is parsed as the time zone indicated, and converted to local.
// If no time section is contained in the stamp, the time is assumed to be
// 0:00:00 in the time zone specified (or local time if no specification set).
// If time string is not valid MinDateTime is returned.
var
Form1: TForm1;
implementation
{$R *.dfm}
function XMLTimeStamp2DateTime(TimeStamp : String): TDateTime;
var
HasDateAndTimePart, HasUTCForce, HasExplicitTimeZone, HasDatePart, HasFractionalSeconds: Boolean;
PlusCount, MinusCount, HourOffset, MinuteOffset, FractionIndex, I: Integer;
TimeOffset: TDateTime;
TimeZoneString, TimeZoneDelimiter: string;
Year, Month, Day, MilliSeconds: Word;
YearS, MonthS, DayS, FracSecS: string;
CurrentDate, MSecsFromFractions: TDateTime;
DotSeparatedDecimals: TFormatSettings;
begin
TimeOffset := 0; TimeZoneString := ''; TimeZoneDelimiter := '+';
FractionIndex := Pos('.', TimeStamp);
{$REGION 'Get the fractional seconds as milliseconds'}
HasFractionalSeconds := FractionIndex > 0;
FracSecS := '0.';
if HasFractionalSeconds then
begin
for I := FractionIndex + 1 to Length(TimeStamp) do
begin
if CharInSet(TimeStamp[I], ['0'..'9']) then FracSecS := FracSecS + TimeStamp[I]
else Break;
end;
end else FracSecS := FracSecS + '0';
DotSeparatedDecimals.Create;
DotSeparatedDecimals.DecimalSeparator := '.';
DotSeparatedDecimals.ThousandSeparator := #0;
MilliSeconds := RoundProper(StrToFloatDef(FracSecS, 0, DotSeparatedDecimals) * 1000);
MSecsFromFractions := EncodeTime(0, 0, 0, MilliSeconds);
{$ENDREGION}
MinusCount := CountOccurrences('-', TimeStamp);
HasDatePart := (MinusCount > 1) or (TimeStamp = '');
PlusCount := CountOccurrences('+', TimeStamp);
HasExplicitTimeZone := PlusCount > 0;
if not HasExplicitTimeZone then
begin
HasExplicitTimeZone := Odd(MinusCount); // 1 or 3 minuses => explicit time zone
TimeZoneDelimiter := '-';
end;
if HasExplicitTimeZone then
begin
TimeZoneString := Copy(TimeStamp, LastDelimiter(TimeZoneDelimiter, TimeStamp) + 1, Length(TimeStamp));
// Now TimeZoneString should be of format xx:xx where x's are numbers!
if (Length(TimeZoneString) = 5) and (TimeZoneString[3] = ':') then
begin
HourOffset := StrToIntDef(Copy(TimeZoneString, 1, 2), 0);
MinuteOffset := StrToIntDef(Copy(TimeZoneString, 3, 2), 0);
TimeOffset := EncodeTime(HourOffset, MinuteOffset, 0, 0);
if TimeZoneDelimiter = '-' then TimeOffset := -TimeOffset;
end;
end;
CurrentDate := Now;
Year := 0; Month := 0; Day := 0;
DecodeDate(CurrentDate, Year, Month, Day);
if not HasDatePart then
begin
// Since XMLTimeToDateTime doesn't cope with strings without date part, add
// a dummy one on current date if it doesn't exist - we can't use day zero
// since then the daylight saving time calculation in the LocalTimeToUtcTime
// fixup being possibly done later will go wrong, if local time is in DST
// and day zero is not. So we have to use current day here, then remove it
// from the final result once we're done otherwise.
YearS := IntToStr(Year);
MonthS := IntToStr(Month);
DayS := IntToStr(Day);
while Length(YearS) < 4 do YearS := '0' + YearS;
while Length(MonthS) < 2 do MonthS := '0' + MonthS;
while Length(DayS) < 2 do DayS := '0' + DayS;
TimeStamp := YearS + '-' + MonthS + '-' + DayS + SoapTimePrefix + TimeStamp;
end;
HasDateAndTimePart := Pos(SoapTimePrefix, TimeStamp) > 0;
HasUTCForce := Pos(SLocalTimeMarker, TimeStamp) > 0;
Result := XMLTimeToDateTime(TimeStamp); // This doesn't support fractions of a second!
// Now the conversion is done with zero milliseconds, we need to add the fractions
Result := Result + MSecsFromFractions;
// XMLTimeToDateTime assumes source as UTC when:
// - No time part is defined and one of the following holds:
// - Explicit time zone is defined (to other than UTC) - here it works WRONG!
// - Explicit time zone is NOT defined and UTC flag is NOT defined - here it works WRONG!
// - Explicit UTC flag is defined - here it works CORRECT!
// - Time part is defined and one of the following holds:
// - Explicit time zone is NOT defined and UTC flag is NOT defined - here it works WRONG!
// - Explicit UTC flag is defined - here it works CORRECT!
// In the cases where it works wrong, we need to manually offset its result
// by the local-to-UTC difference.
if (not HasExplicitTimeZone) and (not HasUTCForce) then
Result := LocalTimeToUtcTime(Result)
else if HasExplicitTimeZone and (not HasDateAndTimePart) then
Result := Result - TimeOffset; // Minus to remove the effect of the offset
if not HasDatePart then
begin
// We added the current date to make XMLTimeToDateTime work, now we need to
// remove (the date part of) it back from the end result.
Result := Result - EncodeDate(Year, Month, Day);
// Since there originally was no date part, then there should not be one in
// the end result also, meaning that the result's date should correspond to
// the zero-day.
while RealGreaterThanOrEqualTo(Result, 1) do Result := Result - 1;
while RealLessThan(Result, 0) do Result := Result + 1;
end;
Result := Max(Result, MinDateTime); // In erroneous situations XMLTimeToDateTime returns something less than MinDateTime, which we want as default
end;
{ Returns a count of the number of occurences of SubText in Text }
function CountOccurrences(const SubText: string; const Text: string): Integer;
var
i, j, SubLength: Integer;
First: Char;
begin
Result := 0;
if Length(SubText) <= 0 then Exit;
First := SubText[1];
SubLength := Length(SubText);
for i := 1 to Length(Text) do
begin
if Text[i] = First then
begin
j := 2;
while (j <= SubLength) and (Text[i + j - 1] = SubText[j]) do Inc(j);
if j > SubLength then Inc(result); // Matched all the way
end;
end;
end;
function UtcTimeToLocalTime(AUtcTime: TDateTime): TDateTime;
begin
Result := TTimeZone.Local.ToLocalTime(AUtcTime);
end;
function LocalTimeToUtcTime(ALocalTime: TDateTime): TDateTime;
begin
Result := TTimeZone.Local.ToUniversalTime(ALocalTime);
end;
function RoundProper(X : extended) : integer;
begin
Result := RealFloorInt(0.5 + x);
end;
function RealFloorInt(R : extended) : integer;
begin
Result := Trunc(RealFloor(R));
end;
function RealFloor(R : extended) : extended;
var
FracR : Extended;
begin
Result := R;
FracR := Abs(Frac(R));
if (FracR >= EPSILON) and RealLessThan(FracR, 1) then begin
if Frac(R) > 0 then Result := R - Frac(R)
else Result := R - (1 - Abs(Frac(R)));
end;
end;
function RealLessThan(R1, R2 : double) : boolean;
begin
if IsPosInf(R2) then Result := not IsPosInf(R1)
else if IsNegInf(R2) or IsPosInf(R1) then Result := False
else if IsNegInf(R1) then Result := not IsNegInf(R2)
else // (-Inf, -EPSILON) => Less,
Result := R1 - R2 < -EPSILON; // [-EPSILON, EPSILON] => Equal
end; // (EPSILON, Inf) => Greater
function RealGreaterThanOrEqualTo(R1, R2 : double) : boolean;
begin
if IsPosInf(R1) or IsNegInf(R2) then Result := True
else if IsPosInf(R2) or IsNegInf(R1) then Result := False
else // (-Inf, -EPSILON) => Less,
Result := R1 - R2 > -EPSILON; // [-EPSILON, EPSILON] => Equal
end; // (EPSILON, Inf) => Greater
function RealLessThanOrEqualTo(R1, R2 : double) : boolean;
begin
if IsPosInf(R2) or IsNegInf(R1) then Result := True
else if IsPosInf(R1) or IsNegInf(R2) then Result := False
else // (-Inf, -EPSILON) => Less,
Result := R1 - R2 < EPSILON; // [-EPSILON, EPSILON] => Equal
end; // (EPSILON, Inf) => Greater
function IsPosInf(AValue : extended) : boolean;
begin
Result := IsInfinite(AValue) and (Sign(AValue) = 1);
end;
function IsNegInf(AValue : extended) : boolean;
begin
Result := IsInfinite(AValue) and (Sign(AValue) = -1);
end;
end.
Тогда юнит-тесты здесь:
unit TestMain;
{
Delphi DUnit Test Case
----------------------
This unit contains a skeleton test case class generated by the Test Case Wizard.
Modify the generated code to correctly setup and call the methods from the unit
being tested.
}
interface
uses
TestFramework, System.SysUtils, Vcl.Graphics, XSBuiltIns, Winapi.Windows,
System.Variants, DateUtils, Vcl.Dialogs, Vcl.Controls, Vcl.Forms, Winapi.Messages, Math,
System.Classes, Main;
type
// Test methods for class TForm1
TestTForm1 = class(TTestCase)
strict private
public
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestXMLTimeStamp2DateTime;
end;
implementation
procedure TestTForm1.SetUp;
begin
// Nothing to do here
end;
procedure TestTForm1.TearDown;
begin
// Nothing to do here
end;
procedure TestTForm1.TestXMLTimeStamp2DateTime;
const
TIME_TOLERANCE = 0.0000000115741; // Approximately 1 millisecond, in days
var
Source: string;
ReturnValue, ExpectedValue, Today: TDateTime;
function DateTimeOfToday: TDateTime;
var
Year, Month, Day: Word;
begin
Year := 0; Month := 0; Day := 0;
DecodeDate(Now, Year, Month, Day);
Result := EncodeDate(Year, Month, Day);
end;
begin
Today := DateTimeOfToday; // Counted only once, we ignore the theoretic chance of day changing during the test execution from DST to non-DST or vice versa
{$REGION 'Empty string'}
// Setup method call parameters
Source := '';
ExpectedValue := MinDateTime;
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for empty string should return MinDateTime, but did not!');
{$ENDREGION}
{$REGION 'Date only strings'}
{$REGION 'Date string - local'}
// Setup method call parameters
Source := '2002-09-24';
ExpectedValue := EncodeDate(2002, 9, 24);
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, 'XMLTimeStamp2DateTime for date string - local should return 24.9.2002, but did not!');
{$ENDREGION}
{$REGION 'Date string - UTC'}
// Setup method call parameters
Source := '2002-09-24Z';
ExpectedValue := EncodeDate(2002, 9, 24);
ExpectedValue := UtcTimeToLocalTime(ExpectedValue);
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for date string - UTC should return 24.9.2002 + local time offset, but did not!');
{$ENDREGION}
{$REGION 'Date string - negative offset'}
// Setup method call parameters
Source := '2002-09-24-03:00';
ExpectedValue := EncodeDate(2002, 9, 24);
ExpectedValue := ExpectedValue + EncodeTime(3, 0, 0, 0); // First convert to UTC by removing the offset
ExpectedValue := UtcTimeToLocalTime(ExpectedValue); // Then convert to local from UTC
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for date string - negative offset should return 24.9.2002 + three hours + local time offset, but did not!');
{$ENDREGION}
{$REGION 'Date string - positive offset'}
// Setup method call parameters
Source := '2002-09-24+11:00';
ExpectedValue := EncodeDate(2002, 9, 24);
ExpectedValue := ExpectedValue - EncodeTime(11, 0, 0, 0); // First convert to UTC by removing the offset
ExpectedValue := UtcTimeToLocalTime(ExpectedValue); // Then convert to local from UTC
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for date string - positive offset should return 24.9.2002 - eleven hours + local time offset, but did not!');
{$ENDREGION}
{$ENDREGION}
{$REGION 'Time only strings'}
{$REGION 'Time string - local'}
// Setup method call parameters
Source := '09:30:10';
ExpectedValue := EncodeTime(9, 30, 10, 0);
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for time string - local should return 09:30:10, but did not!');
{$ENDREGION}
{$REGION 'Time string - UTC'}
// Setup method call parameters
Source := '09:30:10Z';
// Have to add Today for the UtcTimeToLocalTime call to have correct DST
// - then have to remove Today again away to have correct zero-day date
ExpectedValue := Today + EncodeTime(9, 30, 10, 0);
ExpectedValue := UtcTimeToLocalTime(ExpectedValue);
ExpectedValue := ExpectedValue - Today;
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for time string - UTC should return 09:30:10 + local time offset, but did not!');
{$ENDREGION}
{$REGION 'Time string - negative offset'}
// Setup method call parameters
Source := '09:30:10-03:00';
// Have to add Today for the UtcTimeToLocalTime call to have correct DST
// - then have to remove Today again away to have correct zero-day date
ExpectedValue := Today + EncodeTime(9, 30, 10, 0);
ExpectedValue := ExpectedValue + EncodeTime(3, 0, 0, 0); // First convert to UTC by removing the offset
ExpectedValue := UtcTimeToLocalTime(ExpectedValue); // Then convert to local from UTC
ExpectedValue := ExpectedValue - Today;
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for time string - negative offset should return 09:30:10 + three hours + local time offset, but did not!');
{$ENDREGION}
{$REGION 'Time string - positive offset over date line'}
// Setup method call parameters
Source := '06:30:10+11:00';
// Have to add Today for the UtcTimeToLocalTime call to have correct DST
// - then have to remove Today again away to have correct zero-day date
ExpectedValue := Today + EncodeTime(6, 30, 10, 0);
ExpectedValue := ExpectedValue - EncodeTime(11, 0, 0, 0); // First convert to UTC by removing the offset
ExpectedValue := UtcTimeToLocalTime(ExpectedValue); // Then convert to local from UTC
ExpectedValue := ExpectedValue - Today;
if RealGreaterThanOrEqualTo(ExpectedValue, 1) then ExpectedValue := ExpectedValue - 1; // When having time only, date should always be zero!
if RealLessThan(ExpectedValue, 0) then ExpectedValue := ExpectedValue + 1; // When having time only, date should always be zero!
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for time string - positive offset (over day change) should return 06:30:10 - eleven hours + local time offset (modulo 24 hours), but did not!');
{$ENDREGION}
{$REGION 'Fractional time string with negative offset over date line'}
// Setup method call parameters
Source := '14:30:10.25-11:00';
// Have to add Today for the UtcTimeToLocalTime call to have correct DST
// - then have to remove Today again away to have correct zero-day date
ExpectedValue := Today + EncodeTime(14, 30, 10, 250);
ExpectedValue := ExpectedValue + EncodeTime(11, 0, 0, 0); // First convert to UTC by removing the offset
ExpectedValue := UtcTimeToLocalTime(ExpectedValue); // Then convert to local from UTC
ExpectedValue := ExpectedValue - Today;
if RealGreaterThanOrEqualTo(ExpectedValue, 1) then ExpectedValue := ExpectedValue - 1; // When having time only, date should always be zero!
if RealLessThanOrEqualTo(ExpectedValue, 0) then ExpectedValue := ExpectedValue + 1; // When having time only, date should always be zero!
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for fractional time string - negative offset (over day change) should return 14:30:10.25 + eleven hours + local time offset (modulo 24 hours), but did not!');
{$ENDREGION}
{$ENDREGION}
{$REGION 'Date and time strings}
{$REGION 'Date and time string - local'}
// Setup method call parameters
Source := '2002-09-24T09:30:10.25';
ExpectedValue := EncodeDate(2002, 9, 24) + EncodeTime(9, 30, 10, 250);
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for date and time string - local should return 24.9.2002 09:30:10.25, but did not!');
{$ENDREGION}
{$REGION 'Date and time string - UTC'}
// Setup method call parameters
Source := '2002-09-24T09:30:10.25Z';
ExpectedValue := EncodeDate(2002, 9, 24) + EncodeTime(9, 30, 10, 250);
ExpectedValue := UtcTimeToLocalTime(ExpectedValue);
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for date and time string - UTC should return 24.9.2002 09:30:10.25 + local time offset, but did not!');
{$ENDREGION}
{$REGION 'Date and time string - positive offset over date line'}
// Setup method call parameters
Source := '2002-09-24T06:30:10.25+11:00';
ExpectedValue := EncodeDate(2002, 9, 24) + EncodeTime(6, 30, 10, 250);
ExpectedValue := ExpectedValue - EncodeTime(11, 0, 0, 0); // First convert to UTC by removing the offset
ExpectedValue := UtcTimeToLocalTime(ExpectedValue); // Then convert to local from UTC
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for date and time string - positive offset (over day change) should return 24.9.2002 06:30:10.25 - eleven hours + local time offset, but did not!');
{$ENDREGION}
{$REGION 'Date and time string - negative offset over date line'}
// Setup method call parameters
Source := '2002-09-24T14:30:10.25-11:00';
ExpectedValue := EncodeDate(2002, 9, 24) + EncodeTime(14, 30, 10, 250);
ExpectedValue := ExpectedValue + EncodeTime(11, 0, 0, 0); // First convert to UTC by removing the offset
ExpectedValue := UtcTimeToLocalTime(ExpectedValue); // Then convert to local from UTC
// Call the method
ReturnValue := XMLTimeStamp2DateTime(Source);
// Validate method results
CheckEquals(ExpectedValue, ReturnValue, TIME_TOLERANCE, 'XMLTimeStamp2DateTime for date and time string - negative offset (over day change) should return 14:30:10.25 + eleven hours + local time offset, but did not!');
{$ENDREGION}
{$ENDREGION}
end;
initialization
// Register any test cases with the test runner
RegisterTest(TestTForm1.Suite);
end.