Wie ich später erläutere, würde ich immer die TryParse
und TryParseExact
Methoden bevorzugen . Da die Verwendung etwas umfangreich ist , habe ich eine Erweiterungsmethode geschrieben, die das Parsen erheblich vereinfacht:
var dtStr = "2011-03-21 13:26";
DateTime? dt = dtStr.ToDate("yyyy-MM-dd HH:mm");
Im Gegensatz zu Parse
, ParseExact
usw. wirft es keine Ausnahme, und ermöglicht es Ihnen , über zu überprüfen
if (dt.HasValue) { // continue processing } else { // do error handling }
ob die Konvertierung erfolgreich war (in diesem Fall dt
hat sie einen Wert, über den Sie zugreifen können dt.Value
) oder nicht (in diesem Fall ist dies der Fall null
).
Damit können sogar elegante Verknüpfungen wie der "Elvis" -Operator verwendet werden ?.
, zum Beispiel:
int? year = dtStr?.ToDate("yyyy-MM-dd HH:mm")?.Year;
Hier können Sie auch year.HasValue
überprüfen, ob die Konvertierung erfolgreich war, und wenn sie nicht erfolgreich war year
, enthält sie null
den Jahresanteil des Datums. Es wird keine Ausnahme ausgelöst, wenn die Konvertierung fehlgeschlagen ist.
Lösung: Die Erweiterungsmethode .ToDate ()
Probieren Sie es in .NetFiddle aus
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;
}
}
Einige Informationen zum Code
Sie fragen sich vielleicht, warum ich den InvariantCulture
Aufruf verwendet habe TryParseExact
: Dies dient dazu, die Funktion zu zwingen, Formatmuster immer gleich zu behandeln (andernfalls könnte beispielsweise "." Auf Englisch als Dezimaltrennzeichen interpretiert werden, während es sich um ein Gruppentrennzeichen oder ein Datumstrennzeichen handelt Deutsche). Denken Sie daran, dass wir die kulturbasierten Formatzeichenfolgen bereits einige Zeilen zuvor abgefragt haben, sodass dies hier in Ordnung ist.
Update: .ToDate()
(ohne Parameter) Standardmäßig werden jetzt alle gängigen Datums- / Zeitmuster der aktuellen Kultur des Threads verwendet.
Beachten Sie, dass wir das result
und dt
zusammen benötigen , da TryParseExact
es nicht erlaubt, es zu verwenden DateTime?
, was wir zurückgeben möchten. In C # Version 7 können Sie die ToDate
Funktion wie folgt vereinfachen :
// in C#7 only: "DateTime dt;" - no longer required, declare implicitly
if (DateTime.TryParseExact(dateTimeStr, dateFmt,
CultureInfo.InvariantCulture, style, out var dt)) result = dt;
oder, wenn Sie es noch kürzer mögen:
// 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?;
in dem Fall , dass Sie die beiden Erklärungen nicht brauchen DateTime? result = null;
und DateTime dt;
überhaupt - Sie kann es in einer Zeile Code tun. (Es wäre auch erlaubt zu schreiben out DateTime dt
anstatt out var dt
wenn Sie das bevorzugen).
Ich habe den Code weiter unter Verwendung des vereinfachten params
Stichwort: Jetzt haben Sie nicht die 2 brauchen nd überladene Methode nicht mehr.
Anwendungsbeispiel
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!");
}
Wie Sie sehen können, wird in diesem Beispiel nur abgefragt dt.HasValue
, ob die Konvertierung erfolgreich war oder nicht. Als zusätzlichen Bonus können Sie mit TryParseExact streng festlegen, DateTimeStyles
damit Sie genau wissen, ob eine richtige Datums- / Zeitzeichenfolge übergeben wurde oder nicht.
Weitere Anwendungsbeispiele
Mit der überladenen Funktion können Sie ein Array gültiger Formate übergeben, die zum Parsen / Konvertieren von Daten verwendet werden, wie hier gezeigt ( TryParseExact
unterstützt dies direkt), z
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);
Wenn Sie nur wenige Vorlagenmuster haben, können Sie auch schreiben:
var dateStr = "2011-03-21 13:26";
var dt = dateStr.ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");
Fortgeschrittene Beispiele
Sie können den ??
Operator verwenden, um standardmäßig ein ausfallsicheres Format zu verwenden, z
var dtStr = "2017-12-30 11:37:00";
var dt = (dtStr.ToDate()) ?? dtStr.ToDate("yyyy-MM-dd HH:mm:ss");
In diesem Fall wird die .ToDate()
würde gemeinsame lokale Kultur Datumsformate verwenden, und wenn alle diese versagen, würde es versuchen , das zu verwenden , ISO - Standard - Format "yyyy-MM-dd HH:mm:ss"
als Ausweich. Auf diese Weise ermöglicht die Erweiterungsfunktion das einfache "Verketten" verschiedener Fallback-Formate.
Sie können die Erweiterung sogar in LINQ verwenden. Probieren Sie dies aus (oben in der .NetFiddle):
var patterns=new[] { "dd-MM-yyyy", "dd.MM.yyyy" };
(new[] { "15-01-2019", "15.01.2019" }).Select(s => s.ToDate(patterns)).Dump();
Dadurch werden die Daten im Array im laufenden Betrieb mithilfe der Muster konvertiert und an die Konsole ausgegeben.
Hintergrundinformationen zu TryParseExact
Zum Schluss hier einige Kommentare zum Hintergrund (dh der Grund, warum ich es so geschrieben habe):
Ich bevorzuge TryParseExact in dieser Erweiterungsmethode, weil Sie die Ausnahmebehandlung vermeiden. Sie können in Eric Lipperts Artikel über Ausnahmen lesen, warum Sie TryParse anstelle von Parse verwenden sollten. Ich zitiere ihn zu diesem Thema: 2)
Diese unglückliche Entwurfsentscheidung 1) [Anmerkung: die Parse-Methode eine Ausnahme auslösen zu lassen] war so ärgerlich, dass
das Framework-Team natürlich kurz danach TryParse implementierte, was das Richtige tut.
Er tut, aber , TryParse
und TryParseExact
beide sind immer noch viel weniger als komfortabel zu bedienen: Sie zwingen Sie eine nicht initialisierte Variable als verwenden out
Parameter , die keine Nullwerte enthalten sein müssen , und während Sie konvertieren Sie die boolean Rückgabewert bewerten müssen - entweder Sie haben Um eine if
Anweisung sofort zu verwenden, müssen Sie den Rückgabewert in einer zusätzlichen booleschen Variablen speichern, damit Sie die Prüfung später durchführen können. Und Sie können die Zielvariable nicht einfach verwenden, ohne zu wissen, ob die Konvertierung erfolgreich war oder nicht.
In den meisten Fällen möchten Sie nur wissen, ob die Konvertierung erfolgreich war oder nicht (und natürlich den Wert, wenn sie erfolgreich war) . Daher wäre eine nullfähige Zielvariable, die alle Informationen enthält, wünschenswert und viel eleganter - da die gesamte Information ist nur an einem Ort gespeichert: Das ist konsistent und einfach zu bedienen und viel weniger fehleranfällig.
Die Erweiterungsmethode, die ich geschrieben habe, macht genau das (sie zeigt Ihnen auch, welche Art von Code Sie jedes Mal schreiben müssten, wenn Sie ihn nicht verwenden würden).
Ich glaube, der Vorteil von .ToDate(strDateFormat)
ist, dass es einfach und sauber aussieht - so einfach wie das Original DateTime.Parse
sein sollte -, aber mit der Fähigkeit zu überprüfen, ob die Konvertierung erfolgreich war, und ohne Ausnahmen zu werfen.
1) Damit ist gemeint, dass die Ausnahmebehandlung (dh ein try { ... } catch(Exception ex) { ...}
Block) - die erforderlich ist, wenn Sie Parse verwenden, weil sie eine Ausnahme auslöst, wenn eine ungültige Zeichenfolge analysiert wird - in diesem Fall nicht nur unnötig, sondern auch ärgerlich ist Ihren Code komplizieren. TryParse vermeidet dies alles, wie das von mir bereitgestellte Codebeispiel zeigt.
2) Eric Lippert ist ein berühmter StackOverflow-Mitarbeiter und arbeitete einige Jahre bei Microsoft als Hauptentwickler im C # -Compilerteam.