Как я объясню позже, я всегда предпочел бы методы TryParse
и TryParseExact
.Поскольку они немного громоздки в использовании, я написал метод расширения , который значительно упрощает анализ:
var dtStr = "2011-03-21 13:26";
DateTime? dt = dtStr.ToDate("yyyy-MM-dd HH:mm");
В отличие от Parse
, ParseExact
и т. Д. Он не выбрасываетисключение и позволяет вам проверить через
if (dt.HasValue) { // continue processing } else { // do error handling }
, было ли преобразование успешным (в этом случае dt
имеет значение, к которому вы можете получить доступ через dt.Value
) или нет (в данном случае это null
).
, что даже позволяет использовать элегантные ярлыки, такие как «Элвис» -оператор ?.
, например:
int? year = dtStr?.ToDate("yyyy-MM-dd HH:mm")?.Year;
Здесь вы можететакже используйте year.HasValue
, чтобы проверить, успешно ли выполнено преобразование, и если оно не удалось, то year
будет содержать null
, в противном случае указывается годовая часть даты.В случае сбоя преобразования исключение не выдается.
Решение: Метод расширения .ToDate ()
Попробуйте в .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;
}
}
Некоторая информация о коде
Вы можете спросить, почему я использовалInvariantCulture
вызов TryParseExact
: это заставляет функцию обрабатывать шаблоны формата всегда одинаково (в противном случае, например, "." Может интерпретироваться как десятичный разделитель в английском, в то время как это разделитель групп или разделитель даты на немецком языке).Напомним, что мы уже запрашивали строки формата, основанного на культуре, несколькими строками ранее, так что все в порядке.
Обновление: .ToDate()
(без параметров) теперь по умолчанию используется для всех распространенных шаблонов даты / временииз текущей культуры потока. Обратите внимание , что нам нужно вместе result
и dt
, потому что TryParseExact
не позволяет использовать DateTime?
, который мы намерены вернуть.В C # версии 7 вы можете немного упростить функцию ToDate
следующим образом:
// in C#7 only: "DateTime dt;" - no longer required, declare implicitly
if (DateTime.TryParseExact(dateTimeStr, dateFmt,
CultureInfo.InvariantCulture, style, out var dt)) result = dt;
или, если хотите, еще короче:
// 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?;
в этом случае вам вообще не нужны два объявления DateTime? result = null;
и DateTime dt;
- вы можете сделать это в одной строке кода.(Также было бы разрешено писать out DateTime dt
вместо out var dt
, если вы предпочитаете это.)
Я упростил код, используя ключевое слово params
: теперь вам не нужны 2 nd перегруженный метод больше.
Пример использования
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!");
}
Как видите, этот пример просто запрашивает dt.HasValue
чтобы увидеть, было ли преобразование успешным или нет.В качестве дополнительного бонуса TryParseExact позволяет указать строгий DateTimeStyles
, чтобы вы точно знали, была ли передана правильная строка даты / времени.
Дополнительные примеры использования
Перегруженная функция позволяет передавать массив допустимых форматов , используемый для разбора / преобразования дат, как показано и здесь (TryParseExact
напрямую поддерживает это), например
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);
Если у вас есть только несколько шаблонов шаблонов, вы также можете написать:
var dateStr = "2011-03-21 13:26";
var dt = dateStr.ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");
Расширенные примеры
Вы можете использовать оператор ??
для установки по умолчанию в отказоустойчивом формате, например,
var dtStr = "2017-12-30 11:37:00";
var dt = (dtStr.ToDate()) ?? dtStr.ToDate("yyyy-MM-dd HH:mm:ss");
В этом случае .ToDate()
будет использовать общие форматы даты местной культуры, иесли все это не удалось, он попытался бы использовать стандарт ISO формат "yyyy-MM-dd HH:mm:ss"
в качестве запасного варианта.Таким образом, функция расширения позволяет легко «связывать» различные резервные форматы.
Вы даже можете использовать расширение в LINQ, попробуйте это (это в .NetFiddle выше):
var patterns=new[] { "dd-MM-yyyy", "dd.MM.yyyy" };
(new[] { "15-01-2019", "15.01.2019" }).Select(s => s.ToDate(patterns)).Dump();
, который преобразует даты в массиве на лету с помощью шаблонов и выводит их в консоль.
Некоторые сведения о TryParseExact
Наконец, Вот некоторые комментарии по поводу фона (то есть причина, почему я написал это так):
Я предпочитаю TryParseExact в этом методе расширения, потому что вы избегаете обработки исключений - вы можете прочитать в статье Эрика Липперта об исключениях , почему вы должны использовать TryParseвместо того, чтобы разобрать, я цитирую его по этой теме: 2)
Это неудачное дизайнерское решение 1) [аннотация: кпусть метод Parse выбрасывает исключение] так досадно, что команда фреймворков вскоре после этого внедрила TryParse , что делает правильную вещь.
Да, но TryParse
иTryParseExact
оба по-прежнему намного менее удобны в использовании: они вынуждают вас использовать неинициализированную переменную в качестве параметра out
, который не должен иметь значение NULL, и во время преобразования вам нужно оценить логическое возвращаемое значение - либо вы, либонеобходимо немедленно использовать оператор if
, иначе вы должны сохранить возвращаемое значение в дополнительной логической переменной, чтобы вы могли выполнить проверку позже.И вы не можете просто использовать целевую переменную, не зная, было ли преобразование успешным или нет.
В большинстве случаев вы просто хотите узнать , было ли преобразование успешным или нет (и, конечно, значение, если оно было успешным) , поэтому обнуляемая целевая переменная которая хранит всю информацию, была бы желательна и намного более элегантна - потому что вся информация просто хранится в одном месте: она последовательна и проста в использовании, и намного менее подвержена ошибкам.
У меня есть метод расширенияПараметр «Написал» делает именно это (он также показывает, какой код вам придется писать каждый раз, если вы не собираетесь его использовать).
Я считаю, что преимущество .ToDate(strDateFormat)
в том, что он выглядит простыми чистый - так же просто, как предполагалось в оригинальном DateTime.Parse
, - но с возможностью проверки успешности преобразования и без исключения.
1) Здесь подразумевается, что обработка исключений (т. Е. Блок try { ... } catch(Exception ex) { ...}
) - это необходимо при использовании Parse, потому что оно выдаст исключение, если iРазбирается nvalid string - это не только не нужно в этом случае, но также раздражает и усложняет ваш код.TryParse избегает всего этого, поскольку пример кода, который я предоставил, показывает.
2) Эрик Липперт - известный сотрудник StackOverflow и былпару лет работал в Microsoft в качестве основного разработчика в команде по компиляции C #.