Comme je l'expliquerai plus tard, je préférerais toujours les méthodes TryParse
et TryParseExact
. Parce qu'ils sont un peu volumineux à utiliser, j'ai écrit une méthode d'extension qui rend l'analyse beaucoup plus facile:
var dtStr = "2011-03-21 13:26";
DateTime? dt = dtStr.ToDate("yyyy-MM-dd HH:mm");
Contrairement à Parse
, ParseExact
etc., il ne lève pas d'exception et vous permet de vérifier via
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, c'est le cas 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, year
elle contiendra null
, sinon la partie année de la date. Aucune exception n'est levée si la conversion a échoué.
Solution: la méthode d'extension .ToDate ()
Essayez-le dans .NetFiddle
public static class Extensions
{
// Extension method parsing a date string to a DateTime?
// dateFmt is optional and allows to pass a parsing pattern array
// or one or more patterns passed as string parameters
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();
}
// Commented out below because it can be done shorter as shown below.
// For older C# versions (older than C#7) you need it like that:
// DateTime? result = null;
// DateTime dt;
// if (DateTime.TryParseExact(dateTimeStr, dateFmt,
// CultureInfo.InvariantCulture, style, out dt)) result = dt;
// In C#7 and above, we can simply write:
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é l' InvariantCulture
appel TryParseExact
: C'est pour forcer la fonction à traiter les modèles de format toujours de la même manière (sinon, 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 d' un séparateur de date dans Allemand). Rappelez-vous que nous avons déjà interrogé les chaînes de format basées sur la culture quelques lignes auparavant, donc ce n'est pas grave 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 thread.
Notez que nous avons besoin du result
et dt
ensemble, car TryParseExact
ne permet pas d'utiliser DateTime?
, que nous avons l'intention de retourner. Dans C # version 7, vous pouvez simplifier ToDate
un peu la fonction 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?;
auquel cas vous n'avez pas besoin des deux déclarations DateTime? result = null;
et 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 cela).
J'ai encore simplifié le code en utilisant le params
mot - clé: maintenant, vous n'avez plus besoin de la 2 ème 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 demande simplement dt.HasValue
si la conversion a réussi ou non. En prime, TryParseExact permet de spécifier strict DateTimeStyles
afin que vous sachiez exactement si une chaîne date / heure correcte a été passée ou non.
Plus d'exemples d'utilisation
La fonction surchargée vous permet de transmettre un tableau de formats valides utilisés pour analyser / convertir les dates comme indiqué ici également ( TryParseExact
prend directement en charge cela), 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 de 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 l' ??
opérateur pour utiliser par défaut un format de sécurité, par exemple
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 des formats de date de culture locaux communs, et si tout cela échouait, il essaierait d'utiliser le format standard ISO"yyyy-MM-dd HH:mm:ss"
comme solution de secours. 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 patterns=new[] { "dd-MM-yyyy", "dd.MM.yyyy" };
(new[] { "15-01-2019", "15.01.2019" }).Select(s => s.ToDate(patterns)).Dump();
qui convertira les dates du tableau à la volée en utilisant les modèles et les videra dans la console.
Quelques informations sur TryParseExact
Enfin, voici quelques commentaires sur l'arrière-plan (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 évitez la gestion des exceptions - vous pouvez lire dans l'article d'Eric Lippert sur les exceptions pourquoi vous devriez utiliser TryParse plutôt que Parse, je le cite à ce sujet: 2)
Cette décision de conception malheureuse 1) [annotation: laisser la méthode Parse lever une exception] était si vexante que bien sûr
l'équipe des frameworks a implémenté TryParse peu de temps après, ce qui fait ce qu'il faut.
Il fait, mais TryParse
et les TryParseExact
deux sont encore beaucoup moins confortable à utiliser: Ils vous obligent à utiliser une variable non initialisée comme un out
paramètre qui ne doit pas être annulable et si vous vous convertir besoin d'évaluer la valeur de retour booléen - soit vous avez pour utiliser une if
instruction immédiatement ou vous devez stocker la valeur de retour dans une variable booléenne supplémentaire pour pouvoir effectuer la vérification plus tard. 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 simplement savoir si la conversion a réussi ou non (et bien sûr la valeur si elle a réussi) , donc une variable cible Nullable qui conserve toutes les informations serait souhaitable et beaucoup plus élégante - car toutes les informations sont juste stocké dans 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 type de code vous devriez écrire à chaque fois si vous ne l'utilisez pas).
Je pense que l'avantage .ToDate(strDateFormat)
est qu'il a l'air simple et propre - aussi simple que l'original DateTime.Parse
était censé l'être - mais avec la possibilité de vérifier si la conversion a réussi, et sans jeter d'exceptions.
1) Ce que l'on entend ici, c'est que la gestion des exceptions (c'est-à-dire un try { ... } catch(Exception ex) { ...}
bloc) - qui est nécessaire lorsque vous utilisez Parse car elle lèvera une exception si une chaîne invalide est analysée - n'est pas seulement inutile dans ce cas mais aussi ennuyeuse, et compliquer 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 collègue de StackOverflow et a travaillé chez Microsoft en tant que développeur principal au sein de l'équipe du compilateur C # pendant quelques années.