Analizza stringa in DateTime in C #


165

Ho data e ora in una stringa formattata come quella:

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

Come posso analizzarlo System.DateTime?

Voglio usare funzioni come DateTime.Parse()o DateTime.ParseExact()se possibile, per poter specificare manualmente il formato della data.


19
Quindi perché non usi DateTime.Parse?
Austin Salonen,

8
Ero uno dei downvoter. Era perché la tua domanda originale ( stackoverflow.com/revisions/… ) affermava che VUOI utilizzare DateTime.Parse () ma non hai indicato PERCHÉ non puoi usarlo. Ciò ha fatto sembrare una domanda senza senso, soprattutto perché un semplice controllo avrebbe chiarito che quello di cacois era corretto: la tua stringa "2011-03-21 13:26" non è un problema per DateTime.Parse (). Infine, non hai fatto menzione di ParseExact () nella domanda originale. Hai aspettato fino a dopo la risposta di Mitch per aggiungere questo in una modifica.
anon

4
Adoro quelle persone che votano in basso votando la domanda senza dare alcun motivo nei commenti.
Hooch,

Risposte:


271

DateTime.Parse()proverà a capire il formato della data indicata e di solito fa un buon lavoro. Se puoi garantire che le date saranno sempre in un determinato formato, puoi usare ParseExact():

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

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

(Si noti che di solito è più sicuro utilizzare uno dei metodi TryParse nel caso in cui una data non sia nel formato previsto)

Assicurati di controllare le stringhe di formato di data e ora personalizzate durante la costruzione della stringa di formato, in particolare presta attenzione al numero di lettere e maiuscole (ad esempio "MM" e "mm" significano cose molto diverse).

Un'altra risorsa utile per le stringhe in formato C # è la formattazione delle stringhe in C #


5
Correzione: è SEMPRE più sicuro;) Se si chiama un metodo con un'eccezione, verificare sempre prima la condizione dell'eccezione.
Gusdor,

3
Direi che è più sicuro passare sempre la tua cultura. Preferirei fare un'eccezione piuttosto che avere "01-02-2013" interpretato male come il secondo gennaio o il primo febbraio.
Carra,

1
@Carra: le date in formato ISO8601 (ovvero yyyy-mm-dd 'sono sempre interpretate nel modo corretto. Ecco perché usiamo le date in formato ISO8601 ...
Mitch Wheat,

L'analisi esatta può essere utile. A volte, preferirei il mio crash dell'applicazione e la luce del mio computer in fiamme, invece di produrre un output errato. Dipende dall'applicazione.
Allen

ParseExact è eccezionale perché è flessibile, ma ha un aspetto negativo: si noti che i metodi ParseExact e Parse generano eccezioni se si verifica un errore di sintassi nel formato data della variabile s. Quindi, è meglio usare TryParseExcact. Ho sottolineato perché nella mia risposta di seguito.
Matt

47

Come spiegherò più avanti, preferirei sempre i metodi TryParsee TryParseExact. Poiché sono un po 'ingombranti da usare, ho scritto un metodo di estensione che rende l'analisi molto più semplice:

var    dtStr = "2011-03-21 13:26";
DateTime? dt = dtStr.ToDate("yyyy-MM-dd HH:mm");

A differenza Parse, ParseExactecc. Non genera un'eccezione e consente di verificare tramite

if (dt.HasValue) { // continue processing } else { // do error handling }

se la conversione ha avuto esito positivo (in questo caso dtha un valore a cui puoi accedere tramite dt.Value) oppure no (in questo caso lo è null).

Ciò consente persino di utilizzare scorciatoie eleganti come l'operatore "Elvis" ?., ad esempio:

int? year = dtStr?.ToDate("yyyy-MM-dd HH:mm")?.Year;

Qui puoi anche usare year.HasValueper verificare se la conversione ha avuto successo e se non è riuscita year, conterrà null, altrimenti la parte dell'anno della data. Non viene generata alcuna eccezione se la conversione non è riuscita.


Soluzione:   il metodo di estensione .ToDate ()

Provalo in .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;
    }
}

Alcune informazioni sul codice

Potresti chiederti, perché ho usato la InvariantCulturechiamata TryParseExact: Questo è per forzare la funzione a trattare i modelli di formato sempre allo stesso modo (altrimenti per esempio "." Potrebbe essere interpretato come separatore decimale in inglese mentre è un separatore di gruppo o un separatore di date in Tedesco). Ricordiamo che abbiamo già interrogato le stringhe di formato basate sulla cultura alcune righe prima, quindi qui va bene.

Aggiornamento: .ToDate() (senza parametri) per impostazione predefinita ora tutti i modelli di data / ora comuni della cultura corrente del thread.
Si noti che abbiamo bisogno di resulte dtinsieme, perché TryParseExactnon consente di utilizzare DateTime?, che intendiamo restituire. Nella versione 7 di C # potresti semplificare ToDateun po ' la funzione come segue:

 // in C#7 only: "DateTime dt;" - no longer required, declare implicitly
 if (DateTime.TryParseExact(dateTimeStr, dateFmt,
     CultureInfo.InvariantCulture, style, out var dt)) result = dt;

o, se ti piace ancora più breve:

 // 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?;

nel qual caso non hai bisogno delle due dichiarazioni DateTime? result = null;e DateTime dt;affatto - puoi farlo in una riga di codice. (Sarebbe anche permesso scrivere out DateTime dtinvece che out var dtse lo preferisci).

