Przeanalizuj ciąg do DateTime w C #

165

Mam datę i godzinę w łańcuchu o takim formacie:

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

Jak mogę to przeanalizować System.DateTime?

Chcę użyć funkcji takich jak DateTime.Parse()lub, DateTime.ParseExact()jeśli to możliwe, aby móc ręcznie określić format daty.

Samogon
źródło
19
Dlaczego więc nie używasz DateTime.Parse?
Austin Salonen,
8
Byłem jednym z przeciwników. To dlatego, że Twoje pierwotne pytanie ( stackoverflow.com/revisions/ ... ) mówiło, że CHCESZ użyć DateTime.Parse (), ale nie podałeś DLACZEGO nie możesz tego użyć. Wydawało się to bezsensownym pytaniem, zwłaszcza że zwykłe sprawdzenie dałoby jasno do zrozumienia, że ​​cacois jest poprawny: Twój ciąg znaków „2011-03-21 13:26” nie stanowi problemu dla DateTime.Parse (). Wreszcie, w swoim pierwotnym pytaniu nie wspomniałeś o ParseExact (). Czekałeś aż po odpowiedź Mitcha dodać to w edycji.
anon
4
Po prostu uwielbiam tych ludzi, którzy głosują negatywnie bez podawania powodu w komentarzach.
Hooch

Odpowiedzi:

271

DateTime.Parse()spróbuje wymyślić format podanej daty i zwykle działa to dobrze. Jeśli możesz zagwarantować, że daty zawsze będą w podanym formacie, możesz użyć ParseExact():

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

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

(Należy jednak pamiętać, że zwykle bezpieczniej jest użyć jednej z metod TryParse na wypadek, gdyby data nie miała oczekiwanego formatu)

Upewnij się, że sprawdzasz niestandardowe ciągi formatujące datę i godzinę podczas tworzenia ciągu formatu, szczególnie zwracaj uwagę na liczbę liter i wielkości liter (np. „MM” i „mm” oznaczają bardzo różne rzeczy).

Innym przydatnym zasobem dla ciągów formatu C # jest formatowanie ciągów w języku C #

Mitch Wheat
źródło
5
Korekta - jest ZAWSZE bezpieczniejsza;) Jeśli wywołujesz metodę z wyjątkiem, zawsze najpierw sprawdź warunek wyjątku, jeśli to możliwe.
Gusdor
3
Powiedziałbym, że bezpieczniej jest zawsze przekazać swoją kulturę. Wolałbym mieć wyjątek, niż błędnie interpretować „01-02-2013” ​​jako dzień drugiego stycznia lub pierwszego lutego.
Carra,
1
@Carra: daty w formacie ISO8601 (tj. Rrrr-mm-dd 'są zawsze interpretowane w prawidłowy sposób. To'; dlatego używamy dat w formacie ISO8601 ...
Mitch Wheat
Dokładna analiza może być przydatna. Czasami wolałbym, aby moja aplikacja uległa awarii i zapalił się komputer, zamiast generować nieprawidłowe wyniki. Zależy od zastosowania.
Allen
ParseExact jest świetny, ponieważ jest elastyczny, ale ma wadę: należy zauważyć, że metody ParseExact i Parse zgłaszają wyjątki, jeśli wystąpi błąd składni w formacie daty zmiennej s. Dlatego lepiej jest użyć TryParseExcact. W mojej odpowiedzi poniżej wskazałem dlaczego.
Matt,
47

Jak wyjaśnię później, zawsze preferowałbym metody TryParsei TryParseExact. Ponieważ są trochę nieporęczne w użyciu, napisałem metodę rozszerzającą, która znacznie ułatwia analizowanie:

var    dtStr = "2011-03-21 13:26";
DateTime? dt = dtStr.ToDate("yyyy-MM-dd HH:mm");

W przeciwieństwie Parse, ParseExactitd. To nie wyjątek, i pozwala sprawdzić poprzez

if (dt.HasValue) { // continue processing } else { // do error handling }

czy konwersja powiodła się (w tym przypadku dtma wartość, do której można uzyskać dostęp przez dt.Value), czy nie (w tym przypadku jest null).

Pozwala to nawet na używanie eleganckich skrótów, takich jak operator „Elvis” ?., na przykład:

int? year = dtStr?.ToDate("yyyy-MM-dd HH:mm")?.Year;

Tutaj możesz również użyć year.HasValuedo sprawdzenia, czy konwersja się powiodła, a jeśli się nie powiodła, yearbędzie zawierać null, w przeciwnym razie część roku z daty. Nie ma żadnego wyjątku, jeśli konwersja nie powiodła się.


Rozwiązanie:   metoda rozszerzenia .ToDate ()

Wypróbuj w .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;
    }
}

