質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

3回答

3393閲覧

2つの日付の差分で端数が発生する場合の月数(単位数)が取得できない

pegy

総合スコア245

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2021/11/26 16:41

編集2021/11/26 16:41

2つの日付の差分の月数を取得すべく以下のようなコードを作成しました。

Javascript

1function monthDiff(dateFrom, dateTo) { 2 return dateTo.getMonth() - dateFrom.getMonth() + 3 (12 * (dateTo.getFullYear() - dateFrom.getFullYear())) 4} 5console.log(monthDiff(new Date("2021-10-29"),new Date("2021-11-30")))/1
  1. ここで端数の日数が出る場合、1日でも繰上げて一か月(いわゆる月割の考え方)とカウントしたいのですが、どの容易に対応すれば良いでしょうか?

例えばmonthDiff(new Date("2021-10-29"),new Date("2021-12-1"))であれば、10月でも端数が出ていて、12月でも端数が出ているので得たい月数は"3"になります。また、monthDiff(new Date("2021-10-29"),new Date("2021-10-30"))であればどちらも同じ端数月のため得たい月数は1となります。

  1. また、同様に3か月バージョンも同じ理由で思いつかず悩んでいます。例えばmonthDiff(new Date("2021-8-29"),new Date("2021-12-1"))であれば8月9月10月で括って1単位で、残りの11月12月は括っても3か月未満ですが、端数単位を繰り上げるので1単位として合計2単位が得たい単位数になります。

getMonth()+1同士を引けば、1.の月数が得られそうで、そこから3で除するとともに剰余算であまりの有無を確認して+1すれば2.の単位数も得られそうだとも思ったのですが、年跨ぎがあるので、上手くロジックを組み立てられなくなってしまいました。

どなたかアドバイスを頂ければ幸いです。
宜しくお願い申し上げます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

後は、3で割って切り上げ

js

1function monthDiff (d0, d1) { 2 3 function a (dt, f = false) { 4 let [y, m, d] = ['getFullYear', 'getMonth', 'getDate'].map (fn=> dt[fn]()); 5 let M = y * 12 + m + d / 32; 6 return f ? Math.floor (M): Math.ceil (M); 7 } 8 9 let f = +d0 < +d1; 10 let r = a (d1, !f) - a (d0, f); 11 return r; 12} 13 14console.log (monthDiff(new Date("2021-10-29"), new Date("2021-12-1"))); 15console.log (monthDiff (new Date("2021-12-1"), new Date("2021-10-29")));

小石を重機で・・・

投稿2021/11/26 22:54

babu_babu_baboo

総合スコア616

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pegy

2021/11/27 12:23

コメントいただきありがとうございます。正直、初心者マークすぎてコード自体を解析中です・・・ function a (dt, f = false) { let [y, m, d] = ['getFullYear', 'getMonth', 'getDate'].map (fn=> dt[fn]()); let M = y * 12 + m + d / 32; return f ? Math.floor (M): Math.ceil (M); } 1. 引数に f= falseのような代入式を入れることができるんですね、、、+d0 < +d1がboolean(true)だったらこの引数がtrueとして扱われるのか・・・んんわからない。 2. map()は配列のそれぞれの操作としてわかるのですが let [y, m, d] = ['getFullYear', 'getMonth', 'getDate'].map (fn=> dt[fn]()); let M = y * 12 + m + d / 32; をすると、なぜそれぞれyear、month、dayが取得できるのか、そして/32の"32"がわからず。。 歯を食いしばって調べます!
pegy

2021/11/27 12:28

一番、謎であったmap()の中のアロー関数の意味が分かりました。 new Date("2011-11-1")["getFullYear"]()ということですね! dt.getFullYear()という記法しか存じ上げなかったので、オブジェクトの中のメソッドをブラケット+()でも呼び出すことができることを知りませんでした・・・ありがとうございます。
babu_babu_baboo

2021/11/27 12:39

d の範囲は1~31の範囲です。それを少数にする必要があります。その数より大きい数字で割らないと・・・ 日付の差が、負の場合は切り捨てと切り上げを逆にしないと・・・
pegy

2021/11/27 12:57

