iOSでISO 8601の日付を取得するにはどうすればよいですか?


115

2004-02-12T15:19:21+00:00介してPHPでISO 8601日付文字列(たとえば)を取得するのは簡単date('c')ですが、Objective-C(iPhone)ではどのように取得しますか?それを行うための同様の短い方法はありますか?

ここに私がそれをするために見つけた長い道のりがあります:

NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

NSDate *now = [NSDate date];
NSString *formattedDateString = [dateFormatter stringFromDate:now];
NSLog(@"ISO-8601 date: %@", formattedDateString);

// Output: ISO-8601 date: 2013-04-27T13:27:50-0700

それはとても中心的な何かのための恐ろしい多くのリガモラルのようです。


1
短いのは見る人の目にあると思います。3行のコードはかなり短いですよね?追加できるC NSDateサブクラスがあります。これを1行で実行します。私はそれをトリップしました:github.com/soffes/SAMCategories/tree/master/SAMCategories/…。とは言っても、実際にはかなり良い答え(IMHO)が得られたので、エティエンヌかrmaddyのどちらかを答えるべきです。それはちょうど良い習慣であり、本当に役立つ情報を提供してくれた人々に報いる(それは私を助けてくれた。今日この情報が必要だった)。エティエンヌはrmaddyよりも多くのポイントを使用できます。良い基準の印として、私もあなたの質問に賛成票を投じます!
David H

回答:


212

使用NSDateFormatter

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];   
NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
[dateFormatter setLocale:enUSPOSIXLocale];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
[dateFormatter setCalendar:[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]];

NSDate *now = [NSDate date];
NSString *iso8601String = [dateFormatter stringFromDate:now];

そしてSwiftでは:

let dateFormatter = DateFormatter()
let enUSPosixLocale = Locale(identifier: "en_US_POSIX")
dateFormatter.locale = enUSPosixLocale
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
dateFormatter.calendar = Calendar(identifier: .gregorian)

let iso8601String = dateFormatter.string(from: Date())

1
ありがとう、マディ。私がコードを投稿した後、私は今あなたの答えを見ました。でsetLocale重要なのは?
JohnK

2
@rmaddyコピーペーストする人が焼かれないように、ZZZZZを使用するように例を編集するだけかもしれません。
Jesse Rusak 14年

4
@jlmendezboniniいいえ。YYYYを使用することは非常にまれです。通常、yyyyが必要です。
rmaddy 2014

7
ロケールをen_US_POSIXに設定する必要がある理由のソース:developer.apple.com/library/mac/qa/qa1480/_index.html
yood

11
@maddy -グーグルを介してこれを見つけ、他人のために発見することは容易にそれを作りたかったし、実際に正しい形式を掘り起こすためにあなたの信用を与えたかった、ロケールなどしかし、あなたがしたい場合は、別の答えとして、それを投稿して幸せ
ロビン・

60

iOS 10では、NSISO8601DateFormatterこれを処理する新しいクラスが導入されています。Swift 3を使用している場合、コードは次のようになります。

let formatter = ISO8601DateFormatter()
let date = formatter.date(from: "2016-08-26T12:39:00Z")
let string = formatter.string(from: Date())

そしてそれはどんなフォーマットを与えるのですか?また、2016-08-26T12:39:00-0000と2016-08-26T12:39:00-00:00と2016-08-26T12:39:00.374-0000を処理できるかどうかを知っておくとよいでしょう。
マルハル2016

1
私の回答の3行が実行された後、string「2016-09-03T21:47:27Z」が含まれています。
TwoStraws 2016

3
この形式ではミリ秒を処理できません
エンリケ

3
@Enrique:NSISO8601DateFormatWithFractionalSecondsをフォーマッタオプションに追加して、ミリ秒で動作できるようにする必要があります。ドキュメントを確認してください。
PakitoV 2018年

1
NSISO8601DateFormatWithFractionalSecondsは11.2以降でのみ機能します。そうでなければ、クラッシュしました!
hash3r

