1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【JavaScript】DateDelta クラスを自作してみた

Posted at

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: dateAdateB よりも新しい日付であることの真偽値
  • #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 への 引き算は次のルールで行います。

  1. 符号と絶対値は別の工程で求めます。
    1. 新しい日時から古い日時を引き算することで絶対値をまず求めます。
    2. dataAdataBの新旧関係に基づいて符号を決めます。
  2. 新しい日時から古い日時への引き算は、「時間の筆算」により求めます。
    というのは、
    unit = (秒, 分, 時, 日, 月) および
    next_unit = (分, 時, 日, 月, 年) について、
    この順で次の処理をし、最後に年情報を引き算することを意味します。
    1. まず 新しい日時のunit (例えば 秒) から 古い日時のunit を引き算します。
    2. その結果 (ここでは仮にx) により、処理は場合分けされます。
      1. x が負でない場合、筆算の答えにおけるunit として x を採用します。
      2. x が負であった場合、次の通りとします。
        1. 新しい日時のnext_unit (例えば 分) を 1だけ減算します。
        2. 新しい日時のunitborrow (例えば 60) だけ加算します。
        3. 再度 x を求めると必ず 非負となりますので、これを筆算の答えにおける unit として採用します。
  3. 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" と同じ効果をもたらします。

  1. 古い日付 2022年3月21日11:23:02 を older,
    新しい日付 2028年2月17日08:56:11 を newer とします。
  2. 整数部分は #answer.year から求められます。5 です。
  3. older に対して、年情報を 整数部分 だけ加算して得られる older_dush を考えます。
    older_dush は 2027年3月21日11:23:02 です。
    (newerolder_dush の時間差を、(older_dushにとっての1年の長さ) で割ったものこそが、求めたい小数部分です)
  4. newerolder_dush の時間差 を 秒 単位で表します。
    今回は 28762389 となります。
  5. older_dush にとっての1年の長さを 秒 単位で表します。
    つまり、次のようにします。
    1. older_dush に対して、年情報を 1 だけ加算して得られる older_dush2 を考えます。
      older_dush2 は 2028年3月21日11:23:02 です。
    2. older_dush2older_dush の時間差 を秒 単位で表します。
      今回は 31622400 となります。(うるう日である2028年2月29日をまたぐので、日で表すと366日となっています。つまり、mode="leap" と同様の結果となります)
  6. 小数部分は 28762389 / 31622400 = 0.9095... です。
  7. A - B が 負であることから、負号が必要です。
  8. 手順2, 6, 7 より、このメソッドが返却すべき値は -5.9095... となります。

2-2-1-2. mode = "down" の例

Bのほうが新しい日付であるため、mode="BtoA" は、mode="down" と同じ効果をもたらします。

  1. 古い日付 2022年3月21日11:23:02 を older,
    新しい日付 2028年2月17日08:56:11 を newer とします。
  2. 整数部分は #answer.year から求められます。5 です。
  3. newer に対して、年情報を 整数部分 だけ減算して得られる newer_dush を考えます。
    newer_dush は 2023年2月17日08:56:11 です。
    (newer_dusholder の時間差を、(newer_dushにとっての1年の長さ) で割ったものこそが、求めたい小数部分です)
  4. newer_dusholder の時間差 を 秒 単位で表します。
    今回は 28762389 となります。
  5. newer_dush にとっての1年の長さを 秒 単位で表します。
    つまり、次のようにします。
    1. newer_dush に対して、年情報を 1 だけ減算して得られる newer_dush2 を考えます。
      newer_dush2 は 2022年2月17日08:56:11 です。
    2. newer_dushnewer_dush2 の時間差 を秒 単位で表します。
      今回は 31536000 となります。(うるう日をまたがないので、日で表すと365日となっています。つまり、mode="nonleap" と同様の結果となります)
  6. 小数部分は 28762389 / 31536000 = 0.9120... です。
  7. A - B が 負であることから、負号が必要です。
  8. 手順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) というような日付を求めるメソッドです。
引数 dateDate オブジェクトです。

2-2-8. subted_by(date)

date - (dateA - dateB) というような日付を求めるメソッドです。
引数 dateDate オブジェクトです。

3. 使い方

第1章に掲げたコードペンを参考にお使いください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?