Lose milliseconds on DateToISO8601

497 views Asked by At

A lot of APIs want the ISO8601 without the milliseconds like this:

Get only orders which were placed after this timestamp. Should be in YYYY-MM-ddTHH:mm:ssZ format

I guess the main "issue" is the dot after the seconds (between seconds and milliseconds), one would have to url-encode the dot (http GET speaking), right?

SDateFormat: string = 'yyyy''-''mm''-''dd''T''hh'':''nn'':''ss''.''zzz''Z'''; { Do not localize }

I am unable to lose the milliseconds.

DateToISO8601(TTimeZone.Local.ToUniversalTime(RecodeMilliSecond(now, 0), false), true)

This is my approach at the moment:

var
  utc: TDateTime;
...
  utc := TTimeZone.Local.ToUniversalTime(now);
  utc := RecodeMilliSecond(utc, 0);
  ... Format('/orders?storefront=de&ts_created_from_iso=%sT%sZ', [FormatDateTime('yyyy-mm-dd', utc), FormatDateTime('hh:nn:ss', utc)])

Any other ideas?

3

There are 3 answers

1
Remy Lebeau On BEST ANSWER

If I'm understanding your question correctly, you want to know how to make DateToISO8601() not output milliseconds, correct? The answer is, you can't. But, it would be very easy to strip off the milliseconds after the fact using System.Delete() or TStringHelper.Remove() since you know the exact offset and length of the milliseconds in the resulting string.

var
  utc: TDateTime;
  iso: string;
... 
  utc := TTimeZone.Local.ToUniversalTime(Now);
  iso := DateToISO8601(utc, true);
  Delete(iso, 20, 4);
  ... '/orders?storefront=de&ts_created_from_iso=' + iso;

Otherwise, just stick with your manually approach. However, you don't need RecodeMilliseconds(), and only 1 call to FormatDateTime() will suffice:

const
  cISOFormat: string = 'yyyy''-''mm''-''dd''T''hh'':''nn'':''ss''Z''';
var
  utc: TDateTime;
...
  utc := TTimeZone.Local.ToUniversalTime(Now);
  ... '/orders?storefront=de&ts_created_from_iso=' + FormatDateTime(cISOFormat, utc);
1
Rob Lambden On

This is a routine we use:

  function DateTimeToXML(dtInput: TDateTime): String;
  var
    fmt: TFormatSettings;
  begin
    // If this will be used in a multithreaded environment then
    // you should use your own TFormatSettings to be thread safe
    fmt:=TFormatSettings.Create();      // initialize, it's on the stack so MUST NOT be freed
    DateTimeToString(Result, 'yyyy-mm-dd''T''hh:nn:ss''Z''',
                     TTimeZone.Local.ToUniversalTime(dtInput), fmt);
  end;
0
Pieter On

you can copy the function DateToISO8601 from System.DateUtils and change it to:

function [MyClassName].DateToISO8601(const ADate: TDateTime; AInputIsUTC: Boolean = true): string;
const
  SDateFormat: string = '%.4d-%.2d-%.2dT%.2d:%.2d:%.2dZ'; { Do not localize }
  SOffsetFormat: string = '%s%s%.02d:%.02d'; { Do not localize }
  Neg: array[Boolean] of string = ('+', '-'); { Do not localize }
var
  y, mo, d, h, mi, se, ms: Word;
  Bias: Integer;
  TimeZone: TTimeZone;
begin
  DecodeDate(ADate, y, mo, d);
  DecodeTime(ADate, h, mi, se, ms);
  Result := Format(SDateFormat, [y, mo, d, h, mi, se]);
  if not AInputIsUTC then
  begin
    TimeZone := TTimeZone.Local;
    Bias := Trunc(TimeZone.GetUTCOffset(ADate).Negate.TotalMinutes);
    if Bias <> 0 then
    begin
      // Remove the Z, in order to add the UTC_Offset to the string.
      SetLength(Result, Result.Length - 1);
      Result := Format(SOffsetFormat, [Result, Neg[Bias > 0], Abs(Bias) div 
MinsPerHour,
        Abs(Bias) mod MinsPerHour]);
    end
  end;
end;