วิธีสร้างการประทับเวลาและรูปแบบวันที่ตาม ISO 8601, RFC 3339, โซนเวลา UTC


186

วิธีสร้างการประทับวันที่โดยใช้รูปแบบมาตรฐานสำหรับISO 8601และRFC 3339อย่างไร

เป้าหมายคือสตริงที่มีลักษณะดังนี้:

"2015-01-01T00:00:00.000Z"

รูปแบบ:

  • ปี, เดือน, วันเป็น "XXXX-XX-XX"
  • ตัวอักษร "T" เป็นตัวคั่น
  • ชั่วโมง, นาที, วินาที, มิลลิวินาทีเป็น "XX: XX: XX.XXX"
  • ตัวอักษร "Z" เป็นตัวกำหนดโซนสำหรับการชดเชยศูนย์หรือเวลา UTC, GMT, Zulu

กรณีที่ดีที่สุด:

  • ซอร์สโค้ด Swift ที่ง่ายสั้นและตรงไปตรงมา
  • ไม่จำเป็นต้องใช้กรอบงานเพิ่มเติมโครงการย่อย cocoapod รหัส C ฯลฯ

ฉันค้นหา StackOverflow, Google, Apple และไม่พบคำตอบ Swift สำหรับสิ่งนี้

เรียนที่ดูเหมือนมีแนวโน้มมากที่สุดที่มีNSDate, NSDateFormatter,NSTimeZone ,

คำถาม & คำตอบที่เกี่ยวข้อง: ฉันจะรับวันที่ ISO 8601 ใน iOS ได้อย่างไร

นี่คือสิ่งที่ดีที่สุดที่ฉันเคยได้รับ:

var now = NSDate()
var formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
println(formatter.stringFromDate(now))

6
โปรดทราบว่าiOS10 + SIMPLY รวมถึง ISO 8601 BUILT-IN .. มันจะเติมข้อความอัตโนมัติให้คุณ
Fattie

2
@Fattie และ - มันสามารถจัดการกับ. 234Z มิลลิวินาทีล่าสุดได้อย่างไร Zulu / UTC ส่วนหนึ่งของการประทับเวลา? คำตอบ: Matt Longs @ stackoverflow.com/a/42101630/3078330
smat88dd

1
@ smat88dd - เคล็ดลับที่ยอดเยี่ยมขอบคุณ ฉันไม่รู้ว่ามี "ตัวเลือกในการจัดรูปแบบ" แปลกและดุร้าย!
Fattie

ฉันกำลังมองหาวิธีแก้ปัญหาที่ทำงานบน linux
neoneye

@neoneye เพียงแค่ใช้เวอร์ชั่นเก่า (DateFormatter ธรรมดา) และเปลี่ยน iso8601 ปฏิทินเป็น gregorian stackoverflow.com/a/28016692/2303865
Leo Dabus

คำตอบ:


392

Swift 4 • iOS 11.2.1 หรือใหม่กว่า

extension ISO8601DateFormatter {
    convenience init(_ formatOptions: Options, timeZone: TimeZone = TimeZone(secondsFromGMT: 0)!) {
        self.init()
        self.formatOptions = formatOptions
        self.timeZone = timeZone
    }
}

extension Formatter {
    static let iso8601withFractionalSeconds = ISO8601DateFormatter([.withInternetDateTime, .withFractionalSeconds])
}

extension Date {
    var iso8601withFractionalSeconds: String { return Formatter.iso8601withFractionalSeconds.string(from: self) }
}

extension String {
    var iso8601withFractionalSeconds: Date? { return Formatter.iso8601withFractionalSeconds.date(from: self) }
}

การใช้งาน:

Date().description(with: .current)  //  Tuesday, February 5, 2019 at 10:35:01 PM Brasilia Summer Time"
let dateString = Date().iso8601withFractionalSeconds   //  "2019-02-06T00:35:01.746Z"

if let date = dateString.iso8601withFractionalSeconds {
    date.description(with: .current) // "Tuesday, February 5, 2019 at 10:35:01 PM Brasilia Summer Time"
    print(date.iso8601withFractionalSeconds)           //  "2019-02-06T00:35:01.746Z\n"
}

iOS 9 • Swift 3 หรือใหม่กว่า

extension Formatter {
    static let iso8601withFractionalSeconds: DateFormatter = {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
        return formatter
    }()
}

