1. どんなことができるクラスか
次のコードペンのようなことができます。
See the Pen 日付比較 by hiratatomotaka (@hiratatomotaka) on CodePen.
- 2つの日時の間の差分を 年、月、週、日、秒単位で表すことができます。
- 2つの日時の間の差分 を「A年間 Bか月間 C日間 D時間 E分間 F秒間」というような形式で取得できます。
-
Date
オブジェクトとの間で 足し引き算も可能です。
2. 仕様
2-1. コンストラクタ
コンストラクタでは、2つの Date
オブジェクト dateA
, dateB
をこの順で受け取り、これをもとに、次のプライベートフィールドを作ります。
-
#newer_is_A
:dateA
がdateB
よりも新しい日付であることの真偽値 -
#newer
:dateA
,dateB
のうち、より新しいほうのDate
オブジェクト -
#older
:dateA
,dateB
のうち、より古いほうのDate
オブジェクト -
#answer
:dateA
からdateB
を引き算※した結果を表す オブジェクト。
negative
,year
,month
,day
,hour
,minute
,second
をキーとするオブジェクト。-
#answer.negative
: 引き算の結果が 0 より小さいか否か。
#newer_is_A
の否定となる。 -
#answer.year
: 引き算の絶対値における年情報。 -
#answer.month
: 月情報。0から11の間の整数となる。 -
#answer.day
: 日。0~30 -
#answer.hour
: 時。0~23 -
#answer.minute
: 分。0~59 -
#answer.second
: 秒。0~59
-
※ dateA
から dateB
への 引き算は次のルールで行います。
- 符号と絶対値は別の工程で求めます。
- 新しい日時から古い日時を引き算することで絶対値をまず求めます。
-
dataA
とdataB
の新旧関係に基づいて符号を決めます。
- 新しい日時から古い日時への引き算は、「時間の筆算」により求めます。
というのは、
unit
= (秒, 分, 時, 日, 月) および
next_unit
= (分, 時, 日, 月, 年) について、
この順で次の処理をし、最後に年情報を引き算することを意味します。- まず 新しい日時の
unit
(例えば 秒) から 古い日時のunit
を引き算します。 - その結果 (ここでは仮に
x
) により、処理は場合分けされます。-
x
が負でない場合、筆算の答えにおけるunit
としてx
を採用します。 -
x
が負であった場合、次の通りとします。- 新しい日時の
next_unit
(例えば 分) を 1だけ減算します。 - 新しい日時の
unit
をborrow
(例えば 60) だけ加算します。 - 再度
x
を求めると必ず 非負となりますので、これを筆算の答えにおけるunit
として採用します。
- 新しい日時の
-
- まず 新しい日時の
-
borrow
は、古い日時のnext_unit
を、unit
で表現した値とします。
例えば
新しい日時が2022/3/1 0:00:00
、
古い日時が2022/2/28 0:00:00
で、
next_unit
が 月、
unit
が 日
の場合を考えましょう。
x
は 1 - 28 = -27 なので負となります。
そのため、新しい日付の 月 を 1だけ減算して、
日 はborrow
だけ加算します。
borrow
は 古い日時の 1ヶ月 を 日 で表現したものとなります。
古い日時は 2022年2月... なので、 1ヶ月は 28 日となります。
よって、新しい日付は2022/(3-1)/(1+28) 0:00:00
つまり2022/2/29 0:00:00
とみなされます。
そして再度 日の引き算を行いますから、 筆算の答えにおける日 は 29 - 28 = 1 となります。
2-2. メソッド
2-2-1. get_as_year(mode="up")
dataA
から dataB
を引いた結果を年単位で取得するメソッドです。
#answer.year
は 整数ですが、 get_as_year
の返却値は小数部分を含む実数となります。
小数部分は、数え方によって2通りの値となる場合があります。
例えば、2022年3月21日11:23:02 と 2028年2月17日08:56:11 の時間差は、
数え方によって -5.9095...年となったり、 -5.9120...年となったりします。
この違いは、小数部分を計算する際、うるう日が含まれるか含まれないかによって生じます。
そこで、2通りの数え方のうちのどちらを採用するのかを、mode
引数で指示します。
この引数は "up"
, "down"
, "AtoB"
, "BtoA"
, "nonleap"
, "leap"
の6通りが許され、省略された場合や無効な値が代入された場合は"up"
が指定されたものとみなされます。
小数点以下は、次の方法で数えます。
mode="up"
の場合、 古い日付から数えて求めます。
mode="down"
の場合、新しい日付から数えて求めます。
mode="AtoB"
の場合、dateA
から数えて求めます。
mode="BtoA"
の場合、dateB
から数えて求めます。
mode="nonleap"
の場合、うるう年を無視して数えます。
mode="leap"
の場合、うるう年があるものとみなして数えます。
次の2例では、
A: 2022年3月21日11:23:02 と
B: 2028年2月17日08:56:11を比較する場合を考えます。
2-2-1-1. mode = "up"
の例
Bのほうが新しい日付であるため、mode="AtoB"
は、mode="up"
と同じ効果をもたらします。
- 古い日付 2022年3月21日11:23:02 を
older
,
新しい日付 2028年2月17日08:56:11 をnewer
とします。 - 整数部分は
#answer.year
から求められます。5 です。 -
older
に対して、年情報を 整数部分 だけ加算して得られるolder_dush
を考えます。
older_dush
は 2027年3月21日11:23:02 です。
(newer
とolder_dush
の時間差を、(older_dush
にとっての1年の長さ) で割ったものこそが、求めたい小数部分です) -
newer
とolder_dush
の時間差 を 秒 単位で表します。
今回は28762389
となります。 -
older_dush
にとっての1年の長さを 秒 単位で表します。
つまり、次のようにします。-
older_dush
に対して、年情報を 1 だけ加算して得られるolder_dush2
を考えます。
older_dush2
は 2028年3月21日11:23:02 です。 -
older_dush2
とolder_dush
の時間差 を秒 単位で表します。
今回は31622400
となります。(うるう日である2028年2月29日をまたぐので、日で表すと366日となっています。つまり、mode="leap"
と同様の結果となります)
-
- 小数部分は 28762389 / 31622400 = 0.9095... です。
- A - B が 負であることから、負号が必要です。
- 手順2, 6, 7 より、このメソッドが返却すべき値は -5.9095... となります。
2-2-1-2. mode = "down"
の例
Bのほうが新しい日付であるため、mode="BtoA"
は、mode="down"
と同じ効果をもたらします。
- 古い日付 2022年3月21日11:23:02 を
older
,
新しい日付 2028年2月17日08:56:11 をnewer
とします。 - 整数部分は
#answer.year
から求められます。5 です。 -
newer
に対して、年情報を 整数部分 だけ減算して得られるnewer_dush
を考えます。
newer_dush
は 2023年2月17日08:56:11 です。
(newer_dush
とolder
の時間差を、(newer_dush
にとっての1年の長さ) で割ったものこそが、求めたい小数部分です) -
newer_dush
とolder
の時間差 を 秒 単位で表します。
今回は28762389
となります。 -
newer_dush
にとっての1年の長さを 秒 単位で表します。
つまり、次のようにします。-
newer_dush
に対して、年情報を 1 だけ減算して得られるnewer_dush2
を考えます。
newer_dush2
は 2022年2月17日08:56:11 です。 -
newer_dush
とnewer_dush2
の時間差 を秒 単位で表します。
今回は31536000
となります。(うるう日をまたがないので、日で表すと365日となっています。つまり、mode="nonleap"
と同様の結果となります)
-
- 小数部分は 28762389 / 31536000 = 0.9120... です。
- A - B が 負であることから、負号が必要です。
- 手順2, 6, 7 より、このメソッドが返却すべき値は -5.9120... となります。
2-2-2. get_as_month(mode="up")
dataA
から dataB
を引いた結果を月単位で取得するメソッドです。
整数部分は #answer.year
の12 倍 に #answer.month
を加えて求めます。
get_as_year
メソッド同様、小数部分は数え方によって2通りに分かれますので、mode
引数で数え方を指定します。
このメソッドの mode
引数は "up"
, "down"
, "AtoB"
, "BtoA"
の4通りから選びます。
2-2-3. get_as_week()
dataA
から dataB
を引いた結果を週単位で取得するメソッドです。
引数は不要です。
2-2-4. get_as_day()
dataA
から dataB
を引いた結果を日単位で取得するメソッドです。
引数は不要です。
2-2-5. get_as_second()
dataA
から dataB
を引いた結果を秒単位で取得するメソッドです。
引数は不要です。
2-2-6. get_as_object()
dataA
から dataB
を引いた結果としてのプライベートフィールド #answer
のコピーを取得するメソッドです。
引数は不要です。
2-2-7. added_by(date)
date + (dateA - dateB)
というような日付を求めるメソッドです。
引数 date
は Date
オブジェクトです。
2-2-8. subted_by(date)
date - (dateA - dateB)
というような日付を求めるメソッドです。
引数 date
は Date
オブジェクトです。
3. 使い方
第1章に掲げたコードペンを参考にお使いください。