เมื่อฉันอธิบายในภายหลังฉันมักจะชอบTryParse
และTryParseExact
วิธีการ เนื่องจากพวกเขาใช้งานค่อนข้างใหญ่ฉันจึงเขียนวิธีการขยายซึ่งทำให้การแยกวิเคราะห์ง่ายขึ้นมาก:
var dtStr = "2011-03-21 13:26";
DateTime? dt = dtStr.ToDate("yyyy-MM-dd HH:mm");
ซึ่งแตกต่างจากParse
, ParseExact
ฯลฯ มันไม่ได้โยนข้อยกเว้นและช่วยให้คุณสามารถตรวจสอบผ่านทาง
if (dt.HasValue) { // continue processing } else { // do error handling }
การแปลงสำเร็จหรือไม่ (ในกรณีนี้dt
มีค่าที่คุณสามารถเข้าถึงได้dt.Value
) หรือไม่ (ในกรณีนี้คือnull
)
แม้จะอนุญาตให้ใช้ทางลัดที่หรูหราเช่น "Elvis" - ตัวดำเนิน?.
การตัวอย่างเช่น:
int? year = dtStr?.ToDate("yyyy-MM-dd HH:mm")?.Year;
ที่นี่คุณสามารถใช้year.HasValue
เพื่อตรวจสอบว่าการแปลงประสบความสำเร็จหรือไม่และหากไม่สำเร็จyear
จะมีnull
หรือไม่เช่นนั้นส่วนปีของวันที่ จะไม่มีข้อยกเว้นเกิดขึ้นหากการแปลงล้มเหลว
การแก้ไข: วิธีการขยาย. ToDate ()
ลองใน. 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;
}
}
ข้อมูลบางอย่างเกี่ยวกับรหัส
คุณอาจสงสัยว่าทำไมฉันใช้InvariantCulture
โทรTryParseExact
: นี่คือการบังคับให้ฟังก์ชั่นในการรักษารูปแบบรูปแบบเสมอในลักษณะเดียวกัน (เช่น "." สามารถตีความได้ว่าเป็นตัวแยกทศนิยมเป็นภาษาอังกฤษในขณะที่มันเป็นตัวคั่นกลุ่มหรือตัวคั่นวันที่ ภาษาเยอรมัน) จำได้ว่าเราได้สอบถามสายอักขระรูปแบบที่อิงกับวัฒนธรรมมาก่อนหน้าแล้วสองสามบรรทัด
อัปเดต: .ToDate()
(โดยไม่มีพารามิเตอร์) เริ่มต้นเป็นรูปแบบวันที่ / เวลาร่วมกันทั้งหมดของวัฒนธรรมปัจจุบันของเธรด
โปรดทราบว่าเราต้องการresult
และdt
ร่วมกันเพราะTryParseExact
ไม่อนุญาตให้ใช้DateTime?
ซึ่งเราตั้งใจจะกลับมา ในC # เวอร์ชัน 7คุณสามารถทำให้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;
หรือถ้าคุณชอบมันยิ่งสั้นลง:
// 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?;
ในกรณีนี้คุณไม่จำเป็นต้องมีการประกาศทั้งสองDateTime? result = null;
และDateTime dt;
เลยคุณสามารถทำได้ในหนึ่งบรรทัดของรหัส (นอกจากนี้ยังอนุญาตให้เขียนout DateTime dt
แทนout var dt
หากคุณต้องการ)
ฉันได้ลดความซับซ้อนของรหัสเพิ่มเติมโดยใช้params
คำหลัก: ตอนนี้คุณไม่จำเป็นต้องใช้วิธีที่มากเกินไปครั้งที่ 2 อีกต่อไป
ตัวอย่างการใช้งาน
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!");
}
อย่างที่คุณเห็นตัวอย่างนี้เป็นข้อความค้นหาdt.HasValue
เพื่อดูว่าการแปลงสำเร็จหรือไม่ ในฐานะโบนัสพิเศษ TryParseExact อนุญาตให้ระบุอย่างเข้มงวดDateTimeStyles
เพื่อให้คุณรู้แน่ชัดว่าสตริงวันที่ / เวลาที่เหมาะสมได้ถูกส่งผ่านหรือไม่
ตัวอย่างเพิ่มเติมของการใช้งาน
ฟังก์ชั่นโอเวอร์โหลดช่วยให้คุณผ่านอาร์เรย์ของรูปแบบที่ถูกต้องที่ใช้สำหรับการแยก / วันที่แปลงตามที่แสดงที่นี่เช่นกัน ( 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);
หากคุณมีรูปแบบเทมเพลตเพียงไม่กี่ตัวคุณสามารถเขียน:
var dateStr = "2011-03-21 13:26";
var dt = dateStr.ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");
ตัวอย่างขั้นสูง
คุณสามารถใช้??
โอเปอเรเตอร์เพื่อตั้งค่าเริ่มต้นเป็นรูปแบบที่ไม่ปลอดภัยเช่น
var dtStr = "2017-12-30 11:37:00";
var dt = (dtStr.ToDate()) ?? dtStr.ToDate("yyyy-MM-dd HH:mm:ss");
ในกรณีนี้.ToDate()
รูปแบบจะใช้รูปแบบวันที่วัฒนธรรมท้องถิ่นทั่วไปและหากสิ่งเหล่านี้ล้มเหลวก็จะพยายามใช้รูปแบบมาตรฐาน ISO"yyyy-MM-dd HH:mm:ss"
เป็นทางเลือก ด้วยวิธีนี้ฟังก์ชั่นส่วนต่อขยายช่วยให้ "เชน" รูปแบบทางเลือกที่แตกต่างกันได้อย่างง่ายดาย
คุณสามารถใช้ส่วนขยายใน LINQ ลองใช้ (ในส่วนของ. NetFiddle ด้านบน):
var patterns=new[] { "dd-MM-yyyy", "dd.MM.yyyy" };
(new[] { "15-01-2019", "15.01.2019" }).Select(s => s.ToDate(patterns)).Dump();
ซึ่งจะแปลงวันที่ในอาร์เรย์ได้ทันทีโดยใช้รูปแบบและถ่ายโอนข้อมูลไปยังคอนโซล
พื้นหลังบางส่วนเกี่ยวกับ TryParseExact
ในที่สุดนี่คือความคิดเห็นเกี่ยวกับพื้นหลัง (เช่นเหตุผลที่ฉันเขียนด้วยวิธีนี้):
ฉันชอบTryParseExactในวิธีการขยายนี้เนื่องจากคุณหลีกเลี่ยงการจัดการข้อยกเว้น - คุณสามารถอ่านบทความของ Eric Lippert เกี่ยวกับข้อยกเว้นว่าทำไมคุณควรใช้ TryParse มากกว่า Parse ฉันพูดถึงหัวข้อนี้: 2)
การตัดสินใจออกแบบที่โชคร้ายนี้ 1) [คำอธิบายประกอบ: เพื่อให้วิธีการแยกวิเคราะห์เกิดข้อยกเว้น] ได้รบกวนอย่างแน่นอนว่า
ทีมเฟรมเวิร์กใช้งาน TryParse หลังจากนั้นไม่นาน ซึ่งทำสิ่งที่ถูกต้อง
มันทำได้ แต่TryParse
และTryParseExact
ทั้งคู่ก็ยังน้อยกว่าความสะดวกสบายในการใช้งาน: พวกมันบังคับให้คุณใช้ตัวแปร uninitialized เป็นout
พารามิเตอร์ที่ต้องไม่เป็นโมฆะและในขณะที่คุณกำลังแปลงคุณต้องประเมินค่าผลตอบแทนบูลีน - ทั้งที่คุณมี เพื่อใช้if
คำสั่งทันทีหรือคุณต้องเก็บค่าส่งคืนในตัวแปรบูลีนเพิ่มเติมเพื่อให้คุณสามารถตรวจสอบได้ในภายหลัง และคุณไม่สามารถใช้ตัวแปรเป้าหมายโดยไม่ทราบว่าการแปลงสำเร็จหรือไม่
ในกรณีส่วนใหญ่คุณเพียงแค่ต้องการทราบว่าการแปลงนั้นประสบความสำเร็จหรือไม่ (และแน่นอนว่ามันคุ้มค่าหากสำเร็จ)ดังนั้นตัวแปรเป้าหมายที่ไม่มีค่าซึ่งทำให้ข้อมูลทั้งหมดจะเป็นที่ต้องการและสวยงามมากขึ้น - เนื่องจากข้อมูลทั้งหมดเป็น เก็บไว้ในที่เดียว: นั่นคือความสอดคล้องและใช้งานง่ายและข้อผิดพลาดน้อยมาก
วิธีการขยายที่ฉันเขียนนั้นทำอย่างนั้น (มันยังแสดงให้คุณเห็นว่าคุณต้องเขียนโค้ดประเภทใดทุกครั้งหากคุณไม่ได้ใช้งาน)
ฉันเชื่อว่าประโยชน์ของ.ToDate(strDateFormat)
มันคือดูเรียบง่ายและสะอาด - เรียบง่ายเหมือนต้นฉบับDateTime.Parse
ควรจะเป็น - แต่ด้วยความสามารถในการตรวจสอบว่าการแปลงสำเร็จหรือไม่และไม่มีข้อยกเว้น
1)สิ่งที่มีความหมายที่นี่คือการจัดการข้อยกเว้น (เช่นtry { ... } catch(Exception ex) { ...}
บล็อก) - ซึ่งเป็นสิ่งจำเป็นเมื่อคุณใช้การแยกวิเคราะห์เพราะมันจะโยนข้อยกเว้นถ้าสตริงที่ไม่ถูกต้องจะถูกแยกวิเคราะห์ - ไม่เพียงไม่จำเป็นในกรณีนี้ แต่ยังน่ารำคาญและ ทำให้รหัสของคุณซับซ้อนขึ้น TryParse หลีกเลี่ยงสิ่งนี้ทั้งหมดตามตัวอย่างโค้ดที่ฉันให้ไว้
2) Eric Lippert เป็นStackOverflowชื่อดังและทำงานที่ Microsoft ในฐานะผู้พัฒนาหลักในทีมคอมไพเลอร์ C # เป็นเวลาสองปี