โปรโตคอลรหัส

หากคุณต้องการเข้ารหัสและถอดรหัสรูปแบบนี้เมื่อทำงานกับโปรโตคอล Codable คุณสามารถสร้างกลยุทธ์การเข้ารหัส / ถอดรหัสวันที่ที่กำหนดเอง:

extension JSONDecoder.DateDecodingStrategy {
    static let iso8601withFractionalSeconds = custom {
        let container = try $0.singleValueContainer()
        let string = try container.decode(String.self)
        guard let date = Formatter.iso8601withFractionalSeconds.date(from: string) else {
            throw DecodingError.dataCorruptedError(in: container,
                  debugDescription: "Invalid date: " + string)
        }
        return date
    }
}

และกลยุทธ์การเข้ารหัส

extension JSONEncoder.DateEncodingStrategy {
    static let iso8601withFractionalSeconds = custom {
        var container = $1.singleValueContainer()
        try container.encode(Formatter.iso8601withFractionalSeconds.string(from: $0))
    }
}

การทดสอบสนามเด็กเล่น

let dates = [Date()]   // ["Feb 8, 2019 at 9:48 PM"]

การเข้ารหัส

let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601withFractionalSeconds
let data = try! encoder.encode(dates)
print(String(data: data, encoding: .utf8)!)

ถอดรหัส

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601withFractionalSeconds
let decodedDates = try! decoder.decode([Date].self, from: data)  // ["Feb 8, 2019 at 9:48 PM"]

ป้อนคำอธิบายรูปภาพที่นี่


3
มันจะมีประโยชน์ในการเพิ่มส่วนขยายการแปลงที่ตรงกันข้าม:extension String { var dateFormattedISO8601: NSDate? {return NSDate.Date.formatterISO8601.dateFromString(self)} }
Vive

1
เพียงแค่ทราบว่าสิ่งนี้จะเสียความแม่นยำไปเล็กน้อยดังนั้นจึงเป็นเรื่องสำคัญที่จะต้องตรวจสอบให้แน่ใจว่ามีการเปรียบเทียบความเท่าเทียมกันของวันที่ผ่านสตริงที่สร้างขึ้นไม่ใช่เวลาช่วงเวลา let now = NSDate() let stringFromDate = now.iso8601 let dateFromString = stringFromDate.dateFromISO8601! XCTAssertEqual(now.timeIntervalSince1970, dateFromString.timeIntervalSince1970)
pixelrevision

7
ไม่ต้องพูดถึงถ้าคุณไม่ต้องการมิลลิวินาที iOS 10 ใหม่จะISO8601DateFormatterทำให้กระบวนการง่ายขึ้น ฉันได้ออกรายงานข้อผิดพลาด (27242248) ไปยัง Apple ขอให้พวกเขาขยายฟอร์แมตเตอร์ใหม่นี้เพื่อเสนอความสามารถในการระบุมิลลิวินาทีด้วย (เนื่องจากฟอร์แมตใหม่นี้ไม่ได้ใช้กับเราหลายคนโดยไม่มีมิลลิวินาที)
Rob

1
ใน RFC3339เราสามารถพบบันทึกย่อ "NOTE: ISO 8601 กำหนดวันที่และเวลาโดยคั่นด้วย" T "แอปพลิเคชันที่ใช้ไวยากรณ์นี้อาจเลือกเพื่อให้สามารถอ่านได้เพื่อระบุวันที่และเต็มเวลาคั่นด้วย (พูด) อักขระช่องว่าง " มันครอบคลุมรูปแบบวันเดียวโดยไม่ต้องTเช่น: 2016-09-21 21:05:10+00:00?
manRo

3
@LeoDabus ใช่ แต่นี่เป็นผลลัพธ์แรกของ "Swift iso8601" ความคิดเห็นของฉันมีไว้เพื่อเตือนผู้พัฒนารายอื่นที่เจอสิ่งนี้ในอนาคตและไม่ได้กำกับที่ OP
thislooksfun

38

อย่าลืมตั้งสถานที่เกิดเหตุไปen_US_POSIXตามที่อธิบายในทางเทคนิค Q ในสวิฟต์ 3:

let date = Date()
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
print(formatter.string(from: date))