27

マディの答えを補足するものとして、タイムゾーン形式は、単一の「Z」(RFC 822形式用)ではなく、ISO 8601の「ZZZZZ」(Zの5倍)にする必要があります。少なくともiOS 6では。

http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patternsを参照


グレートキャッチ!これに興味のある人は、リンクにアクセスして8601を検索すると、表示されます。
David H

日付のタイムゾーンがUTCの場合、Z(同等)ではなく00:00を表示する方法はありますか?
シム2015年

どうもありがとう;)dateformatterがnil dateを返すことで私の頭を壁に叩いていた!!! :)
polo987

19

見落とされがちな問題は、ISO 8601形式の文字列にミリ秒がある場合とない場合があることです。

つまり、「2016-12-31T23:59:59.9999999」と「2016-12-01T00:00:00」はどちらも合法ですが、静的型付き日付フォーマッターを使用している場合は、どちらも解析されません。

始めてiOSの10あなたが使用する必要がありISO8601DateFormatter、そのハンドルにISO 8601日付文字列のすべてのバリエーションを。以下の例をご覧ください。

let date = Date()
var string: String

let formatter = ISO8601DateFormatter()
string = formatter.string(from: date)

let GMT = TimeZone(abbreviation: "GMT")
let options: ISO8601DateFormatOptions = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone]
string = ISO8601DateFormatter.string(from: date, timeZone: GMT, formatOptions: options)

下記のiOS 9とは、複数のデータフォーマッタと、次のアプローチを使用しています。

両方のケースをカバーし、この微妙な違いを抽象化する答えは見つかりませんでした。これに対処するソリューションは次のとおりです。

extension DateFormatter {

    static let iso8601DateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static let iso8601WithoutMillisecondsDateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static func date(fromISO8601String string: String) -> Date? {
        if let dateWithMilliseconds = iso8601DateFormatter.date(from: string) {
            return dateWithMilliseconds
        }

        if let dateWithoutMilliseconds = iso8601WithoutMillisecondsDateFormatter.date(from: string) {
            return dateWithoutMilliseconds
        }

        return nil
    }
}

使用法:

let dateToString = "2016-12-31T23:59:59.9999999"
let dateTo = DateFormatter.date(fromISO8601String: dateToString)
// dateTo: 2016-12-31 23:59:59 +0000

let dateFromString = "2016-12-01T00:00:00"
let dateFrom = DateFormatter.date(fromISO8601String: dateFromString)
// dateFrom: 2016-12-01 00:00:00 +0000

また、日付フォーマッターに関するAppleの記事を確認することをお勧めします。


Obj-CでNSISO8601DateFormtterを取得する方法も教えてください。(もちろんiOS 10以降)
Motti Shneor

8

したがって、ここにあるNSDateでSam Soffeeのカテゴリを使用してください。そのコードをプロジェクトに追加すると、それ以降、NSDateで単一のメソッドを使用できます。

- (NSString *)sam_ISO8601String

純粋なCで記述されているため、1行であるだけでなく、NSDateFormatterアプローチよりもはるかに高速です。


1
カテゴリの現在の場所はここ
ペリー

1
私はそれに対して助言します、非常にランダムに発生するメモリ例外の問題があります
M_Waly

1
@M_Walyこれを彼に報告しましたか?私は警告なしで彼のコードを構築しました、そしてそれは分析チェックをうまく通過しました。
David H

6

よりIS8601、問題は、表現とタイムゾーンです

  • ISO 8601 = year-month-day time timezone
  • 日付と時刻には、基本形式(YYYYMMDD、hhmmss、...)と拡張形式(YYYY-MM-DD、hh:mm:ss、...)があります。
  • タイムゾーンはズールー語、オフセット、またはGMTです。
  • 日付と時刻の区切り文字にはスペースを使用できます。 T
  • 日付には週の形式がありますが、ほとんど使用されません
  • タイムゾーンは多くのスペースになる可能性があります
  • 2番目はオプションです

