【初心者向け・動画付き】Railsで日時をフォーマットするときはstrftimeよりも、lメソッドを使おう

## はじめに：日時の表示に関してよくある問題

何も考えずにViewに`created_at`のような日付を出力すると、「あれっ？」と思うような表示になることがあります。

```erb:app/views/users/index.html.erb
<% @users.each do |user| %>
  <tr>
    <td><%= user.name %></td>
    <!-- 作成日時を表示する -->
    <td><%= user.created_at %></td>
    <td><%= link_to 'Show', user %></td>
    <td><%= link_to 'Edit', edit_user_path(user) %></td>
    <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
  </tr>
<% end %>
```

<img width="453" alt="Screen Shot 2019-07-30 at 20.47.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/6c12ece9-5d5c-b342-eadc-fde9fb9cbfb1.png">


具体的には以下の2つのポイントが「あれっ？」と思う点だと思います。

- 日本時間（JST）ではなく、世界標準時（UTC）で表示されてしまう
- "Tue, 30 Jul 2019 00:12:19 +0000"のようなフォーマットは日本人にとって馴染みがない

この記事ではこの問題を解決する方法を紹介します。

## タイムゾーンを日本時間に変更する

最初にタイムゾーンを日本時間にしましょう。
`config/application.rb`に以下の設定を追加し、サーバーを再起動してください。

```config/application.rb
module TimeFormatSandbox
  class Application < Rails::Application
    # ...
    
    # タイムゾーンを日本時間に設定
    config.time_zone = 'Asia/Tokyo'
  end
end
```

これで画面に表示された日時が日本時間になります。

<img width="446" alt="Screen Shot 2019-07-30 at 20.52.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/14dc4b35-f336-e7d6-f28b-d5227bcc088a.png">

## 日本人が読みやすい日時フォーマットにする

続いて、日時の表示形式を日本人が読みやすいフォーマットに変更しましょう。

### :warning: strftimeでも変更できるが、あまりオススメできない

ネットを検索すると以下のように`strftime`メソッドを使って検索する方法がよく出てきます。

```erb
<%= user.created_at.strftime('%Y/%m/%d %H:%M:%S') %>
```

