代码之家  ›  专栏  ›  技术社区  ›  Charles Faiga

在Delphi中,有一个将XML日期和时间转换为TDateTime的函数吗

  •  21
  • Charles Faiga  · 技术社区  · 17 年前

    XML日期和时间的格式为

    '-'? yyyy'-'mm'-'dd'T'hh':'mm':'ss('.'s+)?(zzzz)??

    '-'? yyyy是表示年份的四位或四位以上数字(可选负号数字);如果超过四位,则禁止前导零,并且禁止使用“0000”

    其余的“-”是日期部分各部分之间的分隔符;

    第一个mm是表示月份的两位数字;

    dd是一个两位数的数字,表示一天;

    “T”是一个分隔符,表示一天中的时间紧随其后;

    hh是表示小时的两位数字;'如果表示的分钟和秒数为零,并且这样表示的dateTime值是第二天的第一个瞬间(值空间中dateTime对象的hour属性的值不能大于23),则允许使用24';

    “:”是一天中部分时间部分之间的分隔符;

    '.'s+(如果存在)表示分数秒;

    简单例子

    更复杂的例子

    2002-10-10T12:00:00-05:00 (2002年10月10日中午,美国中央夏令时和东部标准时间)为 2002-10-10T17:00:00Z .

    www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html 更多信息

    3 回复  |  直到 17 年前
        1
  •  45
  •   skamradt    17 年前

    德尔福有一个 XSBuiltIns 包含可帮助您转换某些XML数据类型的数据类型的单元(自Delphi 6起):

    TXSDecimal ,你明白了)

    所有这些方法至少包含以下两种方法:

    您可以这样使用它:

    with TXSDateTime.Create() do
      try
        AsDateTime := ClientDataSetParam.AsDateTime; // convert from TDateTime
        Attribute.DateTimeValue := NativeToXS; // convert to WideString
      finally
        Free;
      end;
    
    with TXSDateTime.Create() do
      try
        XSToNative(XmlAttribute.DateTimeValue); // convert from WideString
        CurrentField.AsDateTime := AsDateTime; // convert to TDateTime
      finally
        Free;
      end;
    

    那会让你走的。

    --杰罗恩

        2
  •  15
  •   DelphiUser    13 年前

    这个问题已经得到了很好的回答,但我会在这里补充一些我在类似案例中所做的事情。从XSBuiltIns单元中,我找到了方法

    function XMLTimeToDateTime(const XMLDateTime: InvString; AsUTCTime: Boolean = False): TDateTime;
    

    http://www.w3schools.com/schema/schema_dtypes_date.asp

    这包括仅包含日期、仅包含时间或同时包含日期和时间的字符串,所有这些字符串都具有指定时区、UTC时间或源字符串的本地选项,并返回值作为本地时间。另外,当只给出一个时间时,我希望它总是在“零天”之内,即在操作之后,返回的TDateTime(转换为实数)的整个部分都是零。

    最后,我希望函数在错误输入时返回DateTime.MinValue(主要是在给定空字符串时)。

    • 空字符串导致DateTime对应于第1年的日期,而Delphi的MinDateTime对应于第100年。
    • 无论是否存在“Z”或明确的时区定义,只有日期的字符串始终被视为UTC。
    • 时区修饰符仅在明确定义的情况下应用,否则所有修饰符都假定为UTC,即使没有“Z”。
    • 由于仅时间字符串不受支持,我必须为它们添加一个虚拟日期,然后确保它是当前日期(在转换为UTC或从UTC转换时涵盖DST问题,这反过来又是由于错误的UTC考虑而必须完成的),然后再从结果中减去它,最后,在这些情况下,确保只对时间字符串执行零日要求。

    最终的结果是一个大约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.
    
        3
  •  5
  •   gabr    17 年前

    OmniXML

    function XMLStrToDateTime(nodeValue: XmlString; var value: TDateTime): boolean; overload;
    function XMLStrToDateTime(nodeValue: XmlString): TDateTime; overload;
    function XMLStrToDateTimeDef(nodeValue: XmlString; defaultValue: TDateTime): TDateTime;
    function XMLStrToDate(nodeValue: XmlString; var value: TDateTime): boolean; overload;
    function XMLStrToDate(nodeValue: XmlString): TDateTime; overload;
    function XMLStrToDateDef(nodeValue: XmlString; defaultValue: TDateTime): TDateTime;
    function XMLStrToTime(nodeValue: XmlString; var value: TDateTime): boolean; overload;
    function XMLStrToTime(nodeValue: XmlString): TDateTime; overload;
    function XMLStrToTimeDef(nodeValue: XmlString; defaultValue: TDateTime): TDateTime;
    
    function XMLDateTimeToStr(value: TDateTime): XmlString;
    function XMLDateTimeToStrEx(value: TDateTime): XmlString;
    function XMLDateToStr(value: TDateTime): XmlString;
    function XMLTimeToStr(value: TDateTime): XmlString;