Как я объясняю позже, я бы всегда предпочитать 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 - й перегруженного метода больше.
Пример использования
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, а не Parse, я процитирую его по этой теме: 2)
Это неудачное дизайнерское решение 1) [аннотация: позволить методу Parse генерировать исключение] настолько досадно, что
вскоре после этого команда разработчиков фреймворков внедрила TryParse, что делает правильную вещь.
Это так, но TryParse
и то , и TryParseExact
другое по-прежнему гораздо менее удобны в использовании: они вынуждают вас использовать неинициализированную переменную в качестве out
параметра, который не должен иметь значение NULL, и во время преобразования вам необходимо оценить логическое возвращаемое значение - либо у вас есть использовать if
оператор немедленно или вам нужно сохранить возвращаемое значение в дополнительной логической переменной, чтобы вы могли выполнить проверку позже. И вы не можете просто использовать целевую переменную, не зная, было ли преобразование успешным или нет.
В большинстве случаев вы просто хотите узнать, было ли преобразование успешным или нет (и, конечно, значение, если оно было успешным) , поэтому желательной и намного более элегантной будет целевая переменная, которая хранит всю информацию, потому что вся информация просто хранится в одном месте: это согласованно и просто в использовании, и намного менее подвержено ошибкам.
Метод расширения, который я написал, делает именно это (он также показывает, какой код вам придется писать каждый раз, если вы не собираетесь его использовать).
Я считаю, что выгода в .ToDate(strDateFormat)
том, что он выглядит простым и понятным - настолько простым, каким DateTime.Parse
должен был быть оригинал, - но с возможностью проверки успешности преобразования и без исключения.
1) Под этим подразумевается, что обработка исключений (т. Е. try { ... } catch(Exception ex) { ...}
Блока), которая необходима, когда вы используете Parse, потому что она генерирует исключение, если анализируется недопустимая строка, не только не нужна в этом случае, но и раздражает, и усложняя ваш код. TryParse избегает всего этого, как показывает пример кода, который я предоставил.
2) Эрик Липперт - известный сотрудник StackOverflow, пару лет проработавший в Microsoft в качестве основного разработчика в команде компилятора C #.