Разобрать строку в DateTime в C #

166

У меня есть дата и время в строке, отформатированной так:

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

Как я могу разобрать это System.DateTime?

Я хочу использовать такие функции, как DateTime.Parse()или, DateTime.ParseExact()если возможно, чтобы иметь возможность указать формат даты вручную.

самогон
источник
19
Так почему бы вам не использовать DateTime.Parse?
Остин Салонен
8
Я был одним из downvoters. Это было из-за того, что в вашем первоначальном вопросе ( stackoverflow.com/revisions/… ) было указано, что вы ХОТИТЕ использовать DateTime.Parse (), но не указали ПОЧЕМУ вы не можете его использовать. Это выглядело как бессмысленный вопрос, тем более что простая проверка показала бы, что cacois был верен: ваша строка «2011-03-21 13:26» не является проблемой для DateTime.Parse (). Наконец, вы не упомянули ParseExact () в своем первоначальном вопросе. Вы ждали , пока после того, как ответ Митча , чтобы добавить это в редактировании.
Anon
4
Я просто обожаю тех, кто голосует против, без объяснения причин в комментариях.
Луч

Ответы:

272

DateTime.Parse()постараюсь выяснить формат заданной даты, и это обычно делает хорошую работу. Если вы можете гарантировать, что даты всегда будут в заданном формате, вы можете использовать ParseExact():

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

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

(Но обратите внимание, что обычно безопаснее использовать один из методов TryParse, если дата не в ожидаемом формате)

Обязательно проверяйте пользовательские строки формата даты и времени при построении строки формата, особенно обратите внимание на количество букв и регистр (т. Е. «ММ» и «мм» означают очень разные вещи).

Другим полезным ресурсом для строк формата C # является форматирование строк в C #

Митч Пшеничный
источник
5
Исправление - это ВСЕГДА безопаснее;) Если вы вызываете метод с исключением, всегда сначала проверяйте условие исключения, если это возможно.
Gusdor
3
Я бы сказал, что безопаснее всегда передавать свою культуру. Я предпочел бы сделать исключение, чем ошибочно истолковывать «01-02-2013» как второе января или первое февраля.
Карра
1
@Carra: даты в формате ISO8601 (т. Е. Гггг-мм-дд 'всегда интерпретируются правильно. Вот почему мы используем даты в формате ISO8601 ...
Митч Уит
Точный анализ может быть полезен. Иногда я предпочитаю сбой моего приложения и загорание компьютера, а не вывод неправильного результата. Зависит от приложения.
Аллен
ParseExact великолепен, потому что он гибкий, но у него есть недостатки: обратите внимание, что методы ParseExact и Parse выдают исключения, если в формате даты переменной есть синтаксическая ошибка s. Следовательно, лучше использовать TryParseExcact. Я указал, почему в моем ответе ниже.
Мэтт
47

Как я объясняю позже, я бы всегда предпочитать 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 #.

Matt
источник
13
var dateStr = @"2011-03-21 13:26";
var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm", CultureInfo.CurrentCulture);

Проверьте эту ссылку для других строк формата!

Роб
источник
4

Поместите значение удобочитаемой строки в .NET DateTime с кодом, подобным этому:

DateTime.ParseExact("April 16, 2011 4:27 pm", "MMMM d, yyyy h:mm tt", null);
Зак Петерсон
источник
2

Простой и прямой ответ ->

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.
 * */
Шивам Бхарадвадж
источник
Хороший @Shivam Bharadwaj, я сделал то же самое
Мухаммед Ирфан
2

Вы также можете использовать XmlConvert.ToDateString

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

Хорошо указать вид даты, код:

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

Подробнее о различных параметрах синтаксического анализа http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html.

Амир Шенуда
источник
0

Попробуйте следующий код

Month = Date = DateTime.Now.Month.ToString();   
Year = DateTime.Now.Year.ToString(); 
ViewBag.Today = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(Int32.Parse(Month)) + Year;
Адиль Аюб
источник
Привет, добро пожаловать, пожалуйста, предоставьте объяснение при ответе на вопрос. Просто отправлять код не рекомендуется
Али