2017-09-28 12 views
0

J'ai une ancienne application. Il a une date au format localisé stockée dans ses données. Cette chaîne était uniquement utilisée pour l'affichage, il était donc acceptable de l'avoir sous forme localisée. Maintenant, nous devons le réutiliser en tant que TDateTime. Cela a semblé être simple: parce que nous avons obtenu la chaîne de DateToStr, nous la convertirons en utilisant StrToDate. J'ai donc écrit un petit programme de console pour vérifier:StrToDate ne peut pas convertir la valeur obtenue à partir de DateToStr

program Project1; 

{$APPTYPE CONSOLE} 

{$R *.res} 

uses 
    System.SysUtils; 
var 
    S:String; 
    D: TDateTime; 
begin 
    S := DateToStr(Now); 
    Writeln(S); 
    D := StrToDate(S); //! throws an EConvertError 
    Readln; 
end. 

Il jette un EConvertError: Projet Project1.exe élevé EConvertError classe d'exception avec le message « '28. 9. 2017 'n'est pas une date valide'. Ceci est incorrect, la date mentionnée dans l'exception est valide! Il a été généré via DateToStr il y a un instant. Cela n'a aucun sens pour moi. Serait-ce un bug dans Windows 10?

+2

Comment cela échoue-t-il? Est-ce qu'il lance une erreur? La sortie est-elle inattendue? – PrestonM

+0

Cela fonctionne. Rassurez-vous, c'est le cas. –

+4

[mcve] s'il vous plaît .. –

Répondre

4

Je peux dupliquer votre problème si j'entre dd. M.yyyy pour le format de date courte dans les paramètres régionaux du système d'exploitation. Ou dd. M. yyyy pour avoir le message d'erreur exact que vous avez cité dans la question, notez les espaces dans les chaînes de format.

La raison StrToDate échoue est que la fonction ScanDate dans "sysutils.pas" échoue et renvoie False. La raison pour laquelle ScanDate échoue est un séparateur de date incorrect dans l'enregistrement de paramètres de format par défaut. RTL récupère le séparateur de date avec le code ci-dessous, dans lequel LOCALE_SDATE est passé pour le paramètre LocaleType et "/" est passé pour le paramètre Default.

... 
var 
    Buffer: array[0..1] of Char; 
begin 
    if GetLocaleInfo(Locale, LocaleType, Buffer, 2) > 0 then 
    Result := Buffer[0] else 
    Result := Default; 

Dans le cas de votre format de date le tampon requis est de 3 caractères, puisque le RTL ne fournit que 2 caractères l'API échoue avec ERROR_INSUFFICIENT_BUFFER et la fonction ci-dessus, non concerné par fail raison, retourne le séparateur de date par défaut de "/". Donc, ceci est une erreur dans le RTL Delphi.

Si vous avez un cas d'utilisation valide pour votre chaîne de format impair, utilisez la surcharge StrToDate qui reçoit un paramètre de paramètres de format. Sinon, corrigez simplement vos chaînes de date dans les paramètres régionaux.

Malheureusement, vous ne pouvez pas appliquer une solution générique pour la première alternative. C'est que vous pouvez récupérer le séparateur de date correcte avec, par exemple, comme ceci:

function GetDateSeparator: string; 
var 
    NumChars: Integer; 
begin 
    NumChars := GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, nil, 0); 
    Win32Check(NumChars <> 0); 
    SetLength(Result, NumChars); 
    Win32Check(GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, PChar(Result), NumChars) <> 0); 
    SetLength(Result, Length(Result) - 1); 
end; 

Mais vous ne pouvez pas affecter le retour '. ' au DateSeparator d'un TFormatSettings, parce qu'elle attend un char. Vous devez faire comme si vous saviez que le séparateur est un '.' et l'utiliser, et cela ne fonctionne que parce que le second caractère est un espace qui est dépouillé dans ScanDate.

+0

J'ai écrit une fonction pour éviter ce comportement étrange de RTL: – Lubos

+0

Vous vouliez coller ma solution, mais les commentaires ne sont probablement pas destinés à cela? Cette page n'est pas conviviale. La modification de commentaire est une multiligne, donc je m'attendais à la capacité d'écrire du texte en plusieurs lignes, mais après avoir appuyé sur Entrée, le commentaire a été sauvegardé. C'est inutile mais je suis incapable de le supprimer, désolé. – Lubos

+0

@Lubos - Vous pouvez également utiliser 'VarToDateTime' (variantes), ou' VarDateFromStr' (varutils) directement qui appelle l'API pour effectuer la conversion. –