Rubyで日付・時間を操作

Perlで日付・時間を操作 - DateTime モジュールの使い方
http://iandeth.dyndns.org/mt/ian/archives/000619.html
のようなことをRubyでやる方法を調べてみました。

基本

現在日時のインスタンスを作る
Time.new
  #=> Mon Jun 05 20:03:59 JST 2006

Time.now
  #=> Mon Jun 05 19:47:04 JST 2006

Rubyの場合はデフォルトでローカルのタイムゾーンインスタンスを作ります。newとnowの違いはinitializeを呼ぶか呼ばないかだそうです。

initializeを再定義とかしない限りあまり関係なさそう?

任意の日時のインスタンスを作る
Time.mktime 2006, 6, 2, 16, 30, 50
  #=> Fri Jun 02 16:30:50 JST 2006

あるいは

Time.mktime 50, 30, 16, 2, 6, 2006, 6, 3, 3, "GMT"
  #=> Fri Jun 02 16:30:50 JST 2006

前者の形式では、年、月、日、時、分、秒と指定します。年以降は省略可です。後者の形式では、Time#to_aの結果と同じ順番で指定します。

詳しくは、Rubyリファレンスを参考に。
http://www.ruby-lang.org/ja/man/index.cgi?cmd=view;name=Time

文字列に変換する
time.to_s
  #=> "Fri Jun 02 16:30:50 JST 2006"

Rubyではymdやhmsのようなメソッドはないようです。同じようなことをするなら次のフォーマットで整形するしかなさそうです。

任意のフォーマットに整形された文字列を作る
Time.now.strftime "%Y/%m/%d %H:%M:%S"
  #=> "2006/06/05 19:59:19"
Time.now.strftime "%Y年%m月%d日 %H時%M分%S秒"
  #=> "2006年06月05日 20時00分10秒"

一つ前のhmsやymdをこれを用いて定義してみるとかんな感じか…。

class Time
 def ymd(splitter)
   [year, month, day].join splitter
 end
 def hms(splitter)
   [hour, min, sec].join splitter
 end
end
  #=> nil

Time.now.ymd "/"
  #=> "2006/6/5"

Time.now.hms "/"
  #=> "20/10/13"
月の日数を調べる

標準ではないようなので、Railsのライブラリの一つのActiveSupportを使います。

time = Time.mktime 2005, 2
  #=> Tue Feb 01 00:00:00 JST 2005

time.at_end_of_month.day
  #=> 28

閏年にも対応しているようです。

time = Time.mktime 2004, 2
  #=> Sun Feb 01 00:00:00 JST 2004

time.at_end_of_month.day
  #=> 29
曜日を調べる
wd = %w(月 火 水 木 金 土 日)
  #=> ["月", "火", "水", "木", "金", "土", "日"]

wd[Time.now.wday-1]
  #=> "月"

日時の加算・減算

これを楽に行なえそうなメソッドは標準では用意されてないようです。Railsのライブラリの一つのActiveSupportを使えば楽に出来ます。

4日足す(4日後)
require 'rubygems'
  #=> true

require 'active_support'
  #=> true

time = Time.mktime 2006, 5, 30
  #=> Tue May 30 00:00:00 JST 2006
4.days.since time
  #=> Sat Jun 03 00:00:00 JST 2006
time.since 4.days
  #=> Sat Jun 03 00:00:00 JST 2006
3年引く(3年前)
3.years.ago time
  #=> Fri May 30 06:00:00 JST 2003
time.years_ago 3
  #=> Fri May 30 00:00:00 JST 2003
うるう年を考慮する場合
time.last_year
  #=> Sat Jan 10 00:00:00 JST 2004

365.days.ago time
  #=> Sun Jan 11 00:00:00 JST 2004

ActiveSupport::CoreExtentions::Time#last_yearはうるう年を考慮してないっぽいので365日を引きます。

二つの日時の差分を調べる

差分を求めたりできるものについては探してみた感じではないようです。
何かご存知でしたらよろしくお願いします。

日時っぽい文字列をTimeオブジェクトに変換する

文字列パターンを指定した上で変換
require 'date'
 #=> yes

Date.strptime("2003年04月18日", "%Y年%m月%d日").to_s
  #=> "2003-04-18"

Timeクラスにはこのようなメソッドはありませんが、Dateクラスにはあるようです。ただ基本的にはTimeクラスの方が便利なのであまり使う機会はないかもしれません。またDateクラスでparseできるのは日付表現だけです。

ただし、日本語の日時をparseする際、Time.parseではうまく行かないようなのでDate.strptimeを使う必要がありそうです。DateオブジェクトをTimeオブジェクトに変換するには、ActiveSupport::CoreExtentions::Date#to_timeを使うとよさそうです。もちろん手動で

require 'date'
 #=> yes

date = Date.strptime("2003年04月18日", "%Y年%m月%d日")
  #=> #<Date: 4905495/2,0,2299161>

Time.mktime date.year, date.month, date.day
  #=> Fri Apr 18 00:00:00 JST 2003

とも出来ます。

WEBで使われているフォーマットを変換

require 'time'
  #=> yes

time = Time.parse "09 Feb 1994 22:23:32 GMT"
  #=> Thu Feb 10 07:23:32 JST 1994

time.year
  #=> 1994

通常文字列をTimeオブジェクトに変換するにはTime.parseメソッドを使いますが、よく使われているフォーマットの場合にはそれに対応したメソッドもあります。

time = Time.rfc2822 "09 Feb 1994 22:23:32 GMT"
  #=> Thu Feb 10 07:23:32 JST 1994

様々なフォーマットの文字列に変換する

現在のところ以下の3種類標準添付のtime.rbでサポートされているようです。

メールヘッダー用
require 'time'

Time.now.rfc2822
  #=> "Mon, 05 Jun 2006 21:54:02 +0900"
HTTP Protocol用(RFC 1123)
Time.now.httpdate
  #=> "Mon, 05 Jun 2006 12:55:57 GMT"

リファレンスによれば、このメソッドはRFC:2616に準拠しているようでネタもとのページとはRFCが異なるのですが、表示形式は同じのようです。

XML用(ISO8601)

RSS1.0などで使われている形式です。

Time.now.xmlschema
  #=> "2006-06-05T21:59:28+09:00"

Time.now.iso8601
  #=> "2006-06-05T21:59:39+09:00"

タイムゾーンの概念

Time.nowなどで生成した場合はシステムと同じタイムゾーンに設定されます。UTCに変換するにはutc(あるいはgmtime)メソッドを使います。

Time.now.utc
  #=> Mon Jun 05 13:06:56 UTC 2006

Time.now.gmtime
  #=> Mon Jun 05 13:06:58 UTC 2006

また、変換ではなく新たにUTCでTimeオブジェクトを使う場合はTime.utcを使います。

現在のタイムゾーン名を取得
Time.now.zone
  #=> "JST"
タイムゾーンを変更する

今のところ標準でサポートされているのはUTCとlocalの相互変換のみです。

ロケールに関して

ロケールに関してのライブラリは今のところ知りません。

まとめ

Rubyの場合は標準でTimeクラスがあるだけでなく標準添付でdate.rb、time.rbと言ったライブラリが充実しているため単純な処理であればこれで十分でしょう。

Railsで何か作る際にはActiveSupportの強力な拡張が便利です。単に日付計算したい際にもActiveSupportを使う方が便利かと思います。

RAAやGemsは探していないのでここを探せばもっと良いライブラリがあるかもしれません。