ปัญหาคือถ้าคุณอยู่ในอุปกรณ์ที่ใช้ปฏิทินที่ไม่ใช่แบบคริสต์ศักราชปีจะไม่สอดคล้องกับ RFC3339 / ISO8601 เว้นแต่คุณจะระบุlocaleเช่นเดียวกับtimeZoneและdateFormatสตริง

หรือคุณสามารถใช้ISO8601DateFormatterเพื่อนำคุณออกจากวัชพืชของการตั้งค่าlocaleและtimeZoneตัวคุณเอง:

let date = Date()
let formatter = ISO8601DateFormatter()
formatter.formatOptions.insert(.withFractionalSeconds)  // this is only available effective iOS 11 and macOS 10.13
print(formatter.string(from: date))

สำหรับสวิฟท์ 2 การกระทำที่ดูการแก้ไขก่อนหน้าของคำตอบนี้


เหตุใดเราควรตั้งค่าภาษาเป็น en_US_POSIX แม้ว่าเราไม่ได้อยู่ในสหรัฐฯ
mnemonic23

2
ทางที่ดีคุณต้องบางสถานที่สอดคล้องกันและการประชุมของ / RFC 3999 มาตรฐาน ISO 8601 en_US_POSIXเป็นรูปแบบที่นำเสนอโดย มันเป็นภาษากลางสำหรับการแลกเปลี่ยนวันที่บนเว็บ และคุณไม่สามารถตีความวันที่ผิดพลาดได้หากมีการใช้ปฏิทินหนึ่งรายการในอุปกรณ์เมื่อบันทึกสตริงวันที่และอีกรายการหนึ่งเมื่ออ่านสตริงในภายหลัง นอกจากนี้คุณต้องมีรูปแบบที่รับประกันว่าจะไม่เปลี่ยนแปลง (ซึ่งเป็นสาเหตุที่คุณใช้en_US_POSIXและไม่en_US) ดูQ&A 1480 ทางเทคนิคหรือมาตรฐาน RFC / ISO เหล่านั้นสำหรับข้อมูลเพิ่มเติม
Rob

24

หากคุณต้องการใช้ISO8601DateFormatter()กับวันที่จากฟีด Rails 4+ JSON (และไม่จำเป็นต้องมีหน่วยเป็นมิลลิวินาที) คุณต้องตั้งค่าตัวเลือกสองสามอย่างบนตัวจัดรูปแบบเพื่อให้การทำงานนั้นถูกต้องมิฉะนั้นdate(from: string)ฟังก์ชันจะส่งคืนศูนย์ นี่คือสิ่งที่ฉันใช้:

extension Date {
    init(dateString:String) {
        self = Date.iso8601Formatter.date(from: dateString)!
    }

    static let iso8601Formatter: ISO8601DateFormatter = {
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withFullDate,
                                          .withTime,
                                          .withDashSeparatorInDate,
                                          .withColonSeparatorInTime]
        return formatter
    }()
}

นี่คือผลลัพธ์ของการใช้ตัวเลือกข้อที่ไม่ได้อยู่ในสกรีนช็อตสนามเด็กเล่น:

ป้อนคำอธิบายรูปภาพที่นี่


คุณจะต้องรวมอยู่ในตัวเลือกยังแต่ฉันพยายามแล้วนั้นและมันช่วยให้การขว้างปาข้อผิดพลาด.withFractionalSeconds libc++abi.dylib: terminating with uncaught exception of type NSException
Leo Dabus

@MEnnabah มันทำงานได้ดีสำหรับฉันใน Swift 4. คุณได้รับข้อผิดพลาดหรือไม่?
Matt Long

@LeoDabus มีข้อผิดพลาดเดียวกับคุณคุณแก้ไขมันได้หรือไม่?
อิสระ

กำหนดเอง JSONDecoder DateDecodingStrategy stackoverflow.com/a/46458771/2303865
Leo Dabus

@freeman หากคุณต้องการรักษาวันที่ด้วยจำนวนเสี้ยววินาทีทั้งหมดฉันขอแนะนำให้ใช้สองครั้ง (ช่วงเวลานับตั้งแต่วันที่อ้างอิง) เมื่อบันทึก / รับวันที่ของคุณไปยังเซิร์ฟเวอร์ และใช้กลยุทธ์การถอดรหัสวันที่เริ่มต้น.deferredToDateเมื่อใช้โปรโตคอลที่เข้ารหัสได้
Leo Dabus

