วิธีการสร้าง. NET DateTime จากรูปแบบ ISO 8601


130

ฉันพบวิธีเปลี่ยน DateTime ให้เป็นรูปแบบISO 8601แต่ไม่มีอะไรเกี่ยวกับวิธีทำ reverse ใน C #

ฉันมี2010-08-20T15:00:00Zและฉันต้องการที่จะทำให้มันกลายเป็นDateTimeวัตถุ

ฉันสามารถแยกส่วนของสายตัวเองออกมาได้ แต่ดูเหมือนว่าจะมีงานมากมายสำหรับบางสิ่งที่เป็นมาตรฐานสากลอยู่แล้ว


1
เป็นไปได้ซ้ำกันของการแปลงสตริงเป็นวันที่ใน. NET
abatishchev

1
@Aidin: 24 ส.ค. 53เวลา 12:02 น.
abatishchev

@Aidin: และใช่นี้ซ้ำกัน ความแตกต่างในรูปแบบเท่านั้น ที่เหลือก็เหมือนกัน
abatishchev

6
@abatishchev และนั่นคือเหตุผลว่าทำไมจึงไม่ซ้ำกัน คำตอบใน "ซ้ำ" ไม่ได้จัดการ 8601
Spiralis

3
ใช่มันไม่ซ้ำกัน คำถามนี้เฉพาะสำหรับการแยกวิเคราะห์รูปแบบ ISO 8601
Jose

คำตอบ:


142

โซลูชันนี้ใช้การแจงนับDateTimeStylesและทำงานร่วมกับ Z

DateTime d2 = DateTime.Parse("2010-08-20T15:00:00Z", null, System.Globalization.DateTimeStyles.RoundtripKind);

วิธีนี้จะแก้ปัญหาได้อย่างสมบูรณ์แบบ


3
วิธีแก้ไขที่แก้ไขแล้วDateTime d2= DateTime.Parse("2010-08-20T15:00:00Z", null, DateTimeStyles.RoundtripKind);ดูเหมือนว่าจะทำงานได้ดี
j3ko

4
ใครก็ตามที่ต้องการอธิบายอย่างละเอียดเกี่ยวกับDateTimeStyles.RoundtripKind? คำอธิบาย MSDN นี้จะว่างเปล่า
Steve Parish

8
ดูเหมือนว่าคำถามนี้ได้รับการแก้ไขเพื่อให้สะท้อนถึงคำตอบที่ดีกว่า แต่เนื่องจาก @MamtaD เขียนทับคำตอบเดิมความคิดเห็นนั้นกลายเป็นความเข้าใจผิดมาก ที่จุดเริ่มต้นฉันไม่แน่ใจว่าคำตอบนั้นถูกต้องเนื่องจากความคิดเห็นด้านบน แต่แล้วฉันก็ตระหนักว่าคำตอบที่ไม่ถูกต้องถูกแทนที่ด้วยคำตอบที่ถูกต้อง
Aidin

5
ใช้งานไม่ได้สำหรับฉันด้วยเศษส่วน 2018-06-19T14:56:14.123Zมีการแยกวิเคราะห์เป็นเวลาท้องถิ่นไม่ใช่ UTC ฉันใช้CultureInfo.InvariantCultureแทน null
Erik Hart

1
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการDateTimeStyles.RoundTripKindดูstackoverflow.com/q/39572395/2014893
โรเบิร์ตเคเบลล์

33

แม้ว่า MSDN จะบอกว่ารูปแบบ "s" และ "o" นั้นสะท้อนถึงมาตรฐาน แต่ดูเหมือนว่าพวกเขาจะสามารถแยกวิเคราะห์ได้เพียงบางส่วนเท่านั้น โดยเฉพาะอย่างยิ่งมันเป็นปัญหาถ้าสตริงมีข้อกำหนดของเขตเวลา (ไม่ใช่สำหรับรูปแบบ ISO8601 พื้นฐานหรือรูปแบบความแม่นยำลดลง แต่นี่ไม่ใช่กรณีของคุณ) นั่นคือเหตุผลที่ฉันใช้ประโยชน์จากรูปแบบสตริงที่กำหนดเองเมื่อพูดถึงการแยก ISO8601 ขณะนี้ตัวอย่างที่ฉันต้องการคือ:

static readonly string[] formats = { 
    // Basic formats
    "yyyyMMddTHHmmsszzz",
    "yyyyMMddTHHmmsszz",
    "yyyyMMddTHHmmssZ",
    // Extended formats
    "yyyy-MM-ddTHH:mm:sszzz",
    "yyyy-MM-ddTHH:mm:sszz",
    "yyyy-MM-ddTHH:mm:ssZ",
    // All of the above with reduced accuracy
    "yyyyMMddTHHmmzzz",
    "yyyyMMddTHHmmzz",
    "yyyyMMddTHHmmZ",
    "yyyy-MM-ddTHH:mmzzz",
    "yyyy-MM-ddTHH:mmzz",
    "yyyy-MM-ddTHH:mmZ",
    // Accuracy reduced to hours
    "yyyyMMddTHHzzz",
    "yyyyMMddTHHzz",
    "yyyyMMddTHHZ",
    "yyyy-MM-ddTHHzzz",
    "yyyy-MM-ddTHHzz",
    "yyyy-MM-ddTHHZ"
    };

public static DateTime ParseISO8601String ( string str )
{
    return DateTime.ParseExact ( str, formats, 
        CultureInfo.InvariantCulture, DateTimeStyles.None );
}

