Phân tích chuỗi thành DateTime trong C #


165

Tôi có ngày và giờ trong một chuỗi được định dạng như thế:

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

Làm thế nào tôi có thể phân tích nó System.DateTime?

Tôi muốn sử dụng các chức năng như DateTime.Parse()hoặc DateTime.ParseExact()nếu có thể, để có thể chỉ định định dạng của ngày theo cách thủ công.


19
Vậy tại sao bạn không sử dụng DateTime.Pude?
Austin Salonen

8
Tôi là một trong những người downvoters. Đó là bởi vì câu hỏi ban đầu của bạn ( stackoverflow.com/revutions/và ) đã nói rằng bạn MUỐN sử dụng DateTime.Pude () nhưng bạn không nói rõ TẠI SAO bạn không thể sử dụng nó. Điều này làm cho nó có vẻ như là một câu hỏi vô nghĩa, đặc biệt là vì một kiểm tra đơn giản sẽ làm rõ rằng cacois là chính xác: Chuỗi của bạn "2011 / 03-21 13:26" không phải là vấn đề đối với DateTime.Pude (). Cuối cùng, bạn đã không thực hiện bất kỳ đề cập nào về ParseExact () trong câu hỏi ban đầu của bạn. Bạn đã đợi cho đến sau câu trả lời của Mitch để thêm phần này vào bản chỉnh sửa.
anon

4
Tôi chỉ yêu những người bỏ phiếu mà không đưa ra bất kỳ lý do nào trong các bình luận.
Hooch

Câu trả lời:


271

DateTime.Parse()sẽ cố gắng tìm ra định dạng của ngày đã cho và nó thường hoạt động tốt. Nếu bạn có thể đảm bảo ngày sẽ luôn ở một định dạng nhất định thì bạn có thể sử dụng ParseExact():

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

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

(Nhưng lưu ý rằng thường an toàn hơn khi sử dụng một trong các phương thức TryPude trong trường hợp ngày không ở định dạng mong đợi)

Đảm bảo kiểm tra Chuỗi định dạng ngày và giờ tùy chỉnh khi xây dựng chuỗi định dạng, đặc biệt chú ý đến số lượng chữ và trường hợp (nghĩa là "MM" và "mm" có nghĩa là những thứ rất khác nhau).

Một tài nguyên hữu ích khác cho các chuỗi định dạng C # là Định dạng chuỗi trong C #


5
Sửa lỗi - LUÔN LUÔN an toàn hơn;) Nếu bạn đang gọi một phương thức có ngoại lệ, luôn luôn kiểm tra điều kiện ngoại lệ trước nếu có thể.
Gusdor

3
Tôi muốn nói rằng sẽ an toàn hơn khi luôn vượt qua văn hóa của bạn. Tôi muốn có một ngoại lệ hơn là "01-02-2013" bị hiểu sai là ngày thứ hai của tháng một hoặc đầu tiên của tháng hai.
Carra

1
@Carra: ngày ở định dạng ISO8601 (ví dụ: yyyy-mm-dd 'luôn được hiểu theo cách chính xác. Đó là lý do tại sao chúng tôi sử dụng ngày định dạng ISO8601 ...
Mitch Wheat

Phân tích chính xác có thể hữu ích. Đôi khi, tôi thích ứng dụng của mình bị sập và đèn máy tính bốc cháy, trái ngược với việc tạo ra đầu ra không chính xác. Phụ thuộc vào ứng dụng.
Allen

ParseExact là tuyệt vời vì nó linh hoạt, nhưng nó có một nhược điểm: Lưu ý rằng các phương thức ParseExact và Parse đưa ra các ngoại lệ nếu có lỗi cú pháp trong định dạng ngày của biến s. Do đó, tốt hơn là sử dụng TryPudeExcact. Tôi đã chỉ ra lý do tại sao trong câu trả lời của tôi dưới đây.
Matt

47

Như tôi sẽ giải thích sau, tôi sẽ luôn ủng hộ TryParseTryParseExactphương pháp. Vì chúng hơi cồng kềnh khi sử dụng, tôi đã viết một phương thức mở rộng giúp phân tích cú pháp dễ dàng hơn nhiều:

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

Không giống như Parse, ParseExactvv nó không ném ngoại lệ và cho phép bạn kiểm tra thông qua

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

việc chuyển đổi có thành công hay không (trong trường hợp dtnày có giá trị bạn có thể truy cập qua dt.Value) hay không (trong trường hợp này là vậy null).

Điều đó thậm chí cho phép sử dụng các phím tắt thanh lịch như trình điều khiển "Elvis" ?., ví dụ:

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

Tại đây, bạn cũng có thể sử dụng year.HasValueđể kiểm tra xem chuyển đổi có thành công hay không và nếu nó không thành công thì yearsẽ có phần null, nếu không thì phần năm của ngày. Không có ngoại lệ ném nếu chuyển đổi không thành công.


Giải pháp:   Phương thức mở rộng .ToDate ()

Hãy thử nó trong .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;
    }
}