6

สวิฟท์ 5

หากคุณกำหนดเป้าหมายเป็น iOS 11.0+ / macOS 10.13+ คุณเพียงใช้ISO8601DateFormatterกับตัวเลือกwithInternetDateTimeและwithFractionalSecondsตัวเลือกเช่น:

let date = Date()

let iso8601DateFormatter = ISO8601DateFormatter()
iso8601DateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let string = iso8601DateFormatter.string(from: date)

// string looks like "2020-03-04T21:39:02.112Z"

5

การใช้ประโยชน์ ISO8601DateFormatterบน iOS10 หรือใหม่กว่า

การใช้ประโยชน์ DateFormatterบน iOS9 หรือเก่ากว่า

สวิฟท์ 4

protocol DateFormatterProtocol {
    func string(from date: Date) -> String
    func date(from string: String) -> Date?
}

extension DateFormatter: DateFormatterProtocol {}

@available(iOS 10.0, *)
extension ISO8601DateFormatter: DateFormatterProtocol {}

struct DateFormatterShared {
    static let iso8601: DateFormatterProtocol = {
        if #available(iOS 10, *) {
            return ISO8601DateFormatter()
        } else {
            // iOS 9
            let formatter = DateFormatter()
            formatter.calendar = Calendar(identifier: .iso8601)
            formatter.locale = Locale(identifier: "en_US_POSIX")
            formatter.timeZone = TimeZone(secondsFromGMT: 0)
            formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
            return formatter
        }
    }()
}

5

เพื่อชมเชยเพิ่มเติมAndrés Torres Marroquínและ Leo Dabus ฉันมีรุ่นที่เก็บรักษาเศษเสี้ยววินาที ฉันไม่พบเอกสารใด ๆ เลย แต่ Apple ตัดทอนเศษส่วนวินาทีเป็นไมโครวินาที (ความแม่นยำ 3 หลัก) ทั้งอินพุตและเอาต์พุต (แม้ว่าจะระบุโดยใช้ SSSSSSS ตรงกันข้ามกับUnicode tr35-31 )

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

Xcode 8/9 และ Swift 3.0-3.2

extension Date {
    struct Formatter {
        static let iso8601: DateFormatter = {
            let formatter = DateFormatter()
            formatter.calendar = Calendar(identifier: .iso8601)
            formatter.locale = Locale(identifier: "en_US_POSIX")
            formatter.timeZone = TimeZone(identifier: "UTC")
            formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXXXX"
            return formatter
        }()
    }

    var iso8601: String {
        // create base Date format 
         var formatted = DateFormatter.iso8601.string(from: self)

        // Apple returns millisecond precision. find the range of the decimal portion
         if let fractionStart = formatted.range(of: "."),
             let fractionEnd = formatted.index(fractionStart.lowerBound, offsetBy: 7, limitedBy: formatted.endIndex) {
             let fractionRange = fractionStart.lowerBound..<fractionEnd
            // replace the decimal range with our own 6 digit fraction output
             let microseconds = self.timeIntervalSince1970 - floor(self.timeIntervalSince1970)
             var microsecondsStr = String(format: "%.06f", microseconds)
             microsecondsStr.remove(at: microsecondsStr.startIndex)
             formatted.replaceSubrange(fractionRange, with: microsecondsStr)
        }
         return formatted
    }
}

extension String {
    var dateFromISO8601: Date? {
        guard let parsedDate = Date.Formatter.iso8601.date(from: self) else {
            return nil
        }

        var preliminaryDate = Date(timeIntervalSinceReferenceDate: floor(parsedDate.timeIntervalSinceReferenceDate))

        if let fractionStart = self.range(of: "."),
            let fractionEnd = self.index(fractionStart.lowerBound, offsetBy: 7, limitedBy: self.endIndex) {
            let fractionRange = fractionStart.lowerBound..<fractionEnd
            let fractionStr = self.substring(with: fractionRange)

            if var fraction = Double(fractionStr) {
                fraction = Double(floor(1000000*fraction)/1000000)
                preliminaryDate.addTimeInterval(fraction)
            }
        }
        return preliminaryDate
    }
}

