แยกสตริงเพื่อ DateTime ใน C #


165

ฉันมีวันที่และเวลาในรูปแบบสตริงเหมือนที่:

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

ฉันจะแยกวิเคราะห์มันได้System.DateTimeอย่างไร

ฉันต้องการใช้ฟังก์ชันเช่นDateTime.Parse()หรือDateTime.ParseExact()ถ้าเป็นไปได้เพื่อให้สามารถระบุรูปแบบของวันที่ด้วยตนเอง


19
เหตุใดคุณไม่ใช้ DateTime.Parse
Austin Salonen

8
ฉันเป็นหนึ่งในผู้ลงคะแนนเสียง เป็นเพราะคำถามดั้งเดิมของคุณ ( stackoverflow.com/revisions/… ) ระบุว่าคุณต้องการใช้ DateTime.Parse () แต่คุณไม่ได้ระบุว่าทำไมคุณไม่สามารถใช้งานได้ สิ่งนี้ทำให้ดูเหมือนคำถามไร้สาระโดยเฉพาะอย่างยิ่งเนื่องจากการตรวจสอบง่าย ๆ จะทำให้ชัดเจนว่า cacois นั้นถูกต้อง: สตริงของคุณ "2011-03-21 13:26" ไม่ใช่ปัญหาสำหรับ DateTime.Parse () สุดท้ายคุณไม่ได้พูดถึง ParseExact () ในคำถามดั้งเดิมของคุณ คุณรอจนกระทั่งหลังจากคำตอบของมิทช์เพื่อเพิ่มสิ่งนี้ในการแก้ไข
anon

4
ฉันรักคนที่โหวตคำถามโดยไม่ให้เหตุผลในการแสดงความคิดเห็น
Hooch

คำตอบ:


271

DateTime.Parse()จะลองหารูปแบบของวันที่ที่กำหนดและโดยปกติจะใช้งานได้ดี หากคุณสามารถรับประกันวันที่จะอยู่ในรูปแบบที่กำหนดคุณสามารถใช้ParseExact():

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

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

(แต่โปรดทราบว่าโดยปกติจะปลอดภัยกว่าที่จะใช้หนึ่งในวิธี TryParse ในกรณีที่วันที่ไม่ได้อยู่ในรูปแบบที่คาดไว้)

ตรวจสอบให้แน่ใจว่าได้ตรวจสอบ สตริงรูปแบบวันที่และเวลาที่กำหนดเองเมื่อสร้างสตริงรูปแบบโดยเฉพาะอย่างยิ่งให้คำนึงถึงจำนวนตัวอักษรและตัวพิมพ์เล็ก (เช่น "MM" และ "mm" หมายถึงสิ่งที่แตกต่างกันมาก)

ทรัพยากรที่มีประโยชน์อื่นสำหรับสตริงการจัดรูปแบบ C #คือการจัดรูปแบบสตริงใน C #


5
การแก้ไข - เป็นวิธีที่ปลอดภัยกว่าเสมอ) หากคุณเรียกใช้เมธอดที่มีข้อยกเว้นให้ตรวจสอบเงื่อนไขข้อยกเว้นก่อนเสมอถ้าเป็นไปได้
Gusdor

3
ฉันว่ามันปลอดภัยกว่าที่จะผ่านวัฒนธรรมของคุณ ฉันควรได้รับการยกเว้นมากกว่าการมี "01-02-2013" ถูกตีความผิดว่าเป็นวันที่สองของเดือนมกราคมหรือวันแรกของเดือนกุมภาพันธ์
Carra

1
@Carra: วันที่ในรูปแบบ ISO8601 (เช่น yyyy-mm-dd 'ถูกตีความเสมอวิธีที่ถูกต้อง That'; s ทำไมเราจึงใช้รูปแบบ ISO8601 ...
มิทช์ข้าวสาลี

การแยกวิเคราะห์ที่แน่นอนจะมีประโยชน์ บางครั้งฉันต้องการให้แอปพลิเคชันของฉันทำงานขัดข้องและไฟคอมพิวเตอร์ของฉันติดไฟซึ่งต่างจากการสร้างเอาต์พุตที่ไม่ถูกต้อง ขึ้นอยู่กับแอพพลิเคชั่น
อัลเลน

ParseExactดีมากเพราะมันมีความยืดหยุ่น แต่มันก็มีข้อเสีย: โปรดทราบว่า ParseExact sและการแยกวิเคราะห์วิธีการโยนข้อยกเว้นถ้ามีการผิดพลาดทางไวยากรณ์ในรูปแบบวันที่ของตัวแปร ดังนั้นจึงเป็นการดีกว่าที่จะใช้TryParseExcact ฉันได้ชี้ให้เห็นว่าทำไมในคำตอบของฉันด้านล่าง
Matt

47

เมื่อฉันอธิบายในภายหลังฉันมักจะชอบ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 # เป็นเวลาสองปี


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

ลองดูที่ลิงค์นี้สำหรับสตริงรูปแบบอื่น ๆ !



4

ใส่ค่าของสตริงที่มนุษย์สามารถอ่านได้ลงใน. NET DateTime ด้วยรหัสดังนี้:

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

2

คำตอบที่ง่ายและตรงไปตรงมา ->

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 ฉันทำแบบเดียวกัน
Muhammad Irfan

2

คุณยังสามารถใช้ XmlConvert.ToDateString

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

มันเป็นการดีที่จะระบุประเภทวันที่รหัสคือ:

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

รายละเอียดเพิ่มเติมเกี่ยวกับตัวเลือกการแยกวิเคราะห์ที่แตกต่างกันhttp://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html


0

ลองรหัสต่อไปนี้

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

สวัสดียินดีต้อนรับโปรดให้คำอธิบายเมื่อตอบคำถาม ไม่แนะนำให้โพสต์รหัสเพียง
อาลี
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.