ここにいくつかの有効な文字列があります

2016-04-08T10:25:30Z
2016-04-08 11:25:30+0100
2016-04-08 202530GMT+1000
20160408 08:25:30-02:00
2016-04-08 11:25:30     +0100

ソリューション

NSDateFormatter

だからここに私がonmyway133 ISO8601で使用しているフォーマットがあります

let formatter = NSDateFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.dateFormat = "yyyyMMdd HHmmssZ"

Z識別子日フィールドシンボルテーブル

Z: The ISO8601 basic format with hours, minutes and optional seconds fields. The format is equivalent to RFC 822 zone format (when optional seconds field is absent)

ロケール設定を使用したデータのフォーマットについてlocale

ロケールは、ユーザーの優先言語ではなく、特定のユーザーの書式設定の選択肢を表します。これらは多くの場合同じですが、異なる場合があります。たとえば、ドイツに住んでいるネイティブスピーカーは、言語として英語を選択し、地域としてドイツを選択する可能性があります。

en_US_POSIX 技術的なQ&A QA1480 NSDateFormatterとインターネット日付

一方、固定形式の日付を使用している場合は、最初に日付フォーマッターのロケールを固定形式に適したロケールに設定する必要があります。ほとんどの場合、最適なロケールは "en_US_POSIX"です。これは、ユーザーとシステムの両方の設定に関係なく、米国英語の結果を生成するように特別に設計されたロケールです。"en_US_POSIX"も時系列で不変(米国の場合、将来のある時点で日付のフォーマット方法が変更され、 "en_US"は新しい動作を反映するように変更されますが、 "en_US_POSIX"は変更されません)、およびマシン間( "en_US_POSIX"は、iOSではOS Xで、他のプラットフォームでは同じように機能します。

興味深い関連質問


2016-07-23T07:24:23Zで試したところ、機能しません
Kamen Dobrev

@KamenDobrev「動作しない」とはどういう意味ですか?私はあなたの例をテストに追加するだけですgithub.com/onmyway133/ISO8601/blob/master/ISO8601Tests/…そしてそれは機能します
onmyway133


3

この要旨に基づいて:https : //github.com/justinmakaila/NSDate-ISO-8601/blob/master/NSDateISO8601.swift、次のメソッドを使用してNSDateをyyyy-MM形式のISO 8601日付文字列に変換できます。 -dd'T'HH:mm:ss.SSSZ

-(NSString *)getISO8601String
    {
        static NSDateFormatter *formatter = nil;
        if (!formatter)
        {
            formatter = [[NSDateFormatter alloc] init];
            [formatter setLocale: [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
            formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation: @"UTC"];
            [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS"];

        }
        NSString *iso8601String = [formatter stringFromDate: self];
        return [iso8601String stringByAppendingString: @"Z"];
    }

Sここでは、1秒未満のキャップが重要でした
Mark Meyer

3つ以上のSが必要な場合は、日付フォーマッターを使用して手動で解析する必要がないことに注意してください。
マルハル2016

-1

これは少し簡単で、日付をUTCに入れます。

extension NSDate
{
    func iso8601() -> String
    {
        let dateFormatter = NSDateFormatter()
        dateFormatter.timeZone = NSTimeZone(name: "UTC")
        let iso8601String = dateFormatter.stringFromDate(NSDate())
        return iso8601String
    }
}

let date = NSDate().iso8601()

-1

Swift 3.0およびiOS 9.0の使用

extension Date {
    private static let jsonDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(identifier: "UTC")!
        return formatter
    }()

    var IOS8601String: String {
        get {
            return Date.jsonDateFormatter.string(from: self)
        }
    }

    init?(fromIOS8601 dateString: String) {
        if let d = Date.jsonDateFormatter.date(from: dateString) {
            self.init(timeInterval: 0, since:d)
        } else {
            return nil
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.