นี่เป็นคำตอบที่ดีที่สุดในความเห็นของฉันที่ช่วยให้ผู้ใช้ไปสู่ระดับความแม่นยำระดับไมโครวินาทีซึ่งโซลูชันอื่น ๆ ทั้งหมดตัดทอนเป็นมิลลิวินาที
Michael A. McCloskey

หากคุณต้องการเก็บรักษาวันที่ด้วยจำนวนเสี้ยววินาทีทั้งหมดคุณควรใช้เพียงสองครั้ง (ช่วงเวลานับตั้งแต่วันที่อ้างอิง) เมื่อบันทึก / รับวันที่ของคุณไปยังเซิร์ฟเวอร์
Leo Dabus

@LeoDabus ใช่ถ้าคุณควบคุมทั้งระบบและไม่จำเป็นต้องทำงานร่วมกัน อย่างที่ฉันพูดในคำตอบสิ่งนี้ไม่จำเป็นสำหรับผู้ใช้ส่วนใหญ่ แต่เราไม่สามารถควบคุมการจัดรูปแบบข้อมูลในเว็บ API ได้เสมอและในขณะที่ Android และ Python (อย่างน้อย) รักษาความแม่นยำเศษ 6 หลักบางครั้งจำเป็นต้องทำตามความเหมาะสม
Eli Burke

3

ในกรณีของฉันฉันต้องแปลง DynamoDB - คอลัมน์ LastUpdated (Unix Timestamp) เป็น Normal Time

ค่าเริ่มต้นของ lastUpdated คือ: 1460650607601 - แปลงเป็น 2016-04-14 16:16:47 +0000 ผ่าน:

   if let lastUpdated : String = userObject.lastUpdated {

                let epocTime = NSTimeInterval(lastUpdated)! / 1000 // convert it from milliseconds dividing it by 1000

                let unixTimestamp = NSDate(timeIntervalSince1970: epocTime) //convert unix timestamp to Date
                let dateFormatter = NSDateFormatter()
                dateFormatter.timeZone = NSTimeZone()
                dateFormatter.locale = NSLocale.currentLocale() // NSLocale(localeIdentifier: "en_US_POSIX")
                dateFormatter.dateFormat =  "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
                dateFormatter.dateFromString(String(unixTimestamp))

                let updatedTimeStamp = unixTimestamp
                print(updatedTimeStamp)

            }

3

ในอนาคตรูปแบบอาจต้องเปลี่ยนซึ่งอาจเป็นอาการปวดหัวเล็ก ๆ ที่มี date.dateFromISO8601 โทรทุกที่ในแอป ใช้คลาสและโพรโทคอลเพื่อตัดการใช้งานการเปลี่ยนรูปแบบเวลาวันที่เรียกในที่เดียวจะง่ายขึ้น ใช้ RFC3339 ถ้าเป็นไปได้การแทนที่สมบูรณ์กว่านี้ DateFormatProtocol และ DateFormat นั้นยอดเยี่ยมสำหรับการฉีดแบบพึ่งพา

class AppDelegate: UIResponder, UIApplicationDelegate {

    internal static let rfc3339DateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
    internal static let localeEnUsPosix = "en_US_POSIX"
}

import Foundation

protocol DateFormatProtocol {

    func format(date: NSDate) -> String
    func parse(date: String) -> NSDate?

}


import Foundation

class DateFormat:  DateFormatProtocol {

    func format(date: NSDate) -> String {
        return date.rfc3339
    }

    func parse(date: String) -> NSDate? {
        return date.rfc3339
    }

}


extension NSDate {

    struct Formatter {
        static let rfc3339: NSDateFormatter = {
            let formatter = NSDateFormatter()
            formatter.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierISO8601)
            formatter.locale = NSLocale(localeIdentifier: AppDelegate.localeEnUsPosix)
            formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
            formatter.dateFormat = rfc3339DateFormat
            return formatter
        }()
    }

    var rfc3339: String { return Formatter.rfc3339.stringFromDate(self) }
}

extension String {
    var rfc3339: NSDate? {
        return NSDate.Formatter.rfc3339.dateFromString(self)
    }
}



class DependencyService: DependencyServiceProtocol {

    private var dateFormat: DateFormatProtocol?

    func setDateFormat(dateFormat: DateFormatProtocol) {
        self.dateFormat = dateFormat
    }

