Parsing string ke DateTime dalam C #

165

Saya memiliki tanggal dan waktu dalam string yang diformat seperti itu:

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

Bagaimana saya bisa menguraikannya System.DateTime?

Saya ingin menggunakan fungsi seperti DateTime.Parse()atau DateTime.ParseExact()jika mungkin, untuk dapat menentukan format tanggal secara manual.

Hooch
sumber
19
Jadi mengapa Anda tidak menggunakan DateTime.Parse?
Austin Salonen
8
Saya adalah salah satu downvoters. Itu karena pertanyaan awal Anda ( stackoverflow.com/revisions/… ) menyatakan bahwa Anda INGIN menggunakan DateTime.Parse () tetapi Anda tidak menyatakan MENGAPA Anda tidak bisa menggunakannya. Ini membuatnya tampak seperti pertanyaan yang tidak masuk akal, terutama karena pemeriksaan sederhana akan memperjelas bahwa cacois benar: String Anda "2011-03-21 13:26" bukan masalah untuk DateTime.Parse (). Akhirnya, Anda tidak menyebutkan ParseExact () dalam pertanyaan awal Anda. Anda menunggu sampai setelah jawaban Mitch untuk menambahkan ini dalam suntingan.
anon
4
Saya suka orang-orang di bawah-voting pertanyaan tanpa memberikan alasan dalam komentar.
Hooch

Jawaban:

271

DateTime.Parse()akan mencoba mencari tahu format tanggal yang diberikan, dan biasanya melakukan pekerjaan dengan baik. Jika Anda dapat menjamin tanggal akan selalu dalam format yang diberikan maka Anda dapat menggunakan ParseExact():

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

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

(Tetapi perhatikan bahwa biasanya lebih aman menggunakan salah satu metode TryParse jika tanggal tidak dalam format yang diharapkan)

Pastikan untuk memeriksa String Format Tanggal dan Waktu Kustom ketika membangun string format, terutama memperhatikan jumlah huruf dan huruf (yaitu "MM" dan "mm" berarti hal yang sangat berbeda).

Sumber daya lain yang berguna untuk string format C # adalah Pemformatan String dalam C #

Mitch Wheat
sumber
5
Koreksi - ini SELALU lebih aman;) Jika Anda memanggil metode dengan pengecualian, selalu periksa kondisi pengecualian terlebih dahulu jika memungkinkan.
Gusdor
3
Menurut saya lebih aman untuk selalu melewati budaya Anda. Saya lebih suka memiliki pengecualian daripada memiliki "01-02-2013" disalahtafsirkan sebagai yang kedua Januari atau yang pertama Februari.
Carra
1
@Carra: tanggal dalam format ISO8601 (yaitu yyyy-mm-dd 'selalu ditafsirkan dengan cara yang benar. Itu sebabnya kami menggunakan tanggal format ISO8601 ...
Mitch Wheat
Parse pastinya bisa bermanfaat. Kadang-kadang, saya lebih suka aplikasi saya crash dan komputer saya menyala, bukan menghasilkan output yang salah. Tergantung pada aplikasi.
Allen
ParseExact bagus karena fleksibel, tetapi memiliki kelemahan: Perhatikan bahwa metode ParseExact dan Parse melempar pengecualian jika ada kesalahan sintaksis dalam format tanggal variabel s. Oleh karena itu, lebih baik menggunakan TryParseExcact. Saya telah menunjukkan mengapa dalam jawaban saya di bawah ini.
Mat
47

Seperti yang akan saya jelaskan nanti, saya akan selalu mendukung TryParsedan TryParseExactmetode. Karena mereka agak besar untuk digunakan, saya telah menulis metode ekstensi yang membuat parsing lebih mudah:

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

Tidak seperti Parse, ParseExactdll. Itu tidak membuang pengecualian, dan memungkinkan Anda untuk memeriksa via

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

apakah konversi berhasil (dalam hal ini dtmemiliki nilai yang dapat Anda akses melalui dt.Value) atau tidak (dalam hal ini, itu adalah null).

Itu bahkan memungkinkan untuk menggunakan pintasan elegan seperti operator "Elvis" ?., misalnya:

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

Di sini Anda juga dapat menggunakan year.HasValueuntuk memeriksa apakah konversi berhasil, dan jika tidak berhasil maka yearakan berisi null, jika tidak, bagian tahun tanggal. Tidak ada pengecualian yang dilemparkan jika konversi gagal.


Solusi:   Metode ekstensi .ToDate ()

Cobalah di .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;
    }
}

Beberapa informasi tentang kode

Anda mungkin bertanya-tanya, mengapa saya menggunakan InvariantCulturepanggilan TryParseExact: Ini untuk memaksa fungsi untuk memperlakukan pola format selalu dengan cara yang sama (jika tidak misalnya "." Dapat diartikan sebagai pemisah desimal dalam bahasa Inggris sementara itu adalah pemisah grup atau pemisah tanggal di Jerman). Ingat kita sudah menanyakan string format berbasis budaya beberapa baris sebelumnya sehingga tidak apa-apa di sini.

Pembaruan: .ToDate() (tanpa parameter) sekarang default untuk semua pola tanggal / waktu umum budaya saat ini utas.
Perhatikan bahwa kita perlu resultdan dtbersama - sama, karena TryParseExacttidak memungkinkan untuk digunakan DateTime?, yang ingin kita kembalikan. Di C # Versi 7 Anda bisa menyederhanakan ToDatefungsi sedikit sebagai berikut:

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

atau, jika Anda suka lebih pendek:

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