*一旦、簡便化するためにf=falseを無視して考えてみました。 function a (dt) { let [y, m, d] = ['getFullYear', 'getMonth', 'getDate'].map (fn=> dt[fn]()); let M = y * 12 + m + d / 32; return M; } var hoge = a(new Date("2021-11-11")); var piyo = a(new Date("2022-12-15")) console.log(hoge); console.log(piyo); console.log(piyo-hoge); で分解して考えてみたのですが、恐らくyearの繰上の影響を加味するためにy*12をしており(同じ年なら*12しなくても正しい結果が得られているので逆に推量しました)、dayの影響を無視するためにd/32をしていると考えましたが、なぜこの数式でそれが導かれるのかを少し考えています。
guest

0

ベストアンサー

どなたかアドバイスを頂ければ幸いです。

アドバイスといいますか、自分ならこういった、二つの日付や日時の差分を、何らかの単位(月や日、時間など)で算出する必要があるとき、標準のDateではなくDay.jsを使います。たとえば、ご質問のmonthDiff(dateFrom, dateTo) を以下のように書けます。

javascript

1// 2// monthDiff(dateFrom, dateTo) 3// 4// 引数 5// dateFrom: YYYY-M-D 形式の文字列 6// dateTo: YYYY-M-D 形式の文字列 7// 8// 返される値: 以下の要素を含む、長さ2の配列 9// 先頭要素: 月数 10// 第2要素: 3ヶ月単位数 11// 12const monthDiff = (dateFrom, dateTo) => { 13 const d1 = dayjs(dateFrom, 'YYYY-M-D').startOf('month'); 14 const d2 = dayjs(dateTo, 'YYYY-M-D').endOf('month').add(1, 'second'); 15 16 const n = d2.diff(d1, 'month'); 17 const m = Math.floor(n / 3) + (n % 3 > 0); 18 return [n, m]; 19}; 20

または、与えられたYYYY-M-D形式の文字列の-Dの部分を削除した文字列を、dayjs()に渡して、YYYY-Mの文字列としてパースしてオブジェクトを作ると、指定された月の月初(1日)の日付を表すものになることを利用して、以下のようにも書けます。

javascript

1const monthDiff = (dateFrom, dateTo) => { 2 [dateFrom, dateTo] = [dateFrom, dateTo].map(dateStr => 3 dayjs(dateStr.replace(/-\d{1,2}$/, ''), 'YYYY-M') 4 ); 5 const n = dateTo.diff(dateFrom, 'month') + 1; 6 const m = Math.floor(n / 3) + (n % 3 > 0); 7 return [n, m]; 8};

追記

上記のコードで、n から m を得る計算は、以下でもできます。

javascript

1const m = Math.floor((n + 2) / 3);

あるいは

javascript

1const m = Math.ceil(n / 3)

追記2

コメントから頂きました、

・・・得たい結果としては1(か月)+14日(剰余日数)となります。

を得るための関数 monthDiff_md(dateFrom, dateTo) を作ってみました。

・・・もっとdayjsでスマートに行けるのであれば、お知恵を頂きたくご返信させていただきました。

とのことでしたが、スマートなものになっているかは分かりませんが挙げておきます。

javascript