    func getDateFormat() -> DateFormatProtocol {
        if let dateFormatObject = dateFormat {

            return dateFormatObject
        } else {
            let dateFormatObject = DateFormat()
            dateFormat = dateFormatObject

            return dateFormatObject
        }
    }

}

3

มีISO8601DateFormatterคลาสใหม่ที่ให้คุณสร้างสตริงที่มีเพียงหนึ่งบรรทัด เพื่อความเข้ากันได้ย้อนหลังฉันใช้ห้องสมุด C เก่า ฉันหวังว่านี่จะเป็นประโยชน์สำหรับใครบางคน

Swift 3.0

extension Date {
    var iso8601: String {
        if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
            return ISO8601DateFormatter.string(from: self, timeZone: TimeZone.current, formatOptions: .withInternetDateTime)
        } else {
            var buffer = [CChar](repeating: 0, count: 25)
            var time = time_t(self.timeIntervalSince1970)
            strftime_l(&buffer, buffer.count, "%FT%T%z", localtime(&time), nil)
            return String(cString: buffer)
        }
    }
}

1

เพื่อเสริมเวอร์ชันของ Leo Dabus ฉันได้เพิ่มการสนับสนุนสำหรับโปรเจ็กต์ Swift และ Objective-C รวมทั้งเพิ่มการสนับสนุนสำหรับมิลลิวินาทีที่เป็นทางเลือกซึ่งอาจไม่ใช่วิธีที่ดีที่สุด แต่คุณจะได้รับประเด็น:

Xcode 8 และ Swift 3

extension Date {
    struct Formatter {
        static let iso8601: DateFormatter = {
            let formatter = DateFormatter()
            formatter.calendar = Calendar(identifier: .iso8601)
            formatter.locale = Locale(identifier: "en_US_POSIX")
            formatter.timeZone = TimeZone(secondsFromGMT: 0)
            formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
            return formatter
        }()
    }

    var iso8601: String {
        return Formatter.iso8601.string(from: self)
    }
}


extension String {
    var dateFromISO8601: Date? {
        var data = self
        if self.range(of: ".") == nil {
            // Case where the string doesn't contain the optional milliseconds
            data = data.replacingOccurrences(of: "Z", with: ".000000Z")
        }
        return Date.Formatter.iso8601.date(from: data)
    }
}


extension NSString {
    var dateFromISO8601: Date? {
        return (self as String).dateFromISO8601
    }
}

0

โดยไม่ต้องมีมาสก์สตริงแบบกำหนดเองหรือ TimeFormatters

import Foundation

struct DateISO: Codable {
    var date: Date
}

extension Date{
    var isoString: String {
        let encoder = JSONEncoder()
        encoder.dateEncodingStrategy = .iso8601
        guard let data = try? encoder.encode(DateISO(date: self)),
        let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as?  [String: String]
            else { return "" }
        return json?.first?.value ?? ""
    }
}

let dateString = Date().isoString

นี่เป็นคำตอบที่ดี แต่การใช้.iso8601จะไม่รวมมิลลิวินาที
Stefan Arentz

0

ขึ้นอยู่กับคำตอบที่ยอมรับได้ในกระบวนทัศน์วัตถุ

class ISO8601Format
{
    let format: ISO8601DateFormatter

    init() {
        let format = ISO8601DateFormatter()
        format.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        format.timeZone = TimeZone(secondsFromGMT: 0)!
        self.format = format
    }

    func date(from string: String) -> Date {
        guard let date = format.date(from: string) else { fatalError() }
        return date
    }

    func string(from date: Date) -> String { return format.string(from: date) }
}


class ISO8601Time
{
    let date: Date
    let format = ISO8601Format() //FIXME: Duplication

    required init(date: Date) { self.date = date }

    convenience init(string: String) {
        let format = ISO8601Format() //FIXME: Duplication
        let date = format.date(from: string)
        self.init(date: date)
    }

    func concise() -> String { return format.string(from: date) }

    func description() -> String { return date.description(with: .current) }
}

callsite

let now = Date()
let time1 = ISO8601Time(date: now)
print("time1.concise(): \(time1.concise())")
print("time1: \(time1.description())")


let time2 = ISO8601Time(string: "2020-03-24T23:16:17.661Z")
print("time2.concise(): \(time2.concise())")
print("time2: \(time2.description())")
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.