RailsのTime::DATE_FORMATS[:default]は変更しないほうがいい

## Time::DATE_FORMATS[:default]を変更する記事たち
- [日付/時間フォーマットのデフォルトを設定 - Qiita](https://qiita.com/Vit-Symty/items/399c77d1fd681b77d593)
- [Railsで表示する日付のフォーマットを変える - 動かざることバグの如し](http://thr3a.hatenablog.com/entry/20151021/1445400833)
- [Rails:日時/時刻表示方法（設定）を変更する - Hello world, I am kgmx.](http://kgmx.hatenablog.com/entry/2014/08/27/085515)

Railsで `Time#to_s` をした場合の書式を変更する方法は「rails 時刻 フォーマット」などで調べると出てくるように、 `Time::DATE_FORMATS` を独自に定義する方法がよく知られています。

しかし、いくつかの記事では `Time::DATE_FORMATS[:default]` を変更しています。これはアプリケーションの動作に問題を引き起す可能性がとても高いです。

## 何が問題なのか
そもそも、アプリケーション全体で使用されるdefaultの値を変更する、という時点で影響範囲がとても広くなってしまうということはおわかりいただけると思います。そして、この設定値は、あなたが書くアプリケーションのコードだけではなく、そのアプリケーションが依存しているgemにも影響します。

具体例を出します。

### sidekiq-cron v0.6.3 → v1.0.4 で発生する問題
あるアプリケーションで、 sidekiq-cron を使用しており、そのバージョンを v0.6.4 から v1.0.4 にアップデートすることを考えてみます。

sidekiq-cron は、CronJob が最後にenqueueされた時刻をRedisに保存します。 v0.6.4 でのコードは以下のようになっています。

```ruby:lib/sidekiq/cron/job.rb
#enque cron job to queue
def enque! time = Time.now.utc
  @last_enqueue_time = time
```

https://github.com/ondrejbartas/sidekiq-cron/blob/v0.6.3/lib/sidekiq/cron/job.rb#L47-L74

ここで、 `Time.now.utc` の結果は、このまま `to_s` されてRedisに保存されます。

さて、 Redisから最後に queue に入った時刻を取り出す部分の v1.0.4 でのコードが以下のようになっています。

```ruby:lib/sidekiq/cron/job.rb
def parse_enqueue_time(timestamp)
  DateTime.strptime(timestamp, LAST_ENQUEUE_TIME_FORMAT).to_time.utc
rescue ArgumentError
  DateTime.strptime(timestamp, LAST_ENQUEUE_TIME_FORMAT_OLD).to_time.utc
end
``` 

https://github.com/ondrejbartas/sidekiq-cron/blob/v1.0.4/lib/sidekiq/cron/job.rb#L554-L558

そして、 `LAST_ENQUEUE_TIME_FORMAT_OLD` と `LAST_ENQUEUE_TIME_FORMAT` は次のようになっています。

```ruby
LAST_ENQUEUE_TIME_FORMAT = '%Y-%m-%d %H:%M:%S %z'
LAST_ENQUEUE_TIME_FORMAT_OLD = '%Y-%m-%d %H:%M:%S'
```

https://github.com/ondrejbartas/sidekiq-cron/blob/v1.0.4/lib/sidekiq/cron/job.rb#L15-L16


この辺で勘のいい方は気づかれるかと思いますが、Redisからの `last_enqueue_time` をparseする際、書式が想定外だと例外が発生します。

つまり、 `Time::DATE_FORMATS[:default]` を独自定義していた場合、 sidekiq-cron v0.6.4 時代に実行されたCronJobは v1.0.4 にアップデートすると実行できずに例外が発生してしまいます。

## ではどうすればいいのか
### 1. 都度 strftime する
デフォルトでの `to_s` の結果が望ましくない場合、以下のように都度`strftime`で書式を設定してやるとよいでしょう。

```ruby
Time.zone.now.to_s # => "2006-01-02 15:04:05 +0900"
Time.zone.now.strftime('%Y年%m月%d日') # => "2006年01月02日"
Time.zone.now.strftime('%Y/%m/%d %A') # => "2006/01/02 Monday"
```

### 2. `Time::DATE_FORMATS` に独自の書式を追加する
もしくは、`Time::DATE_FORMATS[:default]` 以外に独自定義した書式を登録し、それを使うようにすればよいでしょう。

```ruby
Time::DATE_FORMATS[:custom] = '%Y/%m/%d %A'

Time.zone.now.to_s(:custom) # => "2006/01/02 Monday"
Time.zone.now.to_s # => "2006-01-02 15:04:05 +0900"
```

### 3. I18nの仕組みを使用する by @onk@github 
<blockquote class="twitter-tweet" data-cards="hidden" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://t.co/V0XPXb6qWU">https://t.co/V0XPXb6qWU</a> ではなく？ ところでコレは DATE_FORMATS 使わずに locale の ja.time.formats に足して I18n.l(now, format: :custom) じゃないかと思いました。</p>&mdash; Takafumi ONAKA (@onk) <a href="https://twitter.com/onk/status/1075063729987846145?ref_src=twsrc%5Etfw">2018年12月18日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Time.current. to_s(:db) は timezone 変えるので、なんとなく to_s(:foo) を単なるフォーマット用途に使うのに恐怖を覚える <a href="https://t.co/Uj6cfhcMqZ">https://t.co/Uj6cfhcMqZ</a></p>&mdash; Takafumi ONAKA (@onk) <a href="https://twitter.com/onk/status/1075077636777336832?ref_src=twsrc%5Etfw">2018年12月18日</a></blockquote>




## 参考
- [Rails で時間・時刻表示の独自フォーマットを作成する | EasyRamble](https://easyramble.com/original-time-format-on-rails.html)