Ho semplificato il codice ulteriormente utilizzando la paramsparola chiave: Ora non è necessario il 2 ° metodo di overload più.


Esempio di utilizzo

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!");
}

Come puoi vedere, questo esempio richiede solo dt.HasValuese la conversione ha avuto esito positivo o meno. Come bonus extra, TryParseExact consente di specificare rigoroso in DateTimeStylesmodo da sapere esattamente se è stata passata o meno una stringa data / ora corretta.


Altri esempi di utilizzo

La funzione di sovraccarico consente di passare un array di formati validi utilizzati per l'analisi / conversione delle date come mostrato anche qui ( TryParseExactsupporta direttamente questo), ad es.

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);

Se hai solo alcuni modelli di modello, puoi anche scrivere:

var dateStr = "2011-03-21 13:26";
var dt = dateStr.ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");

Esempi avanzati

È possibile utilizzare l' ??operatore per impostazione predefinita in un formato fail-safe, ad es

var dtStr = "2017-12-30 11:37:00";
var dt = (dtStr.ToDate()) ?? dtStr.ToDate("yyyy-MM-dd HH:mm:ss");

In questo caso, .ToDate()utilizzerebbe i formati di data di cultura locale comuni e, se tutti questi fallissero, proverebbe a utilizzare il formato standard ISO"yyyy-MM-dd HH:mm:ss" come fallback. In questo modo, la funzione di estensione consente di "concatenare" facilmente diversi formati di fallback.

Puoi anche usare l'estensione in LINQ, provalo (è nella .NetFiddle sopra):

var patterns=new[] { "dd-MM-yyyy", "dd.MM.yyyy" };
(new[] { "15-01-2019", "15.01.2019" }).Select(s => s.ToDate(patterns)).Dump(); 

che convertirà le date nell'array al volo usando gli schemi e scaricandoli sulla console.


Alcuni retroscena su TryParseExact

Infine, ecco alcuni commenti sullo sfondo (ovvero il motivo per cui l'ho scritto in questo modo):

Sto preferendo TryParseExact in questo metodo di estensione, perché eviti la gestione delle eccezioni : puoi leggere l'articolo di Eric Lippert sulle eccezioni per cui dovresti usare TryParse piuttosto che Parse, lo cito su questo argomento: 2)

Questa sfortunata decisione progettuale 1) [annotazione: lasciare che il metodo Parse generi un'eccezione] era così irritante che, naturalmente, il team dei framework implementò TryParse poco dopo, il che fa la cosa giusta.

Lo fa, ma TryParseed TryParseExactentrambi sono ancora molto meno comodi da usare: ti costringono a usare una variabile non inizializzata come outparametro che non deve essere nullable e mentre stai convertendo devi valutare il valore di ritorno booleano - o hai per utilizzare ifimmediatamente un'istruzione o devi archiviare il valore restituito in una variabile booleana aggiuntiva in modo da poter eseguire il controllo in un secondo momento. E non puoi semplicemente usare la variabile target senza sapere se la conversione ha avuto successo o meno.

Nella maggior parte dei casi vuoi solo sapere se la conversione ha avuto successo o meno (e ovviamente il valore se ha avuto successo) , quindi una variabile di destinazione nullable che mantiene tutte le informazioni sarebbe desiderabile e molto più elegante, perché l'intera informazione è memorizzato in un unico posto: è coerente, facile da usare e molto meno soggetto a errori.

Il metodo di estensione che ho scritto fa esattamente questo (mostra anche che tipo di codice dovresti scrivere ogni volta se non lo userai).

Credo che il vantaggio .ToDate(strDateFormat)sia che sembra semplice e pulito - semplice come l'originale DateTime.Parseavrebbe dovuto essere - ma con la possibilità di verificare se la conversione ha avuto successo e senza generare eccezioni.


1) Ciò che si intende qui è che la gestione delle eccezioni (cioè un try { ... } catch(Exception ex) { ...}blocco) - che è necessaria quando si utilizza Parse perché genererà un'eccezione se si analizza una stringa non valida - non è solo non necessaria in questo caso ma anche fastidiosa, e complicando il tuo codice. TryParse evita tutto questo come mostra l'esempio di codice che ho fornito.


2) Eric Lippert è un famoso compagno StackOverflow e ha lavorato in Microsoft come sviluppatore principale del team di compilatori C # per un paio di anni.


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

Dai un'occhiata a questo link per altre stringhe di formato!



4

Inserisci il valore di una stringa leggibile dall'uomo in un DateTime .NET con codice come questo:

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

2

La risposta semplice e diretta ->

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.
 * */

Bello @ Shivam Bharadwaj, l'ho fatto allo stesso modo
Muhammad Irfan,

2

Puoi anche usare XmlConvert.ToDateString

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

È bene specificare il tipo di data, il codice è:

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

Maggiori dettagli sulle diverse opzioni di analisi http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html


0

Prova il seguente codice

Month = Date = DateTime.Now.Month.ToString();   
Year = DateTime.Now.Year.ToString(); 
ViewBag.Today = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(Int32.Parse(Month)) + Year;

Ciao, benvenuto, si prega di fornire una spiegazione quando si risponde a una domanda. È sconsigliato solo l'invio di codice
Ali
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.