212 votes

Convertir une chaîne de caractères en date/heure en C#

J'ai la date et l'heure dans une chaîne de caractères formatée comme celle-ci :

"2011-03-21 13:26" //year-month-day hour:minute

Comment puis-je l'analyser pour System.DateTime ?

Je veux utiliser des fonctions comme DateTime.Parse() o DateTime.ParseExact() si possible, de pouvoir spécifier manuellement le format de la date.

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.

337voto

Mitch Wheat Points 169614

DateTime.Parse() essaiera de déterminer le format de la date donnée, et il fait généralement un bon travail. Si vous pouvez garantir que les dates seront toujours dans un format donné, vous pouvez utiliser ParseExact() :

string s = "2011-03-21 13:26";

DateTime dt = 
    DateTime.ParseExact(s, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);

(Notez toutefois qu'il est généralement plus sûr d'utiliser l'une des méthodes TryParse au cas où une date n'aurait pas le format attendu).

Assurez-vous de vérifier Chaînes de format de date et d'heure personnalisées lors de la construction de la chaîne de format, faites particulièrement attention au nombre de lettres et à la casse (par exemple, "MM" et "mm" signifient des choses très différentes).

Une autre ressource utile pour les chaînes de format C# est Formatage des chaînes de caractères en C#

5 votes

Correction - c'est TOUJOURS plus sûr ;) Si vous appelez une méthode avec une exception, vérifiez toujours d'abord la condition d'exception si possible.

3 votes

Je dirais que c'est plus sûr de toujours passer sa culture. Je préfère avoir une exception plutôt que de voir "01-02-2013" être mal interprété comme étant le 2 janvier ou le 1er février.

1 votes

@Carra : les dates au format ISO8601 (c'est-à-dire 'yyyy-mm-dd') sont toujours interprétées de la bonne manière. C'est pourquoi nous utilisons les dates au format ISO8601...

60voto

Matt Points 3445

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.

0 votes

Que faire si j'ai besoin d'analyser une date au format "dd/MM/yyyy" et ensuite d'analyser une date au format "MM/dd/yyyy" ? Par exemple, j'ai une date "07/09/2021". Je dois être capable de l'analyser correctement. B-)

0 votes

Dans ce cas, votre date est ambiguë et aucune des fonctions d'analyse syntaxique n'est capable de prédire ce que vous voulez dire : 7 septembre ou 9 juillet 2021 ? La seule solution dans ce cas est de laisser l'utilisateur choisir la culture qu'il souhaite utiliser, puis d'appliquer le bon modèle. Ou bien vous utilisez un motif où l'utilisateur doit spécifier le mois comme "septembre" ou "juillet", c'est-à-dire "dd/MMMM/yyyy". @James

14voto

Rob Points 2095
var dateStr = @"2011-03-21 13:26";
var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm", CultureInfo.CurrentCulture);

Regarde ça lien pour d'autres chaînes de format !

5voto

cacois Points 893

DateTime.Parse() devrait fonctionner correctement pour ce format de chaîne. Référence :

http://msdn.microsoft.com/en-us/library/1k1skd40.aspx#Y1240

Est-ce que vous obtenez une FormatException ?

5voto

Zack Peterson Points 19350

Mettez la valeur d'une chaîne lisible par l'homme dans un DateTime .NET avec un code comme celui-ci :

DateTime.ParseExact("April 16, 2011 4:27 pm", "MMMM d, yyyy h:mm tt", null);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X