Một số thông tin về mã

Bạn có thể tự hỏi, tại sao tôi lại sử dụng cách InvariantCulturegọi TryParseExact: Điều này là để buộc hàm xử lý các mẫu định dạng luôn theo cùng một cách (ví dụ: "." Có thể được hiểu là dấu tách thập phân trong tiếng Anh trong khi đó là dấu tách nhóm hoặc dấu tách ngày trong Tiếng Đức). Hãy nhớ lại rằng chúng tôi đã truy vấn các chuỗi định dạng dựa trên văn hóa một vài dòng trước đây vì vậy điều đó là ổn ở đây.

Cập nhật: .ToDate() (không có tham số) hiện mặc định cho tất cả các mẫu ngày / giờ phổ biến của văn hóa hiện tại của luồng.
Lưu ý rằng chúng ta cần resultdtcùng nhau, vì TryParseExactkhông cho phép sử dụngDateTime? , mà chúng tôi dự định sẽ trả lại. Trong C # Phiên bản 7, bạn có thể đơn giản hóa ToDatechức năng một chút như sau:

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

hoặc, nếu bạn thích nó thậm chí còn ngắn hơn:

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

trong trường hợp bạn không cần hai tờ khai DateTime? result = null;DateTime dt;tất cả - bạn có thể thực hiện trong một dòng mã. (Nó cũng sẽ được phép viết out DateTime dtthay vì out var dtnếu bạn thích điều đó).

Tôi đã đơn giản hóa mã hơn nữa bằng cách sử dụng params từ khóa: Bây giờ bạn không cần phương thức quá tải thứ 2 nữa.


Ví dụ về cách sử dụng

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

Như bạn có thể thấy, ví dụ này chỉ là truy vấn dt.HasValue để xem liệu chuyển đổi có thành công hay không. Là một phần thưởng bổ sung, TryPudeExact cho phép chỉ định nghiêm ngặt DateTimeStylesđể bạn biết chính xác liệu chuỗi ngày / thời gian thích hợp đã được thông qua hay chưa.


Thêm ví dụ về việc sử dụng

Hàm quá tải cho phép bạn vượt qua một loạt các định dạng hợp lệ được sử dụng để phân tích cú pháp / ngày chuyển đổi như được hiển thị ở đây ( TryParseExacthỗ trợ trực tiếp điều này), ví dụ:

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

Nếu bạn chỉ có một vài mẫu mẫu, bạn cũng có thể viết:

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

Ví dụ nâng cao

Bạn có thể sử dụng ??toán tử để mặc định ở định dạng không an toàn, ví dụ:

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

Trong trường hợp này, .ToDate()sẽ sử dụng các định dạng ngày văn hóa địa phương phổ biến và nếu tất cả những điều này không thành công, nó sẽ cố gắng sử dụng định dạng tiêu chuẩn ISO"yyyy-MM-dd HH:mm:ss" làm dự phòng. Bằng cách này, chức năng mở rộng cho phép "xâu chuỗi" các định dạng dự phòng khác nhau một cách dễ dàng.

Bạn thậm chí có thể sử dụng tiện ích mở rộng trong LINQ, hãy thử điều này (nó có trong .NetFiddle ở trên):

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