หากคุณไม่ได้แยกวิเคราะห์สตริง TZ-less (ฉันทำ) คุณสามารถเพิ่มบรรทัด "s" เพื่อขยายจำนวนการเปลี่ยนแปลงรูปแบบที่ครอบคลุม


3
ฉันจะเพิ่ม"yyyyMMdd"ในformatsอาร์เรย์เพื่อความแม่นยำลดลงเป็นวันเช่นนี้บางครั้งเมื่อ RFC 5545 RRULE จะพึ่งพา DTSTART เพื่อให้เวลา
Kyle Falconer

1
การใช้Kช่วยให้คุณสามารถหมุนโซนเวลาที่แตกต่างกันของคุณด้วยกัน ฉันเป็นตัวแปรที่กว้างกว่าที่stackoverflow.com/a/31246449/400547แต่ถ้ามีอะไรมากเกินไป (ยอมรับสิ่งที่เป็น ISO 8601 ที่ถูกต้อง แต่ไม่ได้ใช้ในโปรไฟล์ทั่วไป) แต่มันแสดงให้เห็นว่าKสามารถลดขนาดด้วย ที่สาม
จอนฮันนา

20
using System.Globalization;

DateTime d;
DateTime.TryParseExact(
    "2010-08-20T15:00:00",
    "s",
    CultureInfo.InvariantCulture,
    DateTimeStyles.AssumeUniversal, out d);

1
สร้าง False และ d ~~> "1/1/0001 12:00:00 AM" ใน LinqPad :(
Reb.Cabin

@Reb: "2010-08-20T15: 00: 00" และ "s" ถ้าไม่มี "Z" ในตอนท้าย
abatishchev

แก้ไข :) Z แสดงขึ้นในทุกตัวอย่างของฉัน (ซึ่งเกิดขึ้นจะมาจากหน่วยงานต่างๆ GPS และไฟล์ GPX)
Reb.Cabin

พบในการอ้างอิง ISO 8601 อื่นที่ "Z" หมายถึงโซน - เช่นเดียวกับในโซนเวลา
Reb.Cabin

30
Z หมายถึงเวลาของ Zulu หรือ UTC en.wikipedia.org/wiki/ISO_8601#UTC
Peter Stephens

19

นี่คืออันที่ทำงานได้ดีกว่าสำหรับฉัน ( รุ่นLINQPad ):

DateTime d;
DateTime.TryParseExact(
    "2010-08-20T15:00:00Z",
    @"yyyy-MM-dd\THH:mm:ss\Z",
    CultureInfo.InvariantCulture,
    DateTimeStyles.AssumeUniversal, 
    out d);
d.ToString()

ผลิต

true
8/20/2010 8:00:00 AM

ขณะนี้ใช้สิ่งนี้เพื่อตรวจสอบในหน่วยของฉันทดสอบว่าสตริงทั้งหมดที่ฉันคาดว่าจะเป็นวันที่อยู่ในรูปแบบ Iso8601 ขอบคุณ!
anthv123

1
ทำไมสิ่งนี้ถึงส่งคืนการบันทึกเวลาไม่ได้อยู่ใน UTC! มีการละเมิดหลักการความน่าเชื่อถือน้อยมากเนื่องจาก "ค่าคงที่ของวัฒนธรรม" กับ "AssumeUniversal" ไม่ควรทำเช่นนี้เพราะ DST แตกต่างกันอย่างดุเดือดทั่วโลกดังนั้นการกลับมาในเขตเวลาที่มีอยู่ในท้องถิ่นของคุณอาจทำให้เกิดข้อผิดพลาด บนเซิร์ฟเวอร์ที่มีการตั้งค่าที่แตกต่างกัน!
Elaskanator

7

ดูเหมือนว่าสำคัญที่จะต้องตรงกับรูปแบบของสตริง ISO สำหรับTryParseExactการทำงาน ฉันเดา Exact is Exact และคำตอบนี้ชัดเจนที่สุด แต่ต่อไป ...

ในกรณีของฉันคำตอบของ Reb.Cabin ไม่ทำงานเนื่องจากฉันมีอินพุตที่แตกต่างกันเล็กน้อยตาม "ค่า" ของฉันด้านล่าง

ราคา: 2012-08-10T14:00:00.000Z

มีบางส่วนของ 000 พิเศษในนั้นเป็นมิลลิวินาทีและอาจมีมากขึ้น

อย่างไรก็ตามถ้าฉันเพิ่มบางอย่าง.fffในรูปแบบที่แสดงด้านล่างทั้งหมดเป็นเรื่องปกติ

รูปแบบสตริง: @"yyyy-MM-dd\THH:mm:ss.fff\Z"

ใน VS2010 หน้าต่างทันที:

DateTime.TryParseExact(value,@"yyyy-MM-dd\THH:mm:ss.fff\Z", CultureInfo.InvariantCulture,DateTimeStyles.AssumeUniversal, out d);

จริง

คุณอาจต้องใช้DateTimeStyles.AssumeLocalเช่นกันขึ้นอยู่กับโซนเวลาของคุณสำหรับ ...


1
นี้ทำงานสำหรับฉัน แต่ฉันยังมีการเปลี่ยนแปลงไปAssumeUniversal AdjustToUniversal
Augusto Barreto

4

ทำงานได้ดีใน LINQPad4:

Console.WriteLine(DateTime.Parse("2010-08-20T15:00:00Z"));
Console.WriteLine(DateTime.Parse("2010-08-20T15:00:00"));
Console.WriteLine(DateTime.Parse("2010-08-20 15:00:00"));

-1

DateTime.ParseExact(...) ช่วยให้คุณบอกตัวแยกวิเคราะห์ว่าอักขระแต่ละตัวแสดงถึงอะไร

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