dalam hal ini Anda tidak perlu dua deklarasi DateTime? result = null;dan DateTime dt;sama sekali - Anda dapat melakukannya dalam satu baris kode. (Ini juga akan diizinkan untuk menulis out DateTime dtalih-alih out var dtjika Anda lebih suka itu).

Saya telah menyederhanakan kode lebih lanjut dengan menggunakan paramskata kunci: Sekarang Anda tidak perlu 2 nd kelebihan beban metode lagi.


Contoh penggunaan

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

Seperti yang Anda lihat, contoh ini hanya menanyakan dt.HasValueapakah konversi berhasil atau tidak. Sebagai bonus tambahan, TryParseExact memungkinkan untuk menentukan dengan ketat DateTimeStylessehingga Anda tahu persis apakah string tanggal / waktu yang tepat telah berlalu atau tidak.


Lebih Banyak Contoh penggunaan

Fungsi kelebihan beban memungkinkan Anda untuk melewatkan larik format yang valid yang digunakan untuk mem-parsing / mengubah tanggal seperti yang ditunjukkan di sini juga ( TryParseExactsecara langsung mendukung ini), misalnya

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

Jika Anda hanya memiliki beberapa pola templat, Anda juga dapat menulis:

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

Contoh lanjutan

Anda dapat menggunakan ??operator secara default ke format gagal-aman, mis

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

Dalam hal ini, .ToDate()akan menggunakan format tanggal kultur lokal yang umum, dan jika semua ini gagal, ia akan mencoba menggunakan format standar ISO"yyyy-MM-dd HH:mm:ss" sebagai cadangan. Dengan cara ini, fungsi ekstensi memungkinkan untuk "rantai" berbagai format fallback dengan mudah.

Anda bahkan dapat menggunakan ekstensi di LINQ, coba ini (ada di .NetFiddle di atas):

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

yang akan mengonversi tanggal dalam array dengan cepat menggunakan pola dan membuangnya ke konsol.


Beberapa latar belakang tentang TryParseExact

Akhirnya, Berikut adalah beberapa komentar tentang latar belakang (yaitu alasan mengapa saya menulis seperti ini):

Saya lebih suka TryParseExact dalam metode ekstensi ini, karena Anda menghindari penanganan pengecualian - Anda dapat membaca di artikel Eric Lippert tentang pengecualian mengapa Anda harus menggunakan TryParse daripada Parse, saya kutipkan kepadanya tentang topik itu: 2)

Keputusan desain yang tidak menguntungkan ini 1) [anotasi: untuk membiarkan metode Parse mengeluarkan pengecualian] begitu menjengkelkan sehingga tentu saja tim kerangka kerja mengimplementasikan TryParse tak lama kemudian yang melakukan hal yang benar.

Ya, tetapi TryParsedan TryParseExactkeduanya masih jauh dari nyaman untuk digunakan: Mereka memaksa Anda untuk menggunakan variabel yang tidak diinisialisasi sebagai outparameter yang tidak boleh nullable dan saat Anda mengonversi, Anda perlu mengevaluasi nilai pengembalian boolean - baik Anda memiliki untuk menggunakan ifpernyataan segera atau Anda harus menyimpan nilai kembali dalam variabel boolean tambahan sehingga Anda dapat melakukan pemeriksaan nanti. Dan Anda tidak bisa hanya menggunakan variabel target tanpa mengetahui apakah konversi berhasil atau tidak.

Dalam kebanyakan kasus, Anda hanya ingin tahu apakah konversi berhasil atau tidak (dan tentu saja nilainya jika berhasil) , sehingga variabel target yang dapat dibatalkan yang membuat semua informasi diinginkan dan jauh lebih elegan - karena seluruh informasi hanya disimpan di satu tempat: Itu konsisten dan mudah digunakan, dan jauh lebih rentan kesalahan.

Metode ekstensi yang saya tulis tidak persis seperti itu (itu juga menunjukkan kepada Anda jenis kode apa yang harus Anda tulis setiap kali jika Anda tidak akan menggunakannya).

Saya percaya manfaatnya .ToDate(strDateFormat)adalah tampilannya sederhana dan bersih - semudah DateTime.Parseyang seharusnya - tetapi dengan kemampuan untuk memeriksa apakah konversi berhasil, dan tanpa membuang pengecualian.


1) Yang dimaksud di sini adalah penanganan pengecualian (yaitu try { ... } catch(Exception ex) { ...}blok) - yang diperlukan saat Anda menggunakan Parse karena akan mengeluarkan pengecualian jika string yang tidak valid diurai - tidak hanya tidak perlu dalam kasus ini tetapi juga mengganggu, dan menyulitkan kode Anda. TryParse menghindari semua ini karena contoh kode yang saya berikan ditampilkan.


2) Eric Lippert adalah rekan StackOverflow yang terkenal dan bekerja di Microsoft sebagai pengembang utama di tim kompiler C # selama beberapa tahun.

Mat
sumber
13
var dateStr = @"2011-03-21 13:26";
var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm", CultureInfo.CurrentCulture);

Lihat tautan ini untuk string format lain!

Rob
sumber
4

Masukkan nilai string yang dapat dibaca manusia ke dalam .NET DateTime dengan kode seperti ini:

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

Jawaban sederhana dan langsung ->

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
sumber
Nice @Shivam Bharadwaj, saya melakukan hal yang sama
Muhammad Irfan
2

Anda juga dapat menggunakan XmlConvert.ToDateString

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

Baik untuk menentukan jenis tanggal, kodenya adalah:

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

Lebih detail tentang opsi penguraian yang berbeda http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html

Amir Shenouda
sumber
0

Coba kode berikut

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
sumber
Hai, Selamat Datang, Tolong berikan penjelasan saat menjawab pertanyaan. Hanya mengeposkan kode tidak disarankan
Ali