C # 'da DateTime dizesini ayrıştırma


166

Ben tarih ve saati bir gibi biçimlendirilmiş bir dizede:

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

Nasıl ayrıştırabilirim System.DateTime?

Tarih biçimini manuel olarak belirleyebilmek için DateTime.Parse()veya DateTime.ParseExact()mümkünse işlevleri kullanmak istiyorum .


19
Öyleyse neden DateTime.Parse kullanmıyorsunuz?
Austin Salonen

8
Ben aşağılıklardan biriydim. Bunun nedeni, orijinal sorunuzun ( stackoverflow.com/revisions/… ) DateTime.Parse () 'ı kullanmak İSTEMEDİĞİNİZ olduğunu, ancak NEDEN kullanamayacağınızı belirtmemenizdi. Bu, saçma bir soru gibi görünüyordu, çünkü basit bir kontrol cacois'in doğru olduğunu açıklığa kavuşturacağı için: "2011-03-21 13:26" dizeniz DateTime.Parse () için bir sorun değil. Son olarak, orijinal sorunuzda ParseExact () öğesinden bahsetmediniz. Sen kadar bekledi sonra Mitch'in cevabı bir düzenleme bu ekleyin.
anon

4
Yorumlarda herhangi bir gerekçe göstermeden bu insanların oy kullanma sorusunu seviyorum.
Hooch

Yanıtlar:


272

DateTime.Parse()verilen tarihin biçimini anlamaya çalışır ve genellikle iyi bir iş çıkarır. Tarihlerin her zaman belirli bir biçimde olacağını garanti ediyorsanız şunları kullanabilirsiniz ParseExact():

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

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

(Ancak, bir tarihin beklenen biçimde olmaması durumunda TryParse yöntemlerinden birini kullanmanın daha güvenli olduğunu unutmayın)

Özel Tarih ve Saat Biçimi Dizelerini kontrol ettiğinizden emin olun dizesi oluştururken , özellikle harflerin ve büyük / küçük harflerin sayısına dikkat edin (örn. "MM" ve "mm" çok farklı anlamlara gelir).

C # biçim dizeleri için başka bir yararlı kaynak C # dize biçimlendirme


5
Düzeltme - HER ZAMAN daha güvenlidir;) İstisna olan bir yöntemi arıyorsanız, mümkünse her zaman önce istisna durumunu kontrol edin.
Gusdor

3
Kültürünüzü her zaman geçirmenin daha güvenli olduğunu söyleyebilirim. "01-02-2013 "'ün Ocak ayının ikincisi veya Şubat ayının ilk günü olarak yanlış yorumlanmasından çok bir istisnam var.
Carra

1
@Carra: ISO8601 biçimindeki tarihler (yani yyyy-aa-gg 'her zaman doğru şekilde yorumlanır. Bu nedenle ISO8601 biçimindeki tarihleri ​​kullanıyoruz ...
Mitch Wheat

Kesin ayrıştırma yararlı olabilir. Bazen, yanlış çıktı üretmenin aksine, uygulama kilitlenmemi ve bilgisayar ışığımın yanmasını tercih ederim. Uygulamaya bağlıdır.
Allen

ParseExact , esnek olması nedeniyle mükemmeldir, ancak bir dezavantajı vardır: Değişkenin tarih biçiminde bir sözdizimi hatası varsa ParseExact ve Parse yöntemlerinin istisnalar attığını unutmayın s. Bu nedenle, TryParseExcact kullanmak daha iyidir . Aşağıdaki cevabımda neden olduğuna dikkat çektim.
Matt

47

Daha sonra açıklayacağım gibi, her zaman TryParseve TryParseExactyöntemlerini tercih ederim . Kullanımı biraz hantal oldukları için, ayrıştırmayı çok daha kolay hale getiren bir uzantı yöntemi yazdım :

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

Aksine Parse, ParseExactbir istisna atmaz ve üzerinden kontrol etmenizi sağlar

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

dönüşümün başarılı olup olmadığı (bu durumda dtüzerinden erişebileceğiniz bir değere sahipse dt.Value) ya da değil (bu durumda null).

Bu, "Elvis" -operator gibi zarif kısayolların kullanılmasına bile izin verir ?., örneğin:

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

Burada year.HasValue, dönüşümün başarılı olup olmadığını ve başarılı yearolamadıysa null, aksi takdirde tarihin yıl bölümünü içerecek şekilde kontrol etmek için de kullanabilirsiniz . Dönüşüm başarısız olursa herhangi bir istisna söz konusu değildir.


Çözüm:   .ToDate () uzantı yöntemi

Deneyin. 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;
    }
}

Kod hakkında bazı bilgiler

Ben kullandım neden, merak edebilirsiniz InvariantCultureçağırarak TryParseExact: Bu, örneğin (Aksi daima tedavi biçimi desenleri aynı şekilde işlev zorlamak İngilizce ondalık ayırıcı olarak yorumlanabilecek bir grup ayırıcı iken "" veya bir tarih ayırıcı içinde Almanca). Daha önce kültür bazlı format dizelerini birkaç satır önce sorguladığımızı hatırlayın, böylece burada sorun yok.

Güncelleme: .ToDate() (parametreler olmadan) artık iş parçacığının geçerli kültürünün tüm ortak tarih / saat kalıplarını varsayılan olarak kullanıyor. Birlikte ve birlikte ihtiyacımız olduğunu
unutmayın , çünkü geri dönmek istediğimiz kullanıma izin vermez . In C # Sürüm 7 Eğer basitleştirmek olabilir aşağıdaki gibi biraz fonksiyonu:resultdtTryParseExactDateTime?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;

veya daha da kısa isterseniz:

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

