While DateTimeToRFC3339 produces a string that contains milliseconds, like e.g. "2020-10-13T02:19:51.237Z", the conversion back returns a TDateTime in which the milliseconds are 0, which when again converted to string returns "2020-10-13T02:19:51.000Z".
Looking into the code of DateUtils.TryRFC3339ToDateTime shows that there is no handling of milliseconds at all, and this causes the milliseconds to get cut off.
Function TryRFC3339ToDateTime(const Avalue: String; out ADateTime: TDateTime): Boolean;
// 1 2
// 12345678901234567890123
// yyyy-mm-ddThh:nn:ss.zzz
Type
TPartPos = (ppTime,ppYear,ppMonth,ppDay,ppHour,ppMinute,ppSec);
TPos = Array [TPartPos] of byte;
Const
P : TPos = (11,1,6,9,12,15,18);
var
lY, lM, lD, lH, lMi, lS: Integer;
begin
if Trim(AValue) = '' then
begin
Result:=True;
ADateTime:=0;
end;
lY:=StrToIntDef(Copy(AValue,P[ppYear],4),-1);
lM:=StrToIntDef(Copy(AValue,P[ppMonth],2),-1);
lD:=StrToIntDef(Copy(AValue,P[ppDay],2),-1);
if (Length(AValue)>=P[ppTime]) then
begin
lH:=StrToIntDef(Copy(AValue,P[ppHour],2),-1);
lMi:=StrToIntDef(Copy(AValue,P[ppMinute],2),-1);
lS:=StrToIntDef(Copy(AValue,P[ppSec],2),-1);
end
else
begin
lH:=0;
lMi:=0;
lS:=0;
end;
Result:=(lY>=0) and (lM>=0) and (lD>=0) and (lH>=0) and (lMi>=0) and (ls>=0);
if Not Result then
ADateTime:=0
else
{ Cannot EncodeDate if any part equals 0. EncodeTime is okay. }
if (lY = 0) or (lM = 0) or (lD = 0) then
ADateTime:=EncodeTime(lH, lMi, lS, 0)
else
ADateTime:=EncodeDate(lY, lM, lD) + EncodeTime(lH, lMi, lS, 0);
end;