Trochę informacji o kodzie

Możesz się zastanawiać, dlaczego użyłem InvariantCulturewywołania TryParseExact: ma to na celu wymuszenie na funkcji traktowania wzorców formatu zawsze w ten sam sposób (w przeciwnym razie na przykład „.” Może być interpretowane jako separator dziesiętny w języku angielskim, podczas gdy jest to separator grupy lub separator daty w Niemiecki). Przypomnijmy, że już kilka wierszy wcześniej przeszukiwaliśmy ciągi formatu opartego na kulturze, więc tutaj jest w porządku.

Aktualizacja: .ToDate() (bez parametrów) teraz domyślnie wszystkie typowe wzorce daty / godziny bieżącej kultury wątku.
Zauważ , że potrzebujemy resulti dtrazem, ponieważ TryParseExactnie pozwala na użycie DateTime?, które zamierzamy zwrócić. W C # w wersji 7 można ToDatenieco uprościć funkcję w następujący sposób:

 // in C#7 only: "DateTime dt;" - no longer required, declare implicitly
 if (DateTime.TryParseExact(dateTimeStr, dateFmt,
     CultureInfo.InvariantCulture, style, out var dt)) result = dt;

lub jeśli wolisz jeszcze krócej:

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

w takim przypadku nie potrzebujesz tych dwóch deklaracji DateTime? result = null;iw DateTime dt;ogóle - możesz to zrobić w jednej linii kodu. ( Jeśli wolisz, możesz również pisać out DateTime dtzamiast out var dttego).

Mam uproszczony kod dalej za pomocą paramssłowa kluczowego: Teraz już nie potrzebujesz 2 nd przeciążona metoda dłużej.


Przykład użycia

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!");
}

Jak widać, ten przykład po prostu pyta, dt.HasValueczy konwersja się powiodła, czy nie. Jako dodatkowy bonus TryParseExact pozwala określić ścisłe, DateTimeStylesdzięki czemu wiesz dokładnie, czy prawidłowy ciąg daty / czasu został przekazany, czy nie.


Więcej przykładów użycia

Przeciążona funkcja umożliwia przekazanie tablicy prawidłowych formatów używanych do analizowania / konwertowania dat, jak pokazano tutaj ( TryParseExactbezpośrednio to obsługuje), np.

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);

Jeśli masz tylko kilka wzorów szablonów, możesz również napisać:

var dateStr = "2011-03-21 13:26";
var dt = dateStr.ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");

Zaawansowane przykłady

Możesz użyć ??operatora, aby domyślnie ustawić format awaryjny, np

var dtStr = "2017-12-30 11:37:00";
var dt = (dtStr.ToDate()) ?? dtStr.ToDate("yyyy-MM-dd HH:mm:ss");

W takim przypadku .ToDate()użyje typowych formatów dat kultury lokalnej, a jeśli wszystkie te zawiodą, spróbuje użyć standardowego formatu ISO"yyyy-MM-dd HH:mm:ss" jako rezerwy. W ten sposób funkcja rozszerzenia umożliwia łatwe łączenie różnych formatów rezerwowych.

Możesz nawet użyć rozszerzenia w LINQ, wypróbuj to (jest w .NetFiddle powyżej):

var patterns=new[] { "dd-MM-yyyy", "dd.MM.yyyy" };
(new[] { "15-01-2019", "15.01.2019" }).Select(s => s.ToDate(patterns)).Dump(); 

który konwertuje daty w tablicy w locie przy użyciu wzorców i zrzuca je do konsoli.


