- 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?