【Swift】DateFormatterで日付や時刻を出力する

＊[前回の記事](https://qiita.com/tamadeveloper/items/8e4f68d586cc5b0f9a5a)では日付の操作（週や月の始まりを取得する、等）について説明しました。

Swiftでは`DateFormatter`を使って`Date`の日付や時刻を出力できます。一番簡単な方法はシステムで用意されている`dateStyle`や`timeStylew`を利用することです。

```swift
let date = Date()
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
print(formatter.string(from: date))
```

dateStyleとtimeStyleのそれぞれの値は以下のような出力となります。

| dateStyle|  英語    |  日本語     |
|:-----------------|:------------------:|:------------------:|
| .full | Wednesday, May 30, 2018 |  2018年5月30日 水曜日 |
| .long | May 30, 2018 | 2018年5月30日 |
| .medium | May 30, 2018 | 2018/05/30 |
| .short | 5/30/18 |  2018/05/30  |

| timeStyle  |  英語  |  日本語 |
|:-----------------|:------------------:|:------------------:|
| .full | 9:15:40 PM Japan Standard Time | 21時20分47秒 日本標準時 |
| .long | 9:15:40 PM GMT+9 | 21:20:47 JST |
| .medium | 9:15:40 PM | 21:20:47  |
| .short |  9:15 PM | 21:20  |

ただし、これでは思ったような出力ができない場合があります。そこで`DateFormatter`をより細かく設定して使います。

# `setLocalizedDateFormatFromTemplate`を利用する

`DateFormatter`の`setLocalizedDateFormatFromTemplate`メソッドを利用すると、様々な日付の表示が可能となります。

```swift
let date = Date()
let formatter = DateFormatter()
formatter.setLocalizedDateFormatFromTemplate("yMMMMdEEEE")
print(formatter.string(from: date))

// 出力：Thursday, December 20, 2018（英語の場合）
// 出力：2018年12月20日 木曜日（日本語の場合）
```

上記のコードで日付の表示が可能です。その国の表記方法に合わせて、曜日を一番前に表示したり、ドイツ語やフランス語などの場合には月の前に日にちを表示したりしてくれます。

月や曜日を短めに表示するには以下のコードを用います。

```swift
formatter.setLocalizedDateFormatFromTemplate("yMMMdE")
print(formatter.string(from: date))

// Thu, Dec 20, 2018
// 2018年12月20日(木)
```

年と月だけを表示することも可能です。

```swift
formatter.setLocalizedDateFormatFromTemplate("yMMMM")
print(formatter.string(from: date))

// December 2018
// 2018年12月
```

システム設定で和暦を指定している場合は、「2018年」の代わりに「平成30年」と表示されます。

 年と月はさらに短く表示できます。

```swift
formatter.setLocalizedDateFormatFromTemplate("yM")
print(formatter.string(from: date))

// 5/2018（英語）
// 2018/5（日本語）
```

時刻の表示は以下の通りです。

```swift
formatter.setLocalizedDateFormatFromTemplate("jms")
print(formatter.string(from: date))

// 10:34:12 PM（英語）
// 22:34:12（日本語）
```

秒を省いて表示することも可能です。

```swift
formatter.setLocalizedDateFormatFromTemplate("jm")
print(formatter.string(from: date))

// 10:34 PM（英語）
// 22:34（日本語）
```

システム設定で12時間表示を指定している場合は、「午後10時34分」と表示されます。

なお、`jm`の代わりに`Hm`を指定した場合は、システム設定に関わらず24時間表示となり、「午前」「午後」（英語の場合は「AM」「PM」）が表示されなくなります。また`hm`を指定すると、必ず「午前」「午後」が表示されるようになります。

そのため、システム設定に合わせた時刻を表示するには`jm`を使うのが良いです。

# `dateFormat`と`setLocalizedDateFormatFromTemplate`の違い

`setLocalizedDateFormatFromTemplate`と似た`dateFormat`というプロパティがありますが、こちらはローカライズに対応していません。

例えば`yMMMM`を指定すると年が月の前に表示されます。日本語では正しい順番ですが、欧米では「May 2018」のように年は最後に表記されるのが一般的なので、国によってはおかしな表記になってしまう場合があります。また`y`は日本語の場合「2018」としか表示されず、その結果「20185月」と表示されます。

```swift
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yMMMM"
print(formatter.string(from: date))

// 2018May（英語）
// 20185月（日本語）
```

そのため基本的に`dateFormat`は特殊な場合を除いてあまり使わない方が良いと思われます。

# 「明日 10:00」のように表示させる

日付の代わりに「今日」や「明日」と表示させたい場合があります。この場合は`dateStyle`を使う必要があります。`doesRelativeDateFormatting`プロパティを`true`にすると、「今日」や「明日」などと表示されます。

```swift
let date = Date()
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
formatter.doesRelativeDateFormatting = true
print(formatter.string(from: date))

// Today 10:34
// 今日 10:34
```

# タイムゾーンを指定して出力する

`DateFormatter`の`timeZone`プロパティを指定すると、その地域の日付・時刻を表示します。例えば現在のアメリカ西海岸時刻、日本時刻、ベルリン時刻を以下のコードで表示できます。

```swift
let date = Date()
let formatter = DateFormatter()
formatter.setLocalizedDateFormatFromTemplate("yMMMMdE jm")

if let timeZone = TimeZone(identifier: "America/Los_Angeles") {
    formatter.timeZone = timeZone
    print(formatter.string(from: date))
    
    // Wed, May 30, 2018, 11:47 AM
    // 2018年5月30日(水) 11:51
}

if let timeZone = TimeZone(identifier: "Asia/Tokyo") {
    formatter.timeZone = timeZone
    print(formatter.string(from: date))

    // Thu, May 31, 2018, 3:47 AM
    // 2018年5月31日(木) 3:51
}

if let timeZone = TimeZone(identifier: "Europe/Berlin") {
    formatter.timeZone = timeZone
    print(formatter.string(from: date))

    // Wed, May 30, 2018, 8:47 PM
    // 2018年5月30日(水) 20:51
}
```