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.