Comment convertir une chaîne ISO 8601 en Delphidate?
DateTimeToString(result, 'yyyy-mm-dd', myDate);
Quelle est la façon idiomatique de faire la conversion inverse? StringToDateTime()
ne semble pas exister.
évidemment je peux le faire à la manière "dure" en analysant manuellement la chaîne et en encodant le résultat, mais cela semble un mauvais choix.
7 réponses
pourquoi réinventer la roue?
le XML utilise la norme ISO 8601 pour le stockage de la date et de l'heure.
Delphi a intégré un support pour cela depuis Delphi 6 dans le XSBuiltIns unité.
Cette réponse explique comment pour DateTime, ceci est pour Date seulement en utilisant le TXSDate catégorie:
with TXSDate.Create() do
try
AsDate := Date; // convert from TDateTime
DateString := NativeToXS; // convert to WideString
finally
Free;
end;
with TXSDate.Create() do
try
XSToNative(DateString); // convert from WideString
Date := AsDate; // convert to TDateTime
finally
Free;
end;
à partir de XE8, utilisez ISO8601ToDate
(et DateToISO8601
)dateutils.pas
.
http://docwiki.embarcadero.com/Libraries/XE8/en/System.DateUtils.ISO8601ToDate
vous pouvez trouver des routines de conversion Iso-8601 dans notre SynCommons unit.
il a été profondément optimisé pour la vitesse, donc il est beaucoup plus rapide que les fonctions DateTimeToString() et autres, mais bien sûr, le code est plus difficile à suivre. ;)
procedure Iso8601ToDateTimePUTF8CharVar(P: PUTF8Char; L: integer; var result: TDateTime);
var i: integer;
B: cardinal;
Y,M,D, H,MI,SS: cardinal;
// we expect 'YYYYMMDDThhmmss' format but we handle also 'YYYY-MM-DD hh:mm:ss'
begin
result := 0;
if P=nil then
exit;
if L=0 then
L := StrLen(P);
if L<4 then
exit; // we need 'YYYY' at least
if P[0]='T' then
dec(P,8) else begin
B := ConvertHexToBin[ord(P[0])]; // first digit
if B>9 then exit else Y := B; // fast check '0'..'9'
for i := 1 to 3 do begin
B := ConvertHexToBin[ord(P[i])]; // 3 other digits
if B>9 then exit else Y := Y*10+B;
end;
if P[4] in ['-','/'] then begin inc(P); dec(L); end; // allow YYYY-MM-DD
D := 1;
if L>=6 then begin // YYYYMM
M := ord(P[4])*10+ord(P[5])-(48+480);
if (M=0) or (M>12) then exit;
if P[6] in ['-','/'] then begin inc(P); dec(L); end; // allow YYYY-MM-DD
if L>=8 then begin // YYYYMMDD
D := ord(P[6])*10+ord(P[7])-(48+480);
if (D=0) or (D>MonthDays[true][M]) then exit; // worse is leap year=true
end;
end else
M := 1;
if M>2 then // inlined EncodeDate(Y,M,D)
dec(M,3) else
if M>0 then begin
inc(M,9);
dec(Y);
end;
with Div100(Y) do
result := (146097*YDiv100) shr 2 + (1461*YMod100) shr 2 +
(153*M+2) div 5+D-693900;
if (L<15) or not(P[8] in [' ','T']) then
exit;
end;
H := ord(P[9])*10+ord(P[10])-(48+480);
if P[11]=':' then inc(P); // allow hh:mm:ss
MI := ord(P[11])*10+ord(P[12])-(48+480);
if P[13]=':' then inc(P); // allow hh:mm:ss
SS := ord(P[13])*10+ord(P[14])-(48+480);
if (H<24) and (MI<60) and (SS<60) then // inlined EncodeTime()
result := result + (H * (MinsPerHour * SecsPerMin * MSecsPerSec) +
MI * (SecsPerMin * MSecsPerSec) + SS * MSecsPerSec) / MSecsPerDay;
end;
ceci est capable de gérer une conversion très rapide d'un tampon encodé UTF-8 vers un TDateTime
. Pour toutes les dépendances de constantes, vérifiez le code source de l'unité.
je pense que cela devrait fonctionner... la documentation dit la version surchargée de ces méthodes est pour une utilisation dans les threads, mais elle peut être utile pour spécifier les paramètres de format que vous souhaitez utiliser à l'époque.
Function ISO8601ToDateTime(Value: String):TDateTime;
var
FormatSettings: TFormatSettings;
begin
GetLocaleFormatSettings(GetThreadLocale, FormatSettings);
FormatSettings.DateSeparator := '-';
FormatSettings.ShortDateFormat := 'yyyy-MM-dd';
Result := StrToDate(Value, FormatSettings);
end;
Vous pouvez bien sûr écrire des variantes de cela avec Strtodateef et TryStrToDate avec la fonctionnalité équivalente
pour plus de flexibilité, vous pouvez considérer Marco van de Voort scandate routine qui s'occupe de votre chaîne dans n'importe quel format:
var
D: TDateTime;
begin
D := ScanDate('yyyy-mm-dd', '2011-07-11');
Voir version finale (7KB .zip) ajouté à la FPC.
USES Soap.XSBuiltIns;
...
Function XMLDateTimeToLocalDateTime(const Value: String): TDateTime;
begin
with TXSDateTime.Create do
try
XSToNative(Value);
result := AsDateTime;
finally
Free;
end;
end;
Delphi XE3
à partir de XE6 vous pouvez utiliser la fonction System.DateUtils.ISO8601ToDate
:
uses
System.DateUtils;
var
vDat: TDateTime;
begin
vDat := ISO8601ToDate('2018-03-26T11:01:35.867Z');
end.