226

ISO 8601およびRFC 3339のフォーマット標準を使用して、日時スタンプを生成する方法は?

目標は、次のような文字列です。

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

フォーマット:

  • 年、月、日、「XXXX-XX-XX」
  • セパレータとしての文字「T」
  • 時、分、秒、ミリ秒を「XX:XX:XX.XXX」として表します。
  • ゼロ オフセット、別名 UTC、GMT、ズールー時間のゾーン指定子としての文字「Z」。

最良の場合:

  • シンプル、短く、簡単な Swift ソース コード。
  • 追加のフレームワーク、サブプロジェクト、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

12 に答える 12

50

Technical Q&A1480en_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問題は、グレゴリオ暦以外のカレンダーを使用しているデバイスを使用している場合、timeZoneと および文字列を指定しない限り、年が RFC3339/ISO8601 に準拠しないことですdateFormat

または、設定と自分自身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 レンディションについては、この回答の以前のリビジョンを参照してください。

于 2015-01-19T01:04:16.143 に答える
37

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

プレイグラウンドのスクリーンショットで、オプションを使用した場合と使用しなかった場合の結果を次に示します。

ここに画像の説明を入力

于 2017-02-07T23:00:34.433 に答える
5

iOS10ISO8601DateFormatter以降でご利用ください。

iOS9以前でご利用DateFormatterいただけます。

スイフト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
        }
    }()
}
于 2018-03-08T13:14:22.403 に答える
3

将来、形式を変更する必要が生じる可能性があります。これは、アプリ内のあらゆる場所で date.dateFromISO8601 呼び出しが行われる小さな頭痛の種になる可能性があります。クラスとプロトコルを使用して実装をラップすると、日時形式の呼び出しを 1 か所で変更する方が簡単になります。可能であれば、より完全な表現である 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
        }
    }

}
于 2016-12-05T03:13:18.153 に答える
3

私の場合、DynamoDB - lastUpdated 列 (Unix タイムスタンプ) を通常時間に変換する必要があります。

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)

            }
于 2016-04-15T11:09:44.310 に答える
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) }
}

コールサイト

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())")
于 2020-03-24T23:48:52.423 に答える