もちろんこれでも目的は達成できるのですが、他にも日時を表示するViewがあると、`'%Y/%m/%d %H:%M:%S'`のような書式文字列を繰り返し書かないといけないため、コードが[DRY](https://ja.wikipedia.org/wiki/Don%27t_repeat_yourself)になりません。

DRYでないコードは変更に弱いコードになります。
変更に弱いコードは良くないコードです。

### ✅ lメソッドでDRYに書式を指定する（オススメ）

Railsには`l`メソッドという便利なメソッドがあるので、これを活用しましょう。

以下は`l`メソッドの使用例です。

```erb
<!-- lメソッドを使って書式を指定する -->
<td><%= l user.created_at %></td>
```

ただし、`l`メソッドを使うだけでは何も変化がありません。


<img width="446" alt="Screen Shot 2019-07-30 at 20.52.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/14dc4b35-f336-e7d6-f28b-d5227bcc088a.png">

`l`メソッドを活用するには、もう少し作業が必要です。

次に行うのはロケールの設定です。
今回は日本人が読みやすい書式にするのが目的なので、アプリケーションのロケールを`:ja`に設定します。

`config/application.rb`に以下の設定を追加してください。

```config/application.rb
module TimeFormatSandbox
  class Application < Rails::Application
    # ...
    
    # デフォルトのロケールを日本（ja）に設定
    config.i18n.default_locale = :ja
  end
end
```

続いて、`config/locales/ja.yml`というファイルを作成し、以下のような設定を記述します。

```config/locales/ja.yml
ja:
  time:
    formats:
      default: "%Y/%m/%d %H:%M:%S"
```

上の設定は日時（time）のデフォルトの書式を`%Y/%m/%d %H:%M:%S`にするための設定です。

これでサーバーを再起動すると、日本人向けの書式で日時が表示されます。

<img width="372" alt="Screen Shot 2019-07-30 at 21.33.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/67940fed-7393-e71e-d628-c986cc444aae.png">

`strftime`メソッドとは異なり、`l foo.created_at`のような記述でどのViewでも同じ書式が得られるため、コードもDRYになります。

### 応用: 複数の書式を使い分ける

「`l`メソッド、便利なんだけど、この画面だけは"07/30 09:12"みたいに短く表示したいんだよな〜 :thinking: 」というそこのあなた！
大丈夫です。`format`オプションを付けると、書式を切り替えることができます。

```erb
<!-- formatオプションを指定する -->
<td><%= l user.created_at, format: :short %></td>
```

上の例では`:short`を指定したので、`ja.yml`に対応する書式を設定します。

```ja.yml
ja:
  time:
    formats:
      default: "%Y/%m/%d %H:%M:%S"
      short: "%m/%d %H:%M"
```

ご覧のとおり、書式を切り替えることができました。
<img width="301" alt="Screen Shot 2019-08-02 at 7.13.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/6c02784e-4dcd-4ec7-c509-7925f69f06cd.png">

### Tips: i18n_generatorsでja.ymlを自動生成する

上で作成した`config/locales/ja.yml`は、[i18n_generators gem](https://github.com/amatsuda/i18n_generators)を使って自動生成すると便利です。
このgemの使い方は以下のとおりです。

まず、Gemfileにi18n_generatorsを追加し、`bundle install`を実行します。

```ruby:Gemfile
group :development do
  # ...

  gem 'i18n_generators'
end
```

次にターミナルから以下のコマンドを実行します。

```console
rails g i18n_locale ja
```

`config/locales/ja.yml`が生成されるので、このファイルを開いて`ja.time.formats.default`の書式文字列を編集します。
デフォルトの書式文字列は `"%Y年%m月%d日(%a) %H時%M分%S秒 %z"` になっているので、これを要件に合わせて変更してください。

なお、i18n_generatorsが生成した`ja.yml`には他にも日本語ロケール向けの設定がたくさん定義されています。
i18n_generatorsに関する詳しい情報はREADMEを参照してください。

https://github.com/amatsuda/i18n_generators

### Tips: View以外ではI18n.lを使う

Viewでは`l`と書くだけでOKですが、`l`メソッドがヘルパーメソッドとして提供されていない場所（Modelなど）では、`l`だけではNoMethodErrorになります。
その場合は、`l`の代わりに`I18n.l`と書けば、日時をフォーマットすることができます。

ちなみに`l`は`localize`のエイリアスメソッドなので、`localize`と書いても構いません。

以下の記述はどれも同じ結果になります。
 
```ruby 
= l user.created_at
= I18n.l user.created_at
= localize user.created_at
= I18n.localize user.created_at
```

### 追記：Time::DATE_FORMATS[:default]を変更するのもオススメしない

日時のフォーマットを変更する方法としてもうひとつ、`Time::DATE_FORMATS[:default]`の設定を変えるという方法もあるようです。

```config/initializers/time_formats.rb
Time::DATE_FORMATS[:default] = '%Y/%m/%d %H:%M:%S'
```

こうすると、`l`メソッドも`strftime`も使わずに書式を統一することができます。

```erb 
<!-- 何もしなくても書式が'%Y/%m/%d %H:%M:%S'になる -->
<td><%= user.created_at %></td>
```

これだけ見ると「すごく便利じゃん！」と思うかもしれません。
ですが、アプリケーション全体のデフォルト設定が変わってしまうため、予期しない問題を引き起こすかもしれません。
実際に問題が発生する事例は以下の記事で紹介されています。

[RailsのTime::DATE\_FORMATS\[:default\]は変更しないほうがいい \- Qiita](https://qiita.com/yu_suke1994/items/53cec3852cd5b9df3256)

ですので、この方法もあまりオススメできません。

## 動画はこちら

この記事の内容はYouTube動画としてアップしています。
動画を見たい方は以下のリンクから視聴してください。

https://youtu.be/QUnWDvPONAo

## 参考文献

- [Rails アプリケーションを設定する \- Rails ガイド](https://railsguides.jp/configuring.html)
- [Rails 国際化 \(i18n\) API \- Rails ガイド](https://railsguides.jp/i18n.html)