Như tôi sẽ giải thích sau, tôi sẽ luôn ủng hộ TryParse
và TryParseExact
phươ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
, ParseExact
vv 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 dt
nà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ì year
sẽ 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 InvariantCulture
gọ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 result
và dt
cùng nhau, vì TryParseExact
khô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 ToDate
chứ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;
và 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 dt
thay vì out var dt
nế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 ( TryParseExact
hỗ 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 TryParse
và TryParseExact
cả 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 if
câ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.