NSDateFormatterの固定形式の日付の設定について（和暦と24時間表示オフ対策）

NSDateFormatterを使用しているときに、和暦と24時間表示オフでのところで意図した動作にならないという話はよく聞きます。和暦や24時間表示オフでも正しい動作になるようにするための固定形式の日付の設定はどのようにすれば良いのか考えてみました。

# Apple Developerで書かれている固定フォーマット

Working With Fixed Format Date Representations（固定フォーマットの日付表​​現の操作）
https://developer.apple.com/reference/foundation/nsdateformatter

固定形式の日付で作業する場合は、書式文字列を指定するためにdate Formatプロパティを設定します。ほとんどの固定フォーマットでは、localeプロパティをPOSIXロケール（"en_US_POSIX"）に設定し、time ZoneプロパティをUTCに設定します。

```objc
RFC3339DateFormatter = [[NSDateFormatter alloc] init];
RFC3339DateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
RFC3339DateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZZZZZ";
RFC3339DateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
 
/* 39 minutes and 57 seconds after the 16th hour of December 19th, 1996 with an offset of -08:00 from UTC (Pacific Standard Time) */
NSString *string = @"1996-12-19T16:39:57-08:00";
NSDate *date = [RFC3339DateFormatter dateFromString:string];
```

# 色々な方法を試してみました。
公式で言ってるので、それを利用すればいいのですが、他も色々試してみました。
カレンダー設定は、西暦（グレゴリオ歴）にするための設定です。

## サンプルコード
```objc
//現在時刻を取得する
NSDate *now = [NSDate date];

// calendarなしlocaleなし
NSDateFormatter *dateFormatter0 = [[NSDateFormatter alloc] init];
[dateFormatter0 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

[dateFormatter0 setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];

NSString *strDate0 = [dateFormatter0 stringFromDate:now];

NSLog(@"calendarなしlocaleなし:%@", strDate0);


// calendarありlocaleはsystemLocale
NSDateFormatter *dateFormatter1 = [[NSDateFormatter alloc] init];
NSCalendar *calendar1 = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
[dateFormatter1 setCalendar:calendar1];
[dateFormatter1 setLocale:[NSLocale systemLocale]];
[dateFormatter1 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

[dateFormatter1 setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];

NSString *strDate1 = [dateFormatter1 stringFromDate:now];

NSLog(@"calenderありlocaleはsystemLocale:%@", strDate1);


// calenderなしlocaleはsystemLocale
NSDateFormatter *dateFormatter2 = [[NSDateFormatter alloc] init];
[dateFormatter2 setLocale:[NSLocale systemLocale]];
[dateFormatter2 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

[dateFormatter2 setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];

NSString *strDate2 = [dateFormatter2 stringFromDate:now];

NSLog(@"calenderなしlocaleはsystemLocale:%@", strDate2);


// calenderなしlocaleはen_US_POSIX
NSDateFormatter *dateFormatter3 = [[NSDateFormatter alloc] init];
[dateFormatter3 setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
[dateFormatter3 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

[dateFormatter3 setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];

NSString *strDate3 = [dateFormatter3 stringFromDate:now];

NSLog(@"calenderなしlocaleはen_US_POSIX:%@", strDate3);


// calendarありlocaleなし
NSDateFormatter *dateFormatter4 = [[NSDateFormatter alloc] init];
NSCalendar *calendar4 = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
[dateFormatter4 setCalendar:calendar4];
[dateFormatter4 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

[dateFormatter4 setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];

NSString *strDate4 = [dateFormatter4 stringFromDate:now];

NSLog(@"calendarありlocaleなし:%@", strDate4);
```
##実行結果
### 1.西暦（グレゴリオ歴）で２４時間表示
この場合は、すべて同じ結果になりました。

```
calendarなしlocaleなし: 2017-05-19T01:56:55Z
calenderありlocaleはsystemLocale: 2017-05-19T01:56:55Z
calenderなしlocaleはsystemLocale: 2017-05-19T01:56:55Z
calenderなしlocaleはen_US_POSIX: 2017-05-19T01:56:55Z
calendarありlocaleなし: 2017-05-19T01:56:55Z
```
### 2.和暦で２４時間表示
calendarなしだと年の表示が和暦になっています。
しかし、localeを設定していれば、西暦表示となっています。

```
calendarなしlocaleなし: 0029-05-19T02:10:29Z
calenderありlocaleはsystemLocale: 2017-05-19T02:10:29Z
calenderなしlocaleはsystemLocale: 2017-05-19T02:10:29Z
calenderなしlocaleはen_US_POSIX: 2017-05-19T02:10:29Z
calendarありlocaleなし: 2017-05-19T02:10:29Z
```
### 3.西暦（グレゴリオ歴）で２４時間表示OFF
localeなしだと時間の表示が１２時間表示になっています。

```
calendarなしlocaleなし: 2017-05-19T午前2:13:11Z
calenderありlocaleはsystemLocale: 2017-05-19T02:13:11Z
calenderなしlocaleはsystemLocale: 2017-05-19T02:13:11Z
calenderなしlocaleはen_US_POSIX: 2017-05-19T02:13:11Z
calendarありlocaleなし: 2017-05-19T午前2:13:11Z
```

### 4.和暦で２４時間表示OFF
calendarなしlocaleなしの場合は、年の表示が和暦になり、
calendarありlocaleなしだと１２時間表示になっています。

```
calendarなしlocaleなし: 0029-05-19T2:15:24Z
calenderありlocaleはsystemLocale: 2017-05-19T02:15:24Z
calenderなしlocaleはsystemLocale: 2017-05-19T02:15:24Z
calenderなしlocaleはen_US_POSIX: 2017-05-19T02:15:24Z
calendarありlocaleなし: 2017-05-19T午前2:15:24Z
```

# calendarの設定は不要！？
色々試してみ結果、和暦のためのcalenderの設定は、localeをsystemLocaleかen_US_POSIXに設定していれば不要ということがわかりました。
ただ、localeはこの場合だとsystemLocaleでも問題ないのですが、systemLocaleは汎用ロケールなので、確実な固定フォーマットを用いる場合は、en_US_POSIXをLocaleに設定するのが無難だと思われます。