RubyのTime・Dateクラス徹底解説!時刻と日付を自在に操るテクニック
Timeは、Rubyにおいて特定の時点(日付と時刻)を表現するためのクラスです。
この記事を読み終える頃には、Rubyで時刻や日付を自由に扱うために必要な知識をすべて身につけられるでしょう。
この記事で学べること
- 文字列をTimeオブジェクトに変換する方法
- 時刻を構成要素(日・時・ミリ秒など)に分解する方法
- Rubyにおける
Date・DateTime・Timeクラスの使い分け - 任意の形式で時刻をフォーマットする方法
- 2つの
Timeオブジェクトの差分を求める方法
それでは始めましょう!
RubyのTimeクラスとは
RubyのTimeクラスを使うと、日付と時刻をひとつのオブジェクトとして表現できます。
日付は次の3つの要素で構成されます。
- 日
- 月
- 年
時刻は以下の要素から成ります。
- 時
- 分
- 秒
Timeクラスはこれらの情報を「エポック(Unix時間)からの経過秒数」として内部的に保持しています。
Timeオブジェクトの生成方法
Timeオブジェクトを初期化するには、主に以下の方法があります。
Time.now— 現在時刻を表すオブジェクトを取得するatメソッドとUnixタイムスタンプを使って生成するTime.newに年月日を数値で渡して生成する(形式:年/月/日)
実際のコード例:
Time.now # 2018-10-19 15:43:20 +0200 Time.new(2018, 1, 1) # 2018-01-01 00:00:00 +0100 Time.at(15000000000) # 2017-07-14 04:40:00 +0200
時刻の構成要素を取り出す
Timeオブジェクトに対して、任意の構成要素を問い合わせることができます。たとえば、日・月・時をそれぞれ取得できます。
t = Time.now puts t.day # 日 puts t.month # 月 puts t.hour # 時
さらに、「その日付が特定の曜日に該当するか」を確認することも可能です。
たとえば「この日は日曜日か?」といった判定ができます。これらは述語メソッドと呼ばれ、trueまたはfalseを返します。
例:
t = Time.now puts t.monday? # 月曜日? puts t.sunday? # 日曜日? puts t.friday? # 金曜日?
ぜひ試してみてください。
タイムゾーンの扱い方
Timeオブジェクトにはタイムゾーン情報が関連付けられています。
zoneメソッドを使うと、現在設定されているタイムゾーンの略称を確認できます。
タイムゾーンオフセット(UTCとの差)が必要な場合は、utc_offsetメソッドを使用します。このメソッドの戻り値は秒単位なので、3600で割れば時間単位に変換できます。
例:
t = Time.now t.zone # "CET" t.utc_offset / 3600 # 1
UTCでの現在時刻も簡単に取得できます。
Time.now.utc
Rubyの時刻フォーマット(strftime)
Rubyはデフォルトで特定の形式でTimeオブジェクトを出力しますが、用途に応じて別の形式にカスタマイズしたいことも多いはずです。
朗報です。strftimeというメソッドを使えば、ほぼどんな形式にも対応できます。「strftime」は「format time(時刻を整形する)」を意味しています。
仕組みとしては、書式指定子を含んだ文字列を渡し、その指定子が実際の値に置き換えられるというものです。printfメソッドを使ったことがあれば、イメージは非常に近いでしょう。
具体例を見てみましょう:
time = Time.new
time.strftime("%d/%m/%Y") # "05/12/2015"
time.strftime("%k:%M") # "17:48"
time.strftime("%I:%M %p") # "11:04 PM"
time.strftime("Today is %A") # "Today is Sunday"
time.strftime("%d of %B, %Y") # "21 of December, 2015"
time.strftime("Unix time is %s") # "Unix time is 1449336630"
ご覧のとおり、このメソッドは非常に柔軟です。日付を除いた時刻だけを取得したり、年・日・月名を含んだ見栄えの良い日付を作成したりすることもできます。
strftime書式指定子一覧表
| 書式 | 説明 |
|---|---|
| %d | 日(01〜31) |
| %m | 月(01〜12)。1桁表示したい場合は %-m を使用(1〜12) |
| %k | 時(0〜23) |
| %M | 分 |
| %S | 秒(00〜60) |
| %I | 時(1〜12) |
| %p | 午前/午後(AM/PM) |
| %Y | 年 |
| %A | 曜日(名称) |
| %B | 月(名称) |
利用可能な書式の詳細については、以下のリンクを参照してください。
- https://strftime.net/
- https://devdocs.io/ruby/time#method-i-strftime
タイムスタンプの生成
タイムゾーンに依存しないタイムスタンプを生成すれば、イベントの日時を記録するのに便利です。
やり方はこちら:
Time.now.to_i # 1549054305
このタイムスタンプは、UTC(協定世界時)基準の経過秒数を表す数値です。
時刻の加算と減算(差分の計算)
現在時刻ではなく、未来または過去の時刻が欲しい場合もありますよね。
実は、Timeオブジェクト同士で足し算ができるのです!
Timeの内部表現は秒数であることを思い出してください。つまり、次のように書けます。
# 10秒後の時刻を取得 time = Time.new + 10
この例では、現在時刻から10秒後を指すTimeオブジェクトが得られます。その後、その時刻がすでに過ぎたかどうかをチェックできます。
Time.new > time
「昨日の日付」のような値を取得したい場合は、1日の秒数を計算する必要があります。
計算方法:
# 秒 × 分 × 時間 60 * 60 * 24 # 86400
この数値を使えば、現在の日付から引くことで昨日の日付を得られます。
Time.now - 86400
Railsを使っている場合は、さらにシンプルに書けます。
Time.now - 1.day
Dateクラスの使い方
Dateクラスには「時」「分」「秒」という概念がなく、すべての日付情報を「日」単位で内部的に管理します。
Dateクラスを使用するには、事前にrequire 'date'が必要です。
現在の日付はDate.todayで取得できます。
注意点として、Timeクラスと異なり、Date.newはtodayのエイリアスではありません。
Date.today # 現在の日付 Date.new # 負の日付を返す
Dateの演算はTimeクラスと似ていますが、違いは加算する単位が「秒」ではなく「日」である点です。
Date.today + 1 # 1日追加
Dateは時・分・秒に関する情報を持たないため、それらが不要な場合にのみ使用しましょう。
日付と時刻のパース(文字列からの変換)
Date.parseメソッドは、日付らしき文字列なら何でも解析しようとします。
内部では入力フォーマットを推測するシンプルなヒューリスティックアルゴリズムが使われており、場合によっては意図しない結果になることもあります。
例:
Date.parse("10/10/2010") # -> 2010-10-10
Date.parse("September 3") # -> 2015-09-03
Date.parse("May I have a cup of coffee, please?") # -> 1 of May
より厳密な解析が必要な場合は、Date.iso8601メソッドを使用しましょう。
iso8601形式の日付は次のような形式です。
year-month-day
無効な入力が与えられた場合、ArgumentError例外が発生します。
例:
Date.iso8601("2018-10-01")
独自の入力形式を指定したい場合は、Date.strptimeメソッドに書式指定子を組み合わせて使います。ここで使う書式指定子は、strftimeと同じものです。
例:
Date.strptime("3 of September", "%d of %B") # 2015-09-03
文字列をTimeオブジェクトに変換する
時刻の解析にはTimeクラスも使えます。
言い換えると、「2018-01-01 00:00」のような文字列をTimeオブジェクトに変換する方法です!
例を見てみましょう:
require 'time'
Time.parse("September 20 18:00")
入力文字列のフォーマットを明示的に指定したい場合は、strptimeメソッドを使用します。
このように書きます:
require 'time'
Time.strptime("1 of December 2017", "%d of %B %Y")
Time.parseとDate.parseの唯一の違いは、返されるオブジェクトの種類(TimeまたはDate)という点です。
Dateクラスの便利な定数
Dateクラスには、役立つ定数がいくつか用意されています。
たとえば、年間の月名を格納した配列や、曜日名を格納した配列があります。
月の配列はインデックス1から始まるため、「月番号 → 月名」の直接的なマッピングとして利用できます。
曜日の配列は日曜始まりですが、rotateメソッドを使えば月曜始まりに変更できます。
Date::MONTHNAMES # (インデックス0 = nil) Date::DAYNAMES.rotate(1)
DateTimeクラスについて
DateTimeクラスはDateのサブクラスで、日付に加えて秒の情報も保持できます。
例:
require 'date' DateTime.superclass # Date DateTime.now # DateTime: 2018-10-15T16:06:39+02:00
TimeとDateTimeはどちらも同じ仕事をこなせますが、最大の違いは、TimeがC言語で実装されているため高速だという点です。
パフォーマンス比較:
Comparison:
Time: 2644596.6 i/s
DateTime: 231634.8 i/s - 11.42x slower
ActiveSupport – Railsの時刻・日付拡張メソッド
Railsを使ったことがあるなら、3.days.agoのような記述をご存じかもしれません。
これらのメソッドは純粋なRubyには存在せず、RailsのActiveSupportコンポーネントによって追加されたものです。
いくつか例を挙げます。注目すべきは、これらのメソッドがTimeやDateではなく、独自のActiveSupportクラスを返すという点です。
1.hour.to_i # 3600 1.day # ActiveSupport::Duration 3.days.ago # ActiveSupport::TimeWithZone
これらを使って時刻計算を行えば、「明日の日付」なども簡単に取得できます。
Time.now + 1.day
Rails限定のその他の時刻メソッド:
date = Time.current date.change(hour: 20) date.at_beginning_of_day
フォーマット関連では次のように使えます。
date = Date.today date.to_formatted_s(:short) # "16 Jul" date.to_formatted_s(:long) # "July 16, 2018"
これらのメソッドのコード自体はとてもシンプルなので、一度ソースコードを読んでみる価値があります。TimeWithZoneのソースコードも見てみると勉強になりますよ。
まとめ
この記事では、RubyのTimeクラスとDateクラスについて学びました。時刻のパースやフォーマットの方法、そしてActiveSupplyによる時刻拡張の活用法まで解説しました。
最後までお読みいただきありがとうございました!
-
Rubyのエイリアス(別名定義)完全ガイド:aliasキーワードとalias_methodの違い
Rubyでは、既存のメソッドに別名(エイリアス)を付ける方法が2つあります。 alias(キーワード) alias_method(メソッド) どちらも同じ目的で使えますが、挙動が微妙に異なるため、初心者にとって混乱しやすいトピックです。 本記事では、両者の違いを詳しく掘り下げ、しっかりと理解できるように解説していきます。 aliasキーワードとは まずはaliasから見ていきましょう。aliasはRubyのキーワードの一つです(ifやdef、classなどと同じ扱いです)。 基本的な書き方は以下の通りです。 alias print_something puts print_someth
-
RubyのStructとOpenStructの使い方を徹底解説!値オブジェクトを簡単に作る方法
Rubyには、関連する属性をまとめて保持する「値オブジェクト」を手軽に作れるStructという組み込みクラスが用意されています。本記事では、Structの基本的な使い方から注意点、そしてOpenStructとの違いまで詳しく解説します。 そもそもStructとは何か? StructはRubyに組み込まれたクラスで、新しいクラスを動的に生成し、値オブジェクト(Value Object)を作成するために使われます。値オブジェクトとは、関連性のある複数の属性をひとまとめにして扱うためのオブジェクトです。 具体例を挙げてみましょう。 2つの座標(xとy)を持つPointというデータを表したい場合、この