1const monthDiff_md = (dateFrom, dateTo) => { 2 // 引数で与えられた YYYY-M-D 形式の文字列からdayjsオブジェクトを作る 3 const d1 = dayjs(dateFrom, 'YYYY-M-D'); 4 const d2 = dayjs(dateTo,'YYYY-M-D'); 5 6 // d1 と d2 との差分を月単位で得る。その際に小数点以下も取得する。 7 const monthDiff = d2.diff(d1,'month', true); 8 9 // 上記で得た monthDiff の整数部分 n と小数部分 r を取得する。 10 const n = Math.floor(monthDiff); 11 const r = monthDiff - n; 12 13 // d1 に n月 および (n+1)月を加えた日付を作って、d3 および d4 とする。 14 const [d3, d4] = [n, n+1].map(x => d1.add(x, 'month')); 15 16 // d4 と d3 の日数差を計算して m とする。 17 const m = d4.diff(d3, 'day'); 18 19 // m に r を乗じた数を作る。この数の単位は日であり理屈上は整数だが、計算結果は整数にごく近い小数になるのでMath.roundする。 20 const k = Math.round(m * r) 21 22 // n と k を返す 23 return [n, k]; 24}

実行例:

javascript

1const [n, k] = monthDiff_md("2021-6-1","2021-7-15"); 2 3console.log([n, k]);

出力結果:

[ 1, 14 ]

この14日の算出方法を説明します。

javascript

1const date1 = dayjs('2019-01-25') 2date1.diff('2018-06-05', 'month', true) // 7.645161290322581

とあるとおり、diff の第三引数に true を渡すと、小数点以下ありの数値が返ってきます。この小数部分を日数に換算することを考えます。

  • ご例示の、dateFrom:"2021-6-1" と dateTo:"2021-7-15" であれば、d2.diff(d1,'month', true) は、私の手元の環境では

1.4516129032258065
という数を返しました。

  • これの小数部分r = 0.4516129032258065 を日数に換算するために、この 0.45・・・に何を掛ければよいかというと、上記のソースコードに出てくるd3d4 の日数差です。この差は月でいえば一ヶ月ですが、日数換算だとd3の月の月末が何日なのかによって変わります。また2月だったら年が閏年かも考慮しなければなりませんが、このへんを任せられるのがdayjsの強みですね。

  • d3d4 の日数差を m として得て、mに先の小数 r = 0.45・・・ を乗じると、欲しい剰余日数が出るはずですが、計算結果としては整数にごく近い小数になると思います。私の手元では、

14.000000000000002
となりました。これの小数を Math.round で切り捨て(または切り上げ)れば、得たい剰余日数の14が得られます。

投稿2021/11/26 17:43

編集2021/11/27 19:44
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pegy

2021/11/27 12:06 編集

コメントありがとうございます。 1. 毎日夜しかコードに触れない家庭環境でして、ご返信が遅くなりましたことをお許しください。 2. 前回の質問でもdayjsのことをご教示いただいたので、背後ではdayjsで試しているのですが、質問するときに市民権を得ているライブラリやフレームワークの感覚を存じ上げあず、PureJsベースでの質問を差し上げておりました。その点もお詫び申し上げます。(フル活用させていただいております!) さて、誠に申し訳ない点が、実際実装してみるとわかったことで必ずstartが月初であり、endが途中の日(つまり端数日がでる可能性がある)ということと、以外と月割りにすると求めようとしている結果に大きな誤差が生じるため、端数の日数を加味しようという顛末に至っております。 const monthDiff_m = (dateFrom, dateTo) => { const d1 = dayjs(dateFrom, 'YYYY-M-D'); const d2 = dayjs(dateTo,'YYYY-M-D') return d2.diff(d1,'month') } console.log(monthDiff_m("2021-6-1","2021/7/15"))//1 const monthDiff_d = (dateFrom, dateTo) => { const d1 = dayjs(dateFrom, 'YYYY-M-D'); const d2 = dayjs(dateTo,'YYYY-M-D') return d2.diff(d1,'month') } console.log(monthDiff_d("2021-6-1","2021/7/16"))//45 とmonthtとdayのdiffは容易にdayjsで理解しているのですが、monthのdiffを取った場合、感覚的に剰余の日数を取ることはdayjs上可能なのでしょうか? つまり上の例で申し上げるとmonthDiff_m("2021-6-1","2021/7/15")では1とでますが、得たい結果としては1(か月)+14日(剰余日数)となります。 もちろん、 monthDiff_m()で0以下であれば、monthDiff_d()を実行して、日数を取得、1より大きい monthDiff_m()で1より大きい場合には、月数nを取得してadd(n,"month")してから追加後の日付とendoをmonthDiff_d()で端数を取りに行くという方法があるのですが、もっとdayjsでスマートに行けるのであれば、お知恵を頂きたくご返信させていただきました。 特にこのやり方で3か月1単位パターンまで加味すると、一層煩雑なコードになるかと思い検討しておりました。重ねて失礼いたします。
退会済みユーザー

退会済みユーザー

2021/11/27 14:34

コメントありがとうございます。 > 1. 毎日夜しかコードに触れない家庭環境でして、ご返信が遅くなりましたことをお許しください。 ご丁寧にありがとうございます。 > 2. 前回の質問でもdayjsのことをご教示いただいたので・・・(フル活用させていただいております!) フル活用されているとのことで、よかったです! 慣れてくれば、日時の差分だったり足し算引き算して込み入ったことをやる必要があるときに、標準のDateだったらウンザリしていたところを、難なく切り抜けられるようになるかと思います。 さて懸案の > ・・・得たい結果としては1(か月)+14日(剰余日数)となります。 との件ですが、解決策となる案を、回答のほうに追記2として書きましたので、検証いただければと思います。 > 特にこのやり方で3か月1単位パターンまで加味すると、 との件は、また課題が具体的になれば質問いただければと思います。
pegy

2021/11/27 15:12

コメントありがとうございます。とてもよくわかりました! まずdiff()のリファレンスを見ていて、第三引数の存在を見落としておりました、これがまさに欲していた部分になります。 さて、初めにご解説を読んで、なんでその後わざわざd3、d4を置いてさらに計算を重ねているんだろうと悩んでいたのですが、なるほど、rは1か月に対する割合で1か月の日数は月によって相対的に変化するので、そこを計算しに行っているのですね。文中のうるう年等も考慮されているというところもこの理解で腹落ちを致しました。 これを足掛かりに3か月1単位のパターンも自力でいけそうですので、一旦頑張ります。 また、お力添えを頂きましたことに深く御礼申し上げます。
退会済みユーザー

退会済みユーザー

2021/11/27 15:23

解決されたようでよかったです。上記の d3 と d4 の日数差に diff の小数部分を掛けると、欲しい14日が出てくるあたり、Dayjs、なかなかよく出来るなあと当方も再確認できた次第です。ありがとうございました。
guest

0

1.については、pegyさんのコードでほぼ合っています。変更点は最後に+1するところです。例えば同年3月~7月の月数は、7 - 3 + 1 = 5(ヵ月)と求めます。

年を跨ぐ場合も、例えば2020年11月から2021年3月までの月数は、

12 * (2021 - 2020) + (3 - 11) + 1 = 12 * 1 - 8 + 1 = 4 + 1 = 5

のように正しく求められます。

JavaScript

1function monthDiff(dateFrom, dateTo) { 2 return 12 * (dateTo.getFullYear() - dateFrom.getFullYear()) + dateTo.getMonth() - dateFrom.getMonth() + 1; 3} 4 5console.log(monthDiff(new Date("2021-10-29"), new Date("2021-12-31"))); //出力: 3 6console.log(monthDiff(new Date("2020-11-11"), new Date("2021-3-3"))); //出力: 5 7

2.については、月数を3で割った商に、余りが0より大きい場合は1を足す方法と、月数に2を足し、3で割った商を求める方法があります。後者のコードのほうがシンプルです。

JavaScript

1var x = 5; 2 3//方法1 4var a = Math.floor(x / 3); 5if (x % 3 > 0) { 6 a += 1; 7} 8console.log(a); //出力: 2 9 10//方法2 11var b = Math.floor((x + 2) / 3); 12console.log(b); //出力: 2 13

投稿2021/11/26 17:40

編集2021/11/26 17:43
luuguas

総合スコア501

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pegy

2021/11/27 12:11

前回に引き続き、ご回答を頂きありがとうございました。 月数の取得の論理にfloorの使い方を含めて理解し実装することができました。また、kilesa様へのコメントにも記載させていただきましたが、いずれに方法においても、端数の日数を1月に繰り上げて計算する方法で実装はできたのですが、この方法で最終的に得ようとする解が大きな誤差が生じてしまうため、端数の日数を拾う方法で再検討しております。 実際にやっているのは借入金利息の計算のようなものなのですが、さすがに1日を1か月とみなしたり、3か月1単位とみなすと利息の計算結果に想定よりも大きな誤差が出てしまったというところで、除した結果の日数を加味する論理を再構築するに至っております。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問