Розділити рядок до DateTime в C #


165

У мене є дата та час у рядку, відформатованому так:

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

Як я можу його розібрати System.DateTime?

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


19
То чому б не використовувати DateTime.Parse?
Остін Салонен

8
Я був одним із нижніх. Це було через те, що в первісному запитанні ( stackoverflow.com/reitions/… ) було вказано, що Ви ХОЧИЛИ використовувати DateTime.Parse (), але Ви не заявили, ЧОМУ не можете ним користуватися. Це зробило це безглуздим питанням, тим більше, що з простої перевірки було б зрозуміло, що какао правильно: Ваш рядок "2011-03-21 13:26" не є проблемою для DateTime.Parse (). Нарешті, ви не згадували ParseExact () у своєму первісному запитанні. Ви чекали, поки після відповіді Мітч додасть це до редагування.
анонс

4
Я просто люблю тих людей, які голосують під голосуванням, не даючи жодної причини в коментарях.
Хуч

Відповіді:


271

DateTime.Parse()спробуємо розібратися у форматі даної дати, і це, як правило, добре справляється. Якщо ви можете гарантувати, що дати завжди будуть у заданому форматі, ви можете використовувати ParseExact():

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

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

(Але зауважте, що зазвичай безпечніше використовувати один із методів TryParse, якщо дата не в очікуваному форматі)

Не забудьте перевірити користувацькі рядки формату дати та часу під час створення рядка формату, особливо зверніть увагу на кількість букв та регістру (тобто "MM" та "mm" означають дуже різні речі).

Ще один корисний ресурс для рядків формату C # - це String Formatting у C #


5
Виправлення - ЗАВЖДИ безпечніше;) Якщо ви викликаєте метод із винятком, завжди перевірте умову виключення, якщо це можливо.
Гусдор

3
Я б сказав, що безпечніше завжди передавати свою культуру. Я вважаю за краще виняток, ніж те, що "01-02-2013" неправильно трактувались як другого січня, так і першого лютого.
Carra

1
@Carra: дати у форматі ISO8601 (тобто yyyy-mm-dd 'завжди трактуються правильно. Ось чому ми використовуємо дати формату ISO8601 ...
Mitch Wheat

Точний аналіз може бути корисним. Іноді я вважаю за краще збій програми та ввімкнення мого комп'ютера, а не отримання неправильних даних. Залежить від програми.
Аллен

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параметр, який не повинен бути нульовим, і під час перетворення вам потрібно оцінити булеве значення повернення - або у вас є негайно використовувати ifоператор або вам потрібно зберегти повернене значення в додатковій булевій змінній, щоб ви змогли зробити перевірку пізніше. І ви не можете просто використовувати цільову змінну, не знаючи, успішна чи ні конверсія.

У більшості випадків ви просто хочете дізнатись, успішна чи ні конверсія (і, звичайно, значення, якщо вона була успішною) , тому нульова цільова змінна, яка зберігає всю інформацію, була б бажаною і набагато більш елегантною - адже вся інформація є просто зберігається в одному місці: Це стійкий і простий у використанні і набагато менше схильний до помилок.

Написаний нами метод розширення робить саме це (він також показує, який код вам доведеться писати кожен раз, якщо ви не збираєтесь його використовувати).

Я вважаю, що користь .ToDate(strDateFormat)полягає в тому, що вона виглядає простою і чистою - такою ж простою, як і оригінал DateTime.Parseповинна була бути - але з можливістю перевірити, чи конверсія була успішною, і не кидаючи винятків.


1) Мається на увазі, що обробка винятків (тобто try { ... } catch(Exception ex) { ...}блок), яка необхідна під час використання Parse, оскільки вона буде викидати виняток, якщо неправомірний рядок буде розібраний - в цьому випадку є не тільки непотрібним, але й дратує, і ускладнення вашого коду. TryParse уникає всього цього, як показує зразок коду, який я надав.


2) Ерік Ліпперт - відомий співробітник StackOverflow і пару років працював у Microsoft головним розробником над командою компілятора C #.


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/ua/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;

Привіт, Ласкаво просимо, надайте пояснення, відповідаючи на запитання. Лише розміщення коду не рекомендується
Алі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.