sẽ chuyển đổi ngày trong mảng một cách nhanh chóng bằng cách sử dụng các mẫu và đổ chúng vào bàn điều khiển.


Một số nền tảng về TryPudeExact

Cuối cùng, đây là một số ý kiến ​​về nền (tức là lý do tại sao tôi đã viết nó theo cách này):

Tôi thích dùng TryPudeExact trong phương thức mở rộng này, vì bạn tránh xử lý ngoại lệ - bạn có thể đọc trong bài viết của Eric Lippert về các trường hợp ngoại lệ tại sao bạn nên sử dụng TryPude thay vì Parse, tôi trích dẫn anh ấy về chủ đề đó: 2)

Quyết định thiết kế không may này 1) [chú thích: để cho phương thức Parse ném ngoại lệ] đã gây phiền toái đến mức tất nhiên nhóm các khung công tác đã triển khai TryPude ngay sau đó , điều này đúng.

Nó có, nhưng TryParseTryParseExactcả hai vẫn ít thoải mái hơn khi sử dụng: Họ buộc bạn phải sử dụng một biến chưa được khởi tạo như mộtout tham số mà không phải nullable và trong khi bạn đang chuyển đổi bạn cần để đánh giá giá trị trả về boolean - hoặc bạn có để sử dụng một ifcâu lệnh ngay lập tức hoặc bạn phải lưu trữ giá trị trả về trong một biến boolean bổ sung để bạn có thể thực hiện kiểm tra sau. Và bạn không thể chỉ sử dụng biến mục tiêu mà không biết liệu chuyển đổi có thành công hay không.

Trong hầu hết các trường hợp, bạn chỉ muốn biết liệu chuyển đổi có thành công hay không (và tất nhiên là giá trị nếu nó thành công) , do đó, một biến mục tiêu không thể bỏ qua giữ được tất cả thông tin sẽ được mong muốn và thanh lịch hơn nhiều - bởi vì toàn bộ thông tin là chỉ được lưu trữ ở một nơi: Điều đó phù hợp và dễ sử dụng, và ít xảy ra lỗi hơn.

Phương thức mở rộng mà tôi đã viết thực hiện chính xác điều đó (nó cũng cho bạn biết loại mã nào bạn sẽ phải viết mỗi lần nếu bạn không sử dụng nó).

Tôi tin rằng lợi ích của .ToDate(strDateFormat)nó là trông đơn giản và sạch sẽ - đơn giản như bản gốc DateTime.Parseđược cho là - nhưng với khả năng kiểm tra xem chuyển đổi có thành công hay không và không có ngoại lệ.


1) Điều gì có nghĩa ở đây là xử lý ngoại lệ (tức là một try { ... } catch(Exception ex) { ...}khối) - điều cần thiết khi bạn sử dụng Parse vì nó sẽ ném ngoại lệ nếu một chuỗi không hợp lệ được phân tích cú pháp - không chỉ không cần thiết trong trường hợp này mà còn gây khó chịu và làm phức tạp mã của bạn. TryPude tránh tất cả điều này như mẫu mã tôi đã cung cấp đang hiển thị.


2) Eric Lippert là một thành viên nổi tiếng của StackOverflow và đã làm việc tại Microsoft với tư cách là nhà phát triển chính trong nhóm trình biên dịch C # trong một vài năm.


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

Kiểm tra liên kết này cho các chuỗi định dạng khác!



4

Đặt giá trị của chuỗi có thể đọc được của con người vào .NET DateTime với mã như sau:

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

2

Câu trả lời đơn giản và dễ hiểu ->

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, tôi đã làm theo cách tương tự
Muhammad Irfan

2

Bạn cũng có thể sử dụng XmlConvert.ToDateString

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

Nó là tốt để xác định loại ngày, mã là:

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

Thêm chi tiết về các tùy chọn phân tích cú pháp khác nhau http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html


0

Hãy thử đoạn mã sau

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

Xin chào, Chào mừng, Vui lòng cung cấp một lời giải thích khi trả lời một câu hỏi. Chỉ nên đăng mã không được
Ali
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.