[date] 날짜 시간 소인을 작성하고 ISO 8601, RFC 3339, UTC 시간대로 형식을 지정하는 방법은 무엇입니까?

ISO 8601RFC 3339 의 형식 표준을 사용하여 날짜 시간 소인을 생성하는 방법은 무엇입니까?

목표는 다음과 같은 문자열입니다.

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

체재:

  • 년, 월, 일, “XXXX-XX-XX”
  • 구분 기호로 문자 “T”
  • “XX : XX : XX.XXX”와 같은 시간, 분, 초, 밀리 초
  • 영점 오프셋, 일명 UTC, GMT, 줄 루어 시간의 영역 지정자 인 문자 “Z”.

가장 좋은 경우 :

  • 간단하고 짧고 간단한 스위프트 소스 코드.
  • 추가 프레임 워크, 하위 프로젝트, cocoapod, C 코드 등을 사용할 필요가 없습니다.

StackOverflow, Google, Apple 등을 검색했지만 이에 대한 Swift 답변을 찾지 못했습니다.

가장 유망한 보인다 클래스는 NSDate, NSDateFormatter, NSTimeZone.

관련 Q & A : iOS에서 ISO 8601 날짜는 어떻게 얻습니까?

지금까지 내가 찾은 최고는 다음과 같습니다.

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))



답변

스위프트 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 • 스위프트 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
    }()
}

코딩 가능한 프로토콜

코딩 가능한 프로토콜로 작업 할 때이 형식을 인코딩 및 디코딩해야하는 경우 고유 한 사용자 지정 날짜 인코딩 / 디코딩 전략을 만들 수 있습니다.

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"]

여기에 이미지 설명을 입력하십시오


답변

기술 Q & A1480에en_US_POSIX 설명 된대로 로케일을 설정해야합니다 . 스위프트 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))

문제는 그레고리력이 아닌 달력을 사용하는 장치를 사용하는 locale경우 timeZonedateFormat문자열 을 지정하지 않으면 연도가 RFC3339 / ISO8601을 준수하지 않는다는 것 입니다.

또는 당신 ISO8601DateFormatter은 설정의 잡초 localetimeZone자신 에서 당신을 얻을 수 있습니다 :

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))

Swift 2 변환에 대해서는 이 답변의 이전 개정판을 참조하십시오 .


답변

ISO8601DateFormatter()Rails 4+ JSON 피드의 날짜와 함께 날짜 를 사용하려면 (물론 밀리 초가 필요하지 않음) 포맷터에서 몇 가지 옵션을 설정해야 작동합니다. 그렇지 않으면 date(from: string)함수가 nil을 반환합니다. 내가 사용하는 것은 다음과 같습니다.

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
    }()
}

놀이터 스크린 샷이 아닌 옵션 구절을 사용한 결과는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오


답변

스위프트 5

iOS 11.0+ / macOS 10.13+를 대상 ISO8601DateFormatter으로하는 경우withInternetDateTimewithFractionalSeconds같이 옵션 .

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"


답변

용도 ISO8601DateFormatteriOS10 이상에서 .

용도 DateFormatteriOS9 이상에서 .

스위프트 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
        }
    }()
}


답변

Andrés Torres Marroquín과 Leo Dabus를 더 칭찬하기 위해 분수 초를 유지하는 버전이 있습니다. 어디서나 문서화 할 수는 없지만 Apple은 입력 및 출력 모두에서 소수 초를 마이크로 초 (정밀도 3 자릿수)로 자릅니다 ( 유니 코드 tr35-31 과 달리 SSSSSSS를 사용하여 지정하더라도 ).

나는 이것이 대부분의 유스 케이스에 필요하지 않을 것이라고 강조해야한다 . 온라인 날짜는 일반적으로 밀리 초 정밀도가 필요하지 않으며, 필요할 때 다른 데이터 형식을 사용하는 것이 좋습니다. 그러나 때로는 기존 방식과 특정 방식으로 상호 운용해야합니다.

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
    }
}


답변

필자의 경우 DynamoDB-lastUpdated 열 (Unix Timestamp)을 표준시로 변환해야합니다.

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)

            }