※ 結論だけ知りたいという人は解決法から見てください
「まだRailsでstrftime使ってるやついる?」
「いねえよなぁ!!?」
...
...
はい、使ってます。
strftime
を使用して、日時をフォーマットする際
<p class="time"><%= @model.time.strftime('%Y年%m月%d日 %H時%M分') %></p>
のように毎回記述する必要があり
「いちいち指定しなくても同じ形式でフォーマットしてほしい」
「フォーマットをまとめて管理したい」
といった悩みに苦しめられてきました。
「仕方ない」と諦めていたこの悩み...解決する方法を実は見つけてしまったのです。
私みたいに諦めてstrftimeを使っているそこの貴方、今まさにstrftimeを使おうとしているそこの貴方。
「もっと早く気づいていれば、もっといい感じにプロダクトを作れた」
と思うその前に、是非参考にしてみてください。
strftimeとは?
strftimeは、日時のデータを指定したフォーマット文字列に変換できるメソッドです。
詳しい使い方はRubyリファレンスマニュアルを参照してください。
strftimeを使いたくない理由
日時のフォーマットで検索するとよく出てくるstrftimeですが、 使用する時には
@model.time.strftime('%Y年%m月%d日 %H時%M分')
のような記述を繰り返し記述する必要があります。
全て%Y年%m月%d日 %H時%M分
のフォーマットで表示したいだけにも関わらず、同じ内容を繰り返し書くことになるので、コードがDRYな状態とは言えません。
「日時の表示を別の形式に変えて欲しい!」
といった急な要望が来た時
- 修正に時間がかかる
- 一部修正が漏れていた
といった問題が起きやすくなります。
解決法
ja.ymlにフォーマットを記述して、lメソッドを使って呼び出す
今回の問題は、これだけで解決します。
具体的な設定方法をこれから説明します。
デフォルトのlocaleをjaに設定
railsはconfig/locales
というフォルダの中で翻訳したい各国語の文言を管理しています。locale
は元々、多言語化用に使われるものでした。多言語化以外にも、バリデーションの文言を管理したり、各カラムの日本語名を管理したりと、用途は多岐に渡っています。
今回は、config/application.rb
でデフォルトの言語を:ja
(日本語)に指定します。
この設定を入れることで、config/locales/ja.yml
の中で言語を管理するようになります。
デフォルト言語を:ja
に設定して、ja.yml
で言語管理していくことは定番の方法なので、覚えておきましょう。
config.i18n.default_locale = :ja
ja.ymlに指定したいフォーマットを記述
config/locales/ja.yml
に指定したいフォーマットを記述します。ja.yml
がない場合は、作成してください。
ja:
date:
formats:
default: '%Y/%-m/%-d'
time:
formats:
default: '%Y-%-m-%-d %H:%M:%S'
Date
型のフォーマットは、date:
以下に指定したものが使用されます。
Time
、DateTime
、TimeWithZone
型のフォーマットは、time:
以下に指定したものが使用されます。
Date
型に対してtime:
のフォーマットは利用できず、エラーになります。逆も同様です。両方とも設定することをオススメします。
default:
に指定したフォーマットが標準になります。ご自身のアプリケーションの中で一番使われているフォーマットを指定してください。
lメソッドを使用して、フォーマットを反映する
l
メソッドは、I18nモジュールに用意されているlocalize
メソッドのエイリアスで、日付や数値などをlocale
に指定されているフォーマットに変換します。
<%= l @model.time %> <%#=> 2022-06-10 16:30:40 %>
ViewやControllerでは、l
メソッドがヘルパーメソッドとして用意されているので、l
だけで呼び出すことができます。
それ以外の場所では、I18n.l
で呼び出してください。
# Date
I18n.l(Date.today) #=> '2022/6/10'
# Date
I18n.l(Time.current.to_date) #=> '2022/6/13'
# Datetime
I18n.l(DateTime.now) #=> '2022-6-10 16:30:40'
# Time
I18n.l(Time.now) #=> '2022-6-10 16:30:40'
# TimeWithZone
I18n.l(Time.current) #=> '2022-6-10 16:30:40'
# ヘルパーが用意されていないところでlで呼び出すと、NoMethodErrorになる
l(Time.current) #=> NoMethodError (undefined method `l' ...
l
メソッドを使うことで、strftime
で必要だったフォーマット指定が必要なくなります。
応用: default以外のフォーマットを指定する
運用していく中で、日時の表示方法は一つとは限りません。
「このページは年の表示要らない」「このページだけ日本語で月とか日って表示したい」
といった時、default
以外のフォーマットを作成し、フォーマットを切り替えることが可能です。
ja:
date:
formats:
default: '%Y/%-m/%-d'
long: '%Y年%-m月%-d日(%a)'
abbr_day_names: [日, 月, 火, 水, 木, 金, 土]
time:
formats:
default: '%Y-%-m-%-d %H:%M:%S'
short: '%m-%d %H:%M'
<%= l @model.time, format: :short %> <%#=> 06-10 16:30 %>
I18n.l(Date.today, format: :long) #=> '2022年6月10日(金)'
I18n.l(Time.current, format: :short) #=> '06-10 16:30'
# Date型にtimeでしか指定していないフォーマットを使うとエラーになる
I18n.l(Date.today, format: :short) #=> I18n::MissingTranslationData (translation missing: ja.date.formats.short)
# TimeWithZone型にdateでしか指定していないフォーマットを使うとエラーになる
I18n.l(Time.current, format: :long) #=> I18n::MissingTranslationData (translation missing: ja.time.formats.long)
まとめ
strftime
だけを用いて都度日時のフォーマットをしていくと、コードがDRYな状態ではなくなっていきます。
- 対象の
locale
のymlに指定したいフォーマットを記述 -
l
メソッドを用いてView表示する
この2つのことをするだけで、日時のフォーマットをまとめて管理でき、よりDRYなコードに近づきます。
たくさんのstrftime
修正に悩まされるその前に、是非今回の方法を試してみてください。