- Ruby / Rails関連
[Rails5] Active Support Core ExtensionsのStringクラス(3)#to_time、#to_date、#to_datetime
こんにちは、hachi8833です。
Active Support探訪シリーズ第3回は、Core Extensionsのconversions_rbにある、Stringクラスの3つのメソッドです。
今回のメソッド
- メソッド:
String#to_time、String#to_date、String#to_datetime - ディレクトリ配置: rails/rails/blob/5-0-stable/activesupport/lib/active_support/core_ext/string/conversions.rb
条件
- Railsバージョン: 5-0-stable
- Rubyバージョン: 2.3.3
conversions.rb
ソースのコメント等は適宜省略します。
require 'date'
require 'active_support/core_ext/time/calculations'
class String
def to_time(form = :local)
parts = Date._parse(self, false)
used_keys = %i(year mon mday hour min sec sec_fraction offset)
return if (parts.keys & used_keys).empty?
now = Time.now
time = Time.new(
parts.fetch(:year, now.year),
parts.fetch(:mon, now.month),
parts.fetch(:mday, now.day),
parts.fetch(:hour, 0),
parts.fetch(:min, 0),
parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
parts.fetch(:offset, form == :utc ? 0 : nil)
)
form == :utc ? time.utc : time.to_time
end
def to_date
::Date.parse(self, false) unless blank?
end
def to_datetime
::DateTime.parse(self, false) unless blank?
end
end
Active Supportらしい短いコードなのでほっとします。
追伸: RubyMineとpryはコードダイブにも便利
Railsのコードを追うときは、実際にはどのクラスのメソッドが呼び出されているのかを常に気にする必要があります。RubyMineの強力なコードジャンプ機能とpryでのメソッド定義表示は、こういうときにもとても役に立ちますね。
コードの内容
String#to_time、String#to_date、String#to_datetimeはRubyにはないメソッドです。
たまにピュアRubyを使っていると#to_dateがないことに気付いて、いそいそとActive Supportをrequireしたりしますね。
最初に以下をrequireしています。
require 'date'
require 'active_support/core_ext/time/calculations'
参考までに、require元のactive_support/core_ext/time/calculationsではさらに以下をrequireしています。
require 'active_support/duration'
require 'active_support/core_ext/time/conversions'
require 'active_support/time_with_zone'
require 'active_support/core_ext/time/zones'
require 'active_support/core_ext/date_and_time/calculations'
require 'active_support/core_ext/date/calculations'
コード内で頻繁に使われている#parseメソッドは、RubyのDateクラス、Timeクラス、DateTimeクラスのメソッドです。以下ではDate#parseを明示的に呼んでいます。
::Date.parse(self, false) unless blank?
String#to_dateとString#to_datetime
#to_dateと#to_datetimeはunless blank?だけチェックして、素直にDate#parseやDateTime#parseを呼んでいます。
def to_date
::Date.parse(self, false) unless blank?
end
def to_datetime
::DateTime.parse(self, false) unless blank?
end
簡単でよかった。
String#to_time
String#to_timeはもう少し凝ったコードです。デフォルト値は:localです。
def to_time(form = :local)
parts = Date._parse(self, false)
used_keys = %i(year mon mday hour min sec sec_fraction offset)
return if (parts.keys & used_keys).empty?
now = Time.now
time = Time.new(
parts.fetch(:year, now.year),
parts.fetch(:mon, now.month),
parts.fetch(:mday, now.day),
parts.fetch(:hour, 0),
parts.fetch(:min, 0),
parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
parts.fetch(:offset, form == :utc ? 0 : nil)
)
form == :utc ? time.utc : time.to_time
end
#_parseメソッド
上でアンダースコア付きの#_parseメソッドが呼ばれています。
parts = Date._parse(self, false)
Date#_parseはRubyのext/date/date_core.cにありました。リンク先はRubyの標準ライブラリなのでC言語のコードになっています。
アンダースコアなしのDate#parseは時間の文字列をDateオブジェクトに変換するメソッドですが、アンダースコアありのDate#_parseはハッシュを返す点が異なります。
#fetchメソッド
続く#fetchメソッドはRubyのHashクラスで、hash.cにあります。hash.cファイルがRubyプロジェクトディレクトリの直下にあったのでちょっとびっくりしました。
parts.fetch(:year, now.year),
Hash#fetchはハッシュのキーを与えて値を返しますが、キーが空の場合のデフォルト値を与えられるので簡潔に書くことができます。
タイムゾーンがUTCの場合の処理
最後にformが:utcの場合の処理を加えています。それ以外の場合はformで指定したタイムゾーン(デフォルトは:local)が使われます。
form == :utc ? time.utc : time.to_time
こんなふうになっていたんですね。
関連記事
[Rails5] Active Support Core ExtensionsのStringクラス(2)html_safe
[Rails5] Active Support Core ExtensionsのStringクラス(1)String#blank?
