Analyser la chaîne à DateTime en C #

166

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

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

Comment puis-je l'analyser System.DateTime?

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

Gnôle
la source
19
Alors pourquoi n'utilisez-vous pas DateTime.Parse?
Austin Salonen
8
J'étais l'un des downvoters. C'était parce que votre question initiale ( stackoverflow.com/revisions/… ) indiquait que vous VOULEZ utiliser DateTime.Parse () mais que vous n'avez pas déclaré POURQUOI vous ne pouviez pas l'utiliser. Cela a fait paraître une question absurde, d'autant plus qu'une simple vérification aurait montré que le cacois était correct: 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 jusqu'à ce que , après la réponse de Mitch pour ajouter ceci dans une édition.
Anon
4
J'adore ces personnes qui votent contre la question sans donner aucune raison dans les commentaires.
Hooch

Réponses:

272

DateTime.Parse()essaiera de comprendre le format de la date donnée, et cela fait généralement du 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);

(Mais notez qu'il est généralement plus sûr d'utiliser l'une des méthodes TryParse au cas où une date ne serait pas au format attendu)

Assurez-vous de vérifier les chaînes de format de date et d'heure personnalisées lors de la construction de la chaîne de format, en particulier faites attention au nombre de lettres et à la casse (c'est-à-dire que «MM» et «mm» signifient des choses très différentes).

Une autre ressource utile pour les chaînes au format C # est la mise en forme de chaînes en C #

Blé Mitch
la source
5
Correction - c'est TOUJOURS plus sûr;) Si vous appelez une méthode avec une exception, vérifiez toujours la condition d'exception en premier si possible.
Gusdor
3
Je dirais qu'il est plus sûr de toujours transmettre votre culture. Je préfère avoir une exception que d'avoir "01-02-2013" être mal interprété soit comme le 2 janvier ou le premier février.
Carra
1
@Carra: les dates au format ISO8601 (ie aaaa-mm-jj 'sont toujours interprétées correctement. C'est pourquoi nous utilisons les dates au format ISO8601 ...
Mitch Wheat
Parse exact peut être utile. Parfois, je préférerais que mon application plante et que mon ordinateur soit allumé, plutôt que de produire une sortie incorrecte. Dépend de l'application.
Allen
ParseExact est excellent car il est flexible, mais il a un inconvénient: notez que les méthodes ParseExact et Parse lèvent des exceptions s'il y a une erreur de syntaxe dans le format de date de la variable s. Par conséquent, il est préférable d'utiliser TryParseExcact. J'ai expliqué pourquoi dans ma réponse ci-dessous.
Matt
47

Comme je l'expliquerai plus tard, je préférerais toujours les méthodes TryParseet 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, ParseExactetc., 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, dta 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.HasValuepour vérifier si la conversion a réussi, et si elle n'a pas réussi, yearelle 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' InvariantCultureappel 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 resultet dtensemble, car TryParseExactne permet pas d'utiliser DateTime?, que nous avons l'intention de retourner. Dans C # version 7, vous pouvez simplifier ToDateun 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 dtau lieu de out var dtsi vous préférez cela).

J'ai encore simplifié le code en utilisant le paramsmot - 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.HasValuesi la conversion a réussi ou non. En prime, TryParseExact permet de spécifier strict DateTimeStylesafin 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 ( TryParseExactprend 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 TryParseet les TryParseExactdeux sont encore beaucoup moins confortable à utiliser: Ils vous obligent à utiliser une variable non initialisée comme un outparamè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 ifinstruction 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.

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

Consultez ce lien pour d'autres chaînes de format!

Rob
la source
4

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);
Zack Peterson
la source
2

La réponse simple et directe ->

using System;

namespace DemoApp.App

{
public class TestClassDate
{
    public static DateTime GetDate(string string_date)
    {
        DateTime dateValue;
        if (DateTime.TryParse(string_date, out dateValue))
            Console.WriteLine("Converted '{0}' to {1}.", string_date, dateValue);
        else
            Console.WriteLine("Unable to convert '{0}' to a date.", string_date);
        return dateValue;
    }
    public static void Main()
    {
        string inString = "05/01/2009 06:32:00";
        GetDate(inString);
    }
}
}

/**
 * Output:
 * Converted '05/01/2009 06:32:00' to 5/1/2009 6:32:00 AM.
 * */
Shivam Bharadwaj
la source
Nice @Shivam Bharadwaj, je l'ai fait de la même manière
Muhammad Irfan
2

Vous pouvez également utiliser XmlConvert.ToDateString

var dateStr = "2011-03-21 13:26";
var parsedDate = XmlConvert.ToDateTime(dateStr, "yyyy-MM-dd hh:mm");

Il est bon de spécifier le type de date, le code est:

var anotherParsedDate = DateTime.ParseExact(dateStr, "yyyy-MM-dd hh:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);

Plus de détails sur les différentes options d'analyse http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html

Amir Shenouda
la source
0

Essayez le code suivant

Month = Date = DateTime.Now.Month.ToString();   
Year = DateTime.Now.Year.ToString(); 
ViewBag.Today = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(Int32.Parse(Month)) + Year;
Adil Ayoub
la source
Bonjour, bienvenue, veuillez fournir une explication lorsque vous répondez à une question. Il n'est pas recommandé de poster simplement le code
Ali