Trochę informacji o TryParseExact

Na koniec kilka komentarzy na temat tła (tj. Powodu, dla którego napisałem to w ten sposób):

Preferuję TryParseExact w tej metodzie rozszerzenia, ponieważ unikasz obsługi wyjątków - możesz przeczytać w artykule Erica Lipperta o wyjątkach, dlaczego warto używać TryParse zamiast Parse, cytuję go na ten temat: 2)

Ta niefortunna decyzja projektowa 1) [adnotacja: pozwolić metodzie Parse zgłosić wyjątek] była tak irytująca, że ​​oczywiście zespół frameworka zaimplementował TryParse wkrótce potem, co robi dobrze.

Tak, ale TryParsei TryParseExactoba są nadal o wiele mniej niż wygodne w użyciu: zmuszają cię do użycia niezainicjowanej zmiennej jako outparametru, który nie może mieć wartości null, a podczas konwersji musisz ocenić wartość logiczną zwracaną - albo masz aby użyć ifinstrukcji natychmiast lub musisz zapisać zwracaną wartość w dodatkowej zmiennej boolowskiej, abyś mógł sprawdzić później. Nie możesz po prostu użyć zmiennej docelowej, nie wiedząc, czy konwersja się powiodła, czy nie.

W większości przypadków chcesz tylko wiedzieć, czy konwersja się powiodła, czy nie (i oczywiście wartość, jeśli się powiodła) , więc zmienna docelowa dopuszczająca wartość null, która przechowuje wszystkie informacje, byłaby pożądana i znacznie bardziej elegancka - ponieważ cała informacja jest po prostu przechowywane w jednym miejscu: jest spójne i łatwe w użyciu oraz znacznie mniej podatne na błędy.

Metoda rozszerzenia, którą napisałem, robi dokładnie to (pokazuje również, jaki rodzaj kodu musiałbyś napisać za każdym razem, jeśli nie zamierzasz go używać).

Uważam, że zaletą .ToDate(strDateFormat)jest to, że wygląda prosto i DateTime.Parseprzejrzyście - tak prosto, jak miał być oryginał - ale z możliwością sprawdzenia, czy konwersja się powiodła, i bez rzucania wyjątków.


1) Chodzi tutaj o to, że obsługa wyjątków (tj. try { ... } catch(Exception ex) { ...}Blok) - która jest konieczna, gdy używasz Parse, ponieważ zgłosi wyjątek, jeśli analizowany jest nieprawidłowy ciąg - jest nie tylko niepotrzebna w tym przypadku, ale także irytująca, i komplikowanie kodu. TryParse unika tego wszystkiego, ponieważ pokazuje przykładowy kod, który podałem.


2) Eric Lippert jest znanym pracownikiem StackOverflow i przez kilka lat pracował w firmie Microsoft jako główny programista w zespole kompilatora C #.

Matt
źródło
13
var dateStr = @"2011-03-21 13:26";
var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm", CultureInfo.CurrentCulture);

Sprawdź ten link, aby uzyskać inne ciągi formatu!

Rob
źródło
4

Umieść wartość ciągu czytelnego dla człowieka w .NET DateTime za pomocą następującego kodu:

DateTime.ParseExact("April 16, 2011 4:27 pm", "MMMM d, yyyy h:mm tt", null);
Zack Peterson
źródło
2

Prosta i prosta odpowiedź ->

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
źródło
Ładny @Shivam Bharadwaj, zrobiłem to w ten sam sposób
Muhammad Irfan
2

Możesz również użyć XmlConvert.ToDateString

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

Dobrze jest określić rodzaj daty, kod to:

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

Więcej szczegółów na temat różnych opcji analizowania http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html

Amir Shenouda
źródło
0

Wypróbuj poniższy kod

Month = Date = DateTime.Now.Month.ToString();   
Year = DateTime.Now.Year.ToString(); 
ViewBag.Today = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(Int32.Parse(Month)) + Year;
Adil Ayoub
źródło
Cześć, witam, proszę podać wyjaśnienie w odpowiedzi na pytanie. Samo wysyłanie kodu pocztowego nie jest zalecane
Ali