bu durumda iki bildirime ihtiyacınız yoktur DateTime? result = null;ve DateTime dt;hiç bir şekilde bunu tek bir kod satırında yapabilirsiniz. (Yazmak out DateTime dtyerine yazmasına da izin verilir .out var dt İsterseniz).

paramsAnahtar kelimeyi kullanarak kodu daha da basitleştirdim : Artık 2. aşırı yüklenmiş yönteme artık ihtiyacınız yok .


Kullanım örneği

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

Gördüğünüz gibi, bu örnek dt.HasValuedönüşümün başarılı olup olmadığını sorgular . Ekstra bir bonus olarak, TryParseExact kesin olarak belirtilmesine izin verir, DateTimeStylesböylece uygun bir tarih / saat dizesinin geçilip geçilmediğini tam olarak bilirsiniz.


Daha fazla kullanım örneği

Aşırı yüklenmiş işlev, burada gösterildiği gibi tarihleri ​​ayrıştırmak / dönüştürmek için kullanılan bir dizi geçerli formatı geçirmenize olanak tanır ( doğrudan bunu destekler), örn.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);

Yalnızca birkaç şablon deseniniz varsa, şunları da yazabilirsiniz:

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

Gelişmiş örnekler

Sen kullanabilirsiniz ??fail-safe biçimi, örneğin varsayılan operatöre

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

Bu durumda, .ToDate()ortak yerel kültür tarihi biçimlerini kullanır ve tüm bunlar başarısız olursa, ISO standart biçimini kullanmaya çalışır."yyyy-MM-dd HH:mm:ss" bir yedek olarak . Bu şekilde uzatma işlevi, farklı geri dönüş biçimlerini kolayca "zincirlemenizi" sağlar.

Uzantıyı LINQ'da bile kullanabilirsiniz, bunu deneyin (yukarıdaki .NetFiddle'da bulunur):

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

desenleri kullanarak dizideki tarihleri ​​anında dönüştürecek ve bunları konsola dökecektir.


TryParseExact hakkında bazı bilgiler

Son olarak, İşte arka plan hakkında bazı yorumlar (yani neden bu şekilde yazdım):

Ben tercih ediyorum TryParseExact nedeniyle, bu uzantı yönteminde durum işleme önlemek yapabilirsiniz - İstisnalar hakkında Eric Lippert en makalesinde okumak : Bunun yerine Parse daha TryParse kullanmalısınız neden, bu konu hakkında ona alıntı ) 2

Bu talihsiz tasarım kararı 1) [ek açıklama: Ayrıştırma yönteminin bir istisna atmasına izin vermek], çerçeve ekibinin kısa bir süre sonra TryParse'yi uyguladığı doğru bir şey yapıyordu .

Kullanıyor , ancak TryParseve TryParseExacther ikisi de hala kullanmaktan çok daha az: Başlatılmamış bir değişkeni outparametre olarak kullanılmaya zorlayan bir parametre olarak kullanmaya zorlar ve dönüştürürken boolean dönüş değerini değerlendirmeniz gerekir. kullanmakif ifadeyi hemen veya dönüş değerini ek bir boole değişkeninde saklamanız gerekir; böylece daha sonra kontrolü yapabilirsiniz. Dönüşümün başarılı olup olmadığını bilmeden hedef değişkeni kullanamazsınız.

Çoğu durumda , dönüşümün başarılı olup olmadığını (ve tabii ki başarılı olursa değeri) bilmek istersiniz , bu nedenle boş bir hedef değişken tüm bilgileri tutan arzu edilir ve çok daha zarif olur - çünkü tüm bilgiler sadece tek bir yerde saklanır: Bu tutarlı ve kullanımı kolaydır ve çok daha az hataya açıktır.

Yazdığım uzatma yöntemi tam olarak bunu yapıyor (ayrıca kullanmayacağınız her seferinde ne tür bir kod yazmanız gerektiğini gösterir).

Bunun yararı .ToDate(strDateFormat), orijinalin DateTime.Parseolması gerektiği kadar basit ve temiz göründüğü , ancak dönüşümün başarılı olup olmadığını ve istisnalar atmadan kontrol edebilme yeteneği olduğuna inanıyorum .


1) Burada kastedilen, geçersiz bir dize ayrıştırılırsa bir istisna atacağı için Parse'yi kullandığınızda gerekli olan istisna işlemenin (yani bir try { ... } catch(Exception ex) { ...}blok) sadece bu durumda değil, aynı zamanda can sıkıcı ve kodunuzu karmaşık hale getirir. Sağladığım kod örneği gösterdiği için TryParse tüm bunları önler.


2) Eric Lippert ünlü bir StackOverflow üyesidir ve Microsoft'ta C # derleyici ekibinde birkaç yıl asıl geliştirici olarak çalışıyordu.


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

Diğer biçim dizeleri için bu bağlantıya göz atın !



4

İnsan tarafından okunabilir bir dizenin değerini, şöyle bir kodla bir .NET DateTime içine yerleştirin:

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

2

Basit ve anlaşılır cevap ->

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.
 * */

Nice @Shivam Bharadwaj, aynı şekilde yaptım
Muhammad Irfan

2

XmlConvert.ToDateString öğesini de kullanabilirsiniz

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

Tarih türünü belirtmek iyidir, kod:

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

Farklı ayrıştırma seçenekleri hakkında daha fazla bilgi http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html


0

Aşağıdaki kodu deneyin

Month = Date = DateTime.Now.Month.ToString();   
Year = DateTime.Now.Year.ToString(); 
ViewBag.Today = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(Int32.Parse(Month)) + Year;

Merhaba, Hoş Geldiniz, Lütfen bir soruya cevap verirken açıklama yapın. Sadece posta kodu önerilmez
Ali
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.