Comme je l'expliquerai plus tard, je privilégierais toujours la méthode TryParse
y TryParseExact
méthodes. Parce qu'elles sont un peu encombrantes à utiliser, j'ai rédigé une méthode d'extension ce qui rend l'analyse syntaxique beaucoup plus facile :
var dtStr = "2011-03-21 13:26";
DateTime? dt = dtStr.ToDate("yyyy-MM-dd HH:mm");
Ou plus simplement, si vous voulez utiliser implicitement les modèles de dates de votre culture actuelle, vous pouvez l'utiliser comme suit :
DateTime? dt = dtStr.ToDate();
Dans ce cas, il n'est pas nécessaire de spécifier un modèle particulier.
Contrairement à Parse
, ParseExact
etc., il ne lève pas d'exception et vous permet de vérifier par le biais de
if (dt.HasValue) { // continue processing } else { // do error handling }
si la conversion a réussi (dans ce cas dt
a une valeur à laquelle vous pouvez accéder via dt.Value
) ou non (dans ce cas, il s'agit de null
).
Cela permet même d'utiliser des raccourcis élégants comme l'opérateur "Elvis". ?.
par exemple :
int? year = dtStr?.ToDate("yyyy-MM-dd HH:mm")?.Year;
Ici, vous pouvez également utiliser year.HasValue
pour vérifier si la conversion a réussi, et si elle n'a pas réussi alors year
contiendra null
sinon la partie année de la date. Aucune exception n'est levée si la conversion échoue.
Solution : La méthode d'extension .ToDate()
Essayez-le dans .NetFiddle
public static class Extensions
{
/// Extension method parsing a date string to a DateTime? <para/>
/// <summary>
/// </summary>
/// <param name="dateTimeStr">The date string to parse</param>
/// <param name="dateFmt">dateFmt is optional and allows to pass
/// a parsing pattern array or one or more patterns passed
/// as string parameters</param>
/// <returns>Parsed DateTime or null</returns>
public static DateTime? ToDate(this string dateTimeStr, params string[] dateFmt)
{
// example: var dt = "2011-03-21 13:26".ToDate(new string[]{"yyyy-MM-dd HH:mm",
// "M/d/yyyy h:mm:ss tt"});
// or simpler:
// var dt = "2011-03-21 13:26".ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");
const DateTimeStyles style = DateTimeStyles.AllowWhiteSpaces;
if (dateFmt == null)
{
var dateInfo = System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat;
dateFmt=dateInfo.GetAllDateTimePatterns();
}
var result = DateTime.TryParseExact(dateTimeStr, dateFmt, CultureInfo.InvariantCulture,
style, out var dt) ? dt : null as DateTime?;
return result;
}
}
Quelques informations sur le code
Vous vous demandez peut-être pourquoi j'ai utilisé InvariantCulture
en appelant TryParseExact
: Ceci est pour forcer la fonction à traiter les modèles de format toujours de la même manière (autrement, par exemple, "." pourrait être interprété comme un séparateur décimal en anglais alors qu'il s'agit d'un séparateur de groupe). ou un séparateur de date en allemand). Rappelez-vous que nous avons déjà interrogé les chaînes de format basées sur la culture quelques lignes auparavant, donc c'est correct ici.
Mise à jour : .ToDate()
(sans paramètres) utilise désormais par défaut tous les modèles de date/heure courants de la culture actuelle du fil de discussion.
Note que nous avons besoin de la result
y dt
ensemble, parce que TryParseExact
ne permet pas d'utiliser DateTime?
que nous avons l'intention de retourner. Sur C# Version 7 vous pourriez simplifier le ToDate
un peu comme suit :
// in C#7 only: "DateTime dt;" - no longer required, declare implicitly
if (DateTime.TryParseExact(dateTimeStr, dateFmt,
CultureInfo.InvariantCulture, style, out var dt)) result = dt;
ou, si vous l'aimez encore plus court :
// in C#7 only: Declaration of result as a "one-liner" ;-)
var result = DateTime.TryParseExact(dateTimeStr, dateFmt, CultureInfo.InvariantCulture,
style, out var dt) ? dt : null as DateTime?;
dans ce cas, vous n'avez pas besoin des deux déclarations DateTime? result = null;
y DateTime dt;
du tout - vous pouvez le faire en une seule ligne de code. (Il serait également permis d'écrire out DateTime dt
au lieu de out var dt
si vous préférez).
L'ancien style de C# l'aurait exigé de la manière suivante (j'ai supprimé cela du code ci-dessus) :
// DateTime? result = null;
// DateTime dt;
// if (DateTime.TryParseExact(dateTimeStr, dateFmt,
// CultureInfo.InvariantCulture, style, out dt)) result = dt;
J'ai encore simplifié le code en utilisant le params
mot clé : maintenant vous n'avez pas besoin des 2 et plus de méthode surchargée.
Exemple d'utilisation
var dtStr="2011-03-21 13:26";
var dt=dtStr.ToDate("yyyy-MM-dd HH:mm");
if (dt.HasValue)
{
Console.WriteLine("Successful!");
// ... dt.Value now contains the converted DateTime ...
}
else
{
Console.WriteLine("Invalid date format!");
}
Comme vous pouvez le voir, cet exemple ne fait qu'interroger dt.HasValue
pour savoir si la conversion a réussi ou non. En prime, TryParseExact permet de spécifier des règles strictes pour la conversion. DateTimeStyles
afin de savoir exactement si une chaîne de date/heure correcte a été transmise ou non.
Autres exemples d'utilisation
La fonction surchargée vous permet de passer une fonction tableau des formats valides utilisé pour l'analyse/conversion des dates comme indiqué aquí également ( TryParseExact
le prend directement en charge), par exemple
string[] dateFmt = {"M/d/yyyy h:mm:ss tt", "M/d/yyyy h:mm tt",
"MM/dd/yyyy hh:mm:ss", "M/d/yyyy h:mm:ss",
"M/d/yyyy hh:mm tt", "M/d/yyyy hh tt",
"M/d/yyyy h:mm", "M/d/yyyy h:mm",
"MM/dd/yyyy hh:mm", "M/dd/yyyy hh:mm"};
var dtStr="5/1/2009 6:32 PM";
var dt=dtStr.ToDate(dateFmt);
Si vous ne disposez que de quelques modèles, vous pouvez également écrire :
var dateStr = "2011-03-21 13:26";
var dt = dateStr.ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");
Exemples avancés
Vous pouvez utiliser le ??
pour utiliser par défaut un format de sécurité, par ex.
var dtStr = "2017-12-30 11:37:00";
var dt = (dtStr.ToDate()) ?? dtStr.ToDate("yyyy-MM-dd HH:mm:ss");
Dans ce cas, le .ToDate()
utiliserait les formats de date courants de la culture locale, et si tout cela échouait, il essaierait d'utiliser l'option norme ISO format "yyyy-MM-dd HH:mm:ss"
comme solution de repli. De cette façon, la fonction d'extension permet de "chaîner" facilement différents formats de repli.
Vous pouvez même utiliser l'extension dans LINQ, essayez ceci (c'est dans le .NetFiddle ci-dessus) :
var strDateArray = new[] { "15-01-2019", "15.01.2021" };
var patterns=new[] { "dd-MM-yyyy", "dd.MM.yyyy" };
var dtRange = strDateArray.Select(s => s.ToDate(patterns));
dtRange.Dump();
qui convertira les dates du tableau à la volée en utilisant les modèles et les affichera sur la console.
Quelques informations sur TryParseExact
Enfin, voici quelques commentaires sur le contexte (c'est-à-dire la raison pour laquelle je l'ai écrit de cette façon) :
Je préfère TryParseExact dans cette méthode d'extension, car vous éviter le traitement des exceptions - vous pouvez lu dans l'article d'Eric Lippert sur les exceptions pourquoi vous devriez utiliser TryParse plutôt que Parse, je le cite à ce sujet : 2)
Ce site une décision de conception malheureuse 1) [annotation : pour laisser la méthode Parse lever une exception] était si contrariant que, bien sûr, il n'y avait pas d'autre solution. l'équipe des cadres a implémenté TryParse peu de temps après qui fait ce qu'il faut.
C'est le cas, mais TryParse
y TryParseExact
les deux sont encore bien moins confortables à utiliser : Elles vous obligent à utiliser une variable non initialisée comme une out
qui ne doit pas être nullable et pendant que vous convertissez, vous devez évaluer la valeur de retour booléenne - soit vous devez utiliser un paramètre de type if
ou vous devez stocker la valeur de retour dans une variable booléenne supplémentaire afin de pouvoir effectuer la vérification ultérieurement. Et vous ne pouvez pas simplement utiliser la variable cible sans savoir si la conversion a réussi ou non.
Dans la plupart des cas, vous voulez juste savoir si la conversion a réussi ou non (et bien sûr la valeur si elle a réussi) donc un variable cible annulable qui conserve toutes les informations serait souhaitable et beaucoup plus élégante - car l'ensemble des informations est simplement stocké en un seul endroit : C'est cohérent et facile à utiliser, et beaucoup moins sujet aux erreurs.
La méthode d'extension que j'ai écrite fait exactement cela (elle vous montre également quel genre de code vous devriez écrire à chaque fois si vous n'allez pas l'utiliser).
Je crois que l'avantage de .ToDate(strDateFormat)
est qu'il a l'air simple et propre, aussi simple que l'original. DateTime.Parse
était censé être - mais avec la possibilité de vérifier si la conversion a réussi, et sans lancer d'exceptions.
1) Ce que l'on veut dire ici, c'est que traitement des exceptions (c'est-à-dire un try { ... } catch(Exception ex) { ...}
) - qui est nécessaire lorsque vous utilisez Parse parce qu'il lèvera une exception si une chaîne invalide est analysée - est non seulement inutile dans ce cas, mais aussi ennuyeux et complique votre code. TryParse évite tout cela, comme le montre l'exemple de code que j'ai fourni.
2) Eric Lippert est un célèbre Compagnon de StackOverflow et a travaillé chez Microsoft en tant que développeur principal dans l'équipe du compilateur C# pendant quelques années.
21 votes
Alors pourquoi ne pas utiliser DateTime.Parse ?
9 votes
Je faisais partie de ceux qui ont voté contre. C'est parce que votre question originale ( stackoverflow.com/revisions/ ) indiquait que vous VOULAIS utiliser DateTime.Parse() mais vous n'indiquiez pas POURQUOI vous ne pouviez pas l'utiliser. Cela donnait l'impression d'une question absurde, d'autant plus qu'une simple vérification aurait montré clairement que cacois avait raison : votre chaîne "2011-03-21 13:26" n'est pas un problème pour DateTime.Parse(). Enfin, vous n'avez fait aucune mention de ParseExact() dans votre question initiale. Vous avez attendu que après La réponse de Mitch pour ajouter ceci dans une édition.
9 votes
J'adore ces gens qui votent moins qu'une question sans donner de raison dans les commentaires.