※ 表示が正しくないと思う場合は、JavaScript を有効にしてください。
年 月 日まで
その日まで、あと何日かな?
今日からかぞえて、指定の年月日までの日数を計算します。
日付を選択したら、「計算する」ボタンをタップ! あと何日あるか、すぐにわかります。
誕生日まで、定期テストまで、資格試験まで、東京オリンピック開幕式まで、
あるいはもっと先のいつかまで、あと何日か知って、毎日を正しく過ごそう!
※ 過去の日付へは計算できません。
現時点でのテスト済みブラウザは、
です。
動作の確認は、Windows デスクトップアプリ 「DaysUntil」で行っています。 検証は行っておりますが、ご利用に関しては、 自己責任でお願いします。 計算結果に誤りがあったとしても、当方では責任を負いかねます。
スマホやタブレットで開く場合は、
https://www.straightapps.com/web/js-days-until.html
)
投稿 September 29, 2020
ここから先は、上記、指定の日付までの日数計算プログラム開発を行った際に調べたりした JavaScript コードについての情報です。
作者背景については、「素因数分解トレーニング」のページの 「ここから先は開発情報です」をご覧ください。
まずは、HTML 部です。
<p id="today"></p> <span id="year"></span> 年 <!-- 年コンボボックス --> <span id="month"></span> 月 <!-- 月コンボボックス --> <span id="day"></span> 日まで <!-- 日コンボボックス --> <p id="days"></p> <button onclick="calc()" title="計算する"> 計算する </button> <script type="text/javascript" src="js/daysuntil.js"></script>
p id="today" の部分は、 「今日は 2020 年 9 月 29 日です。」のような表示になる場所です。
続く span id="year" など3つの部分は、 コンボボックス(ドロップダウンリスト)が表示される部分です。
そして p id="days" には、 計算結果「あと 0 日です。」のような表示になるところです。
「計算する」ボタンは、押されたら JavaScript の calc 関数を呼び出すようになっています。
表示を更新する部分がすべて定義されたので、JavaScript を読み込んで、初期化等を実行しています。
以降のセクションで、それぞれ詳細を確認していきます。
なお、ご参考までに、JavaScript ソースコードそのものを、拡張子 js を拡張子 txt に変えて、次のリンクより開けるようにしてあります。
daysuntil-init.txt
daysuntil.txt
※ 作成時はタブを4文字としていますので、環境によってはタブが8文字のため、
コメント等がずれて見えるかも知れません。
※ 念のため書き添えますが、このソースコードをこのまま転載・公開することはご遠慮ください。
なお、本サイトのご利用に際しては、必ずプライバシーポリシー(免責事項等)をご参照ください。
投稿 September 30, 2020
daysuntil-init.js は、 <head> 〜 </head> タグ内でロードしていますが、 この場所で読み込む場合は、ページ本体のデータ(body 部)が読み込まれる前に、ロードされるようです。
なので、この中で、例えばあとで定義されている p id="days" にアクセスしようとしても、アクセスできません。
var date = new Date(); // 日付 var selYear = 0, selMonth = 0, selDay = 0; // 選択中の年・月・日
daysuntil-init.js にあるのは上記のものだけです。
変数 date に、新しく作成した
ほぼすべての機能が実装されている daysuntil.js は、 body タグ内でロードされています。 これは、HTML の body を先頭から読み込んでいき、 そのコードに到達したときに読み込まれる、というものです。
<script type="text/javascript" src="js/daysuntil.js"></script>
ここでは、これより前で定義された p id="today" や span id="year" にアクセスできます。
なお、JavaScript のコードは、head や body タグ内に直接記述することも可能です。 機能テストには有効かもしれませんが、ごちゃごちゃになるので、おすすめできる方法ではありません。 また、HTML タグの onClick などに直接コードを書くこともできるようですが、小規模でない限りは、混乱のもとになるかと思います。
<script type="text/javascript"> <!-- <!-- JavaScript コードを記述 --> // --> </script> <noscript> <!-- JavaScript が無効の場合の注意書きなどを記述 --> </noscript>
投稿 September 30, 2020
body が読み進まれていき、script の行まで到達すると、src で指定されたファイルが読み込まれます。
<script type="text/javascript" src="js/daysuntil.js"></script>
daysuntil.js は、 p id="today" や span id="year" にアクセスしますので、 この script の行は、それらよりあとに記述されていなくてはなりません。 一般的には </body> の直前でいいようですが、 このページでは、読みにくくならないように、 必要なすべての HTML 要素の定義が終わった直後、 つまり button の定義の直後に置いています。
なお、ただ単純に、ブラウザがその行を解析するときに読む、というだけのようですから、 script 行が複数あれば、それぞれそのタイミングで読み込まれるようです。
daysuntil.js の先頭部分は、次のような流れになっています。
まずは、本日の日付を表示しています。
var elem; var str, strTmp; // 本日の日付を表示します。 elem = document.getElementById("today"); str = "今日は " + date.getFullYear() + " 年 " + (date.getMonth()+1) + " 月 " + date.getDate() + " 日です。"; elem.innerHTML = str;
p id="today" の部分を、innerHTML を設定することにより、 書き換えています。 innerHTML の利用について、詳しくは、「【基礎】HTML 要素のテキストの書き換え」 に書いています。
daysuntil-init.js で作成している、Date クラスのオブジェクト date の関数を呼び出しています。 getFullYear() 関数は、西暦年を返します。 getMonth() 関数は、月を、0〜11で返します。 なので、1〜12月にするためには、+1しなくてはいけません。 getDate() 関数は、日を返します。
JavaScript は型の概念が薄いので、文字列として連結し、いったん str に入れています。 それを innerHTML にセットする手順にしていますが、 いったん str に入れなくても、直接 innerHTML に入れられるのではないかと思います。 コードは読みにくくなりますから、直接じゃないほうがいいでしょう。
また、月を連結している部分、 「(date.getMonth()+1) + " 月 "」のようにカッコでくくられています。 もし「date.getMonth() + 1 + " 月 "」にすると、 例えば9月なら、getMonth() が 8 を返しますが、 出力は「81 月 」になってしまいます。 つまり、"8" + "1" + " 月 " と解釈されています。 カッコで優先順位を与えれば、解決されます。
続けて、年・月・日コンボボックス(ドロップダウンリスト)を作成しています。
// 年コンボボックスを作成します。 setYearCombo(); // 月コンボボックスを作成します。 setMonthCombo(); // 日コンボボックスを作成します。 setDayCombo(true);
これらの関数については、あとで解説します。
最後に、p id="days" の部分の初期表示を設定しています。
elem = document.getElementById("days"); elem.innerHTML = 'あと 0 日です。';
このあとは関数の定義しかありませんので、 この daysuntil.js の実行は終わりで、続きの HTML が読み込まれていくことになります。
「素因数分解トレーニング」では、 描画のために、意味なく main ループを定義し、 addEventListener 関数で、ページ読み込み完了後のスタートさせていましたが、 このプログラムでは、ループさせるコードはありません。
投稿 September 30, 2020
<span id="year"></span> の部分に、 「年」を表すコンボボックス(ドロップダウンリスト)を作成し、表示しています。 なお、「コンボボックス」は Windows PC での呼び方で、 クリックすると表示される項目リストから1つ選ぶ、というものです。
daysuntil.js が読み込まれたときに呼び出される、 setYearCombo 関数 で実行されています。
function setYearCombo() { elem = document.getElementById("year");
ここにセットしたい HTML 文字列は、次のようなものです。
<select name="year" id="to-year" onChange="changeYear(this.value)"> <option value="2020"> 2020 </option> <option value="2021"> 2021 </option> <!-- 同様の部分は省略 --> </select>
すなわち、HTML の select タグで、ドロップダウンリストを作成します。 リストになる項目は、option タグで定義できます。 好きな個数だけ option タグを定義したあと、select タグを閉じれば OK です。
select タグにある onChange は、 選択が変更されたときに実行される関数を定義するものです。 daysuntil.js で定義している、 changeYear 関数 に、 選択された option の value で指定している値を、引数として渡すということになります。
決まった値だけから選択するだけなら(例えば月は、いつでも 1 〜 12 月しかないので)、 HTML に直接 select/option を書けばよいのですが、 年には、「今日」の年から、この先 10 年分を表示したかったので、JavaScript で出力しています。 つまり、2020 年なら 2030 年まで選択可能となり、2021 年なら 2021 年から 2031 年までが選択可能となります。
それをコードにし、innerHTML に設定するまでが、下記の部分です。
str = '<select name="year" id="to-year" onChange="changeYear(this.value)">'; // select 内で size="3" 指定すると、3要素を同時表示するようになる var year = date.getFullYear(); for (var i = 0; i <= 10; i ++){ strTmp = ('<option value="' + (year+i) + '"> ' + (year+i) + ' </option>'); str += strTmp; } str += '</select>'; elem.innerHTML = str;
最後に、計算時に参照するため、選ばれた値を selYear に設定しています。 変数 selYear は、daysuntil-init.js で定義されています。
// 選択中の年を記録
selYear = year;
}
投稿 October 1, 2020
「月」のドロップダウンリストも、「年」と同じように、 select タグと option タグを用意し、 span id="month" の innerHTML を書き換えています。
選択が変更されたときに呼び出される関数は、changeMonth( this.value ) です。
「年」よりは「月」のほうが本来簡単なのですが、 1桁と2桁の数字を単純に出力すると左寄せになってしまいますので、 見た目向上のため、1桁のときは数字の前にスペースを入れて、調整しています。 また、初期状態で「今月」が選ばれるようにしています。
まずは関数の定義と、span id="month" を取得しています。
function setMonthCombo() { elem = document.getElementById("month");
変数 str に、select タグを用意します。 選択が変更されたときに呼び出される onChange には、 changeMonth 関数を指定しています。 引数に this.value を指定していますので、 option タグの value で指定された値が渡されることになります。
str = '<select id="to-month" onChange="changeMonth(this.value)">';
今見直すと、elem も str も、定義しないで使っています。 もちろん本来は var を付けるべきですが、これでも大丈夫なところが・・・怖いと言えます。
var month = date.getMonth() + 1; for (var i = 1; i <= 12; i ++){ if (i == month){ if ( i < 10 ){ strTmp = ( '<option value="' + i + '" selected> ' + i + ' </option>' ); } else{ strTmp = ( '<option value="' + i + '" selected> ' + i + ' </option>' ); } } else if ( i < 10 ){ strTmp = ( '<option value="' + i + '"> ' + i + ' </option>' ); } else{ strTmp = ( '<option value="' + i + '"> ' + i + ' </option>' ); } str += strTmp; }
変数 month には、daysuntil-init.js で定義し、 本日の日付データが入っているオブジェクト date を用いて、今月を取得しています。 getMonth 関数が 0 〜 11 を返すため、1を足して、1 〜 12 に直しています。
それぞれの月に option タグを作ります。 value は月そのものの値とし、1桁の場合は数字の前にスペースを入れています。 また、設定する数値が「今月」と一致するなら、option タグに selected を付けて、 初期状態でそのオプションが選ばれた状態になるようにしています。 いずれもいったん strTmp に option タグ全体をセットしてから、 str に追加しています。
str += '</select>'; elem.innerHTML = str; // 選択中の月を記録 selMonth = month; }
最後に select タグを閉じ、innerHTML で書き換えています。 selMonth には、選択されている値をセットします。 初期状態では、date から取得した month が、選択状態になっています。
投稿 October 1, 2020
「日」ドロップダウンリストも、 select タグを用意し、選択肢を option タグで定義するところは同じです。 初期状態で選択される値には、selected を追加しておきます。
「日」で面倒なのは、「月」が変更されたとき、選べない日を表示しないよう、更新する処理です。 例えば 10 月が選ばれているなら 31 日まで表示しますが、 9 月が選ばれたときには 30 日までに書き換えます。 このとき、例えば 15 日が選択されていたなら、「月」が変更されても 15 日が選択された状態としたいです。 また、10 月 31 日が選ばれているとき、9 月が選択されたら、9 月には 31 日がないので、 自動的に 30 日を選択しなおすほうが親切です。 さらに、2月の場合、うるう年があるので、「年」が変更されたときも気にする必要があります。
setDayCombo 関数は、引数 bToday を得ています。 true が渡された場合は起動時の初期化を行います。 この場合は、選択される日付が「今日」になります。 false が渡された場合は、選ばれている日付をキープします。
function setDayCombo(bToday) { elem = document.getElementById("day"); str = '<select id="to-day" onChange="changeDay(this.value)">';
変数 str には、select タグを設定しています。 選択が変更されたときは、onChange で指定している changeDay 関数 が呼び出されます。
var day = date.getDate(); if (bToday == false){ day = selDay; }
変数 day に、daysuntil-init.js で定義した本日のデータ、 date を使用して、日付を取得しています。 引数 bToday で false が渡された場合、すなわち初期化ではない場合、 day を今選択されている日 selDay で上書きし、 日がキープされるようにします。
var eom = new Date( selYear, selMonth, 0 ).getDate(); // 選択中年月の End of Month if (day > eom){ day = eom; }
変数
以下、「月」と同じように、1桁の場合は先頭にスペースを入れながら、 day と一致する選択肢に selected を入れる、 というコードになっています。 「コード詳細を確認」を展開して、確認していただけます。
投稿 October 1, 2020
select タグの onChange に JavaScript の関数を記述すると、 選択が変更されたときに、その関数が呼び出されます。
「年」ドロップダウンリストの場合、 select onChange="changeYear(this.value) のように書いていますので、 選択が変更されたとき、changeYear 関数が呼び出されます。 引数は、option の value に書かれた値で、ここでは 2020 や 2021 のようになります。 仮引数名 selectedYear で受け取ります。
function changeYear(selectedYear) { // うるう年に当たる場合は、2月の日数が変わります。 selYear = selectedYear; // 2月選択中以外は、何もする必要はありません。 if (selMonth != 2){ return; } // うるう年かもしれないので、日コンボを再設定します。 // 2月に29日がある、or 2月29日選択中だが無効 setDayCombo(false); }
引数で受けた、新しく選択された年 selectedYear を、現在の選択として selYear にコピーしています。
もし今2月が選ばれている場合、うるう年のため、日数が変わるかも知れません。 2月以外の場合は、そこで関数を終了します。
2月の場合、「日」ドロップダウンリストを再初期化するため、setDayCombo 関数を呼び出します。 引数を false にしていますので、できるだけ今選択されている日がキープされます。
同様に、「月」ドロップダウンリストの選択が変更された場合は、 次の changeMonth 関数が呼び出されます。
function changeMonth(selectedMonth) { selMonth = selectedMonth; // 日コンボを再設定します。 setDayCombo(false); }
引数で受けた、新しく選択された月 selectedMonth を、現在の選択として selMonth にコピーしています。
月が変わると日数も変わる可能性がありますので、setDayCombo 関数を呼び出しています。 引数を false にしていますので、できるだけ今選択されている日がキープされます。
「日」ドロップダウンリストは、簡単です。
function changeDay(selectedDay) { selDay = selectedDay; }
引数で受けた、新しく選択された selectedDay を、現在の選択として selDay にコピーしています。
これにより何か書き換える必要はありませんので、これだけです。
投稿 October 2, 2020
「計算する」ボタンは、HTML で button onclick="calc()" と定義されていますので、 押されたときに JavaScript の calc 関数が呼び出されます。
function calc() { elem = document.getElementById("days"); nDiff = getDaysUntil(); elem.innerHTML = 'あと ' + nDiff + ' 日です。'; }
日数を計算する処理は下記 getDaysUntil 関数が実行していて、 返された値 nDiff を使って、 p id="days" の部分の innerHTML を書き換えています。
残り日数の計算関数、本体はこの getDaysUntil です。
function getDaysUntil() { var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); var eom; var nDiff = 0; // あと何日か
変数 year, month, day にそれぞれ、「今日」の年、月、日を設定しています。 また、一時的に使用する eom を定義し、 合計の日数を計算して戻り値とする nDiff を定義し、初期化しています。
if (year == selYear && month == selMonth){ nDiff = parseInt(selDay) - parseInt(day); return nDiff; }
目的の年月と「今日」の年月を比較して、 年月とも同じ、つまり今月内の日付の違いのみなら、 日を引き算すればよいので、簡単です。
parseInt 関数は、引数を明示的に整数値として扱うためのものです。 C/C++ で言うところの「int にキャスト」のイメージだと思います。 引き算の場合はなくてもいいのかもしれませんが、 足し算の場合は、型は最初の要素に合わせられるようですから、 数値の足し算のつもりでも、文字列の連結になってしまう可能性があります。 特に数字が入った文字列が、計算されないで連結されると、かなり深い問題が起きそうです。
if (year == selYear){ // 今月の残り日数を計算します。 eom = new Date( year, month, 0 ).getDate(); // 選択中年月の End of Month nDiff = parseInt(eom) - parseInt(day); // 挟まっている月の日数を加算します。 var curMonth = month + 1; // 翌月 while (curMonth < selMonth){ // 目標の月の前月まで eom = new Date( year, curMonth, 0 ).getDate(); // 指定年月の End of Month(もとの値は自動破棄) nDiff += parseInt(eom); curMonth ++; } // 目標の月の日数を加算します。 nDiff += parseInt(selDay); return nDiff; }
次は、年は同じで月が違う場合です。 つまり、年内ということです。
まずは変数 eom に今月の日数を取得して、今月末までの日数を計算します。 ある年月の日数の取得方法は上記コードの通りで、 また、数値を確実に数値として計算するため、parseInt 関数を使用しています。
続いて、「今日」の翌月(来月)から、指定の月の前月まで、日数を計算します。 つまり、まるまる挟まっている月の日数です。
while 文は、カッコ内の条件式が成立しているうちは、 続く中カッコないの部分を繰り返し処理する、というものです。 C/C++ とまったく同じです。なので、ちゃんと書かないと無限ループに入りがちです。 最初から条件が成立していなければ、記述された処理は1回も実行されません。
挟まっている月の日数をすべて足したら、指定した月の目標の日までを加算すれば、完了です。
// 今年の12月31日までの日数を計算 nDiff = calcDaysWithinTheYear(); // 挟まっている年の日数を加算します。 var curYear = year + 1; // 翌年 while (curYear < selYear){ // 目標の年の前年まで nDiff += (isLeapYear(curYear) ? 366 : 365); curYear ++; } // 挟まっている月の日数を加算します。 var curMonth = 1; // 1月から while (curMonth < selMonth){ // 目標の月の前月まで eom = new Date( selYear, curMonth, 0 ).getDate(); // 指定年月の End of Month nDiff += parseInt(eom); curMonth ++; } // 目標の月の日数を加算します。 nDiff += parseInt(selDay); return nDiff; }
残りは、年から異なる場合です。
まずは nDiff に、「今日」から年末までの日数をセットしています。 機能が明確で、そこそこ長くなりそうですので、calcDaysWithinTheYear 関数として分離させています。
次は目的の年まで、まるまる挟まっている年の日数を加算しています。 先ほどと同じ、while 文は条件が成立しているうちはずっと繰り返す、ループ用の制御文です。 年ごとに 1 〜 12 月の日数を、Date オブジェクトを使用して足し算すれば間違いないのですが、 せっかくなので、うるう年の判定関数 isLeapYear を用意しています。
あとは指定の年の、指定の月日までの計算ですが、 まるまる挟まっている月の日数を加算したあと、指定の月の日数を加算しています。 書きながら思うには、「まるまる挟まっている月の日数を加算」は、関数化すると良さそうです。
calcDaysWithinTheYear 関数は、「今年」の残り日数を計算する関数です。
// 同年内の残り日数を計算します(12月31日までの日数)。 function calcDaysWithinTheYear() { var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); var eom; var nMonthTo = 12; var nDayTo = 31;
変数 year、month、day に、「今日」の年月日を設定しています。 コードの再利用のため、nMonthTo に 12(月)、nDayTo に 31(日)を設定しています。 変数名の最初に n が付いているのは、 Windows 風の変数名付けになっているためです。 Windows では、変数名の最初に変数の型を付けて、わかりやすくすることになっています。 n は整数値、number であることを意味します。
// 今月の残り日数を計算します。 var eom = new Date( year, month, 0 ).getDate(); var nDiff = parseInt(eom) - parseInt(day);
計算の流れは、ここまでと同じです。 最初に、今月末までの日数を計算しています。
// 今月が12月の場合は、nDayTo をクリアしておく必要があります。 if (month == 12){ nDayTo = 0; } // 挟まっている月の日数を加算します。 var curMonth = month + 1; // 翌月 while (curMonth < nMonthTo){ // 目標の月の前月まで eom = new Date( year, curMonth, 0 ).getDate(); // 指定年月の End of Month(もとの値は自動破棄) nDiff += parseInt(eom); curMonth ++; } // 12月の日数を加算します。 nDiff += parseInt(nDayTo); return nDiff; }
あとは、まるまる挟まっている月の日数を加算し、12 月の月末までの日数を加算しています。 「今日」が 12 月だった場合のみ、もうすでに計算が済んでいますので、 初期設定で 31 を入れた nDayTo をクリアして、余計に加算されないようにしています。
最後に、うるう年かどうかを判定する、isLeapYear 関数です。 引数で渡した年がうるう年なら true を、そうでなければ false を返します。
/** * うるう年かどうかを返します。 * 4で割り切れる年は、うるう年です。 * しかし、100でも割り切れる年は、うるう年ではありません。 * しかし、400でも割り切れる年は、うるう年です。 */ function isLeapYear(year) { // 4で割り切れない年は、うるう年ではありません。 if (Math.floor(year / 4) != (year / 4)){ return false; } // 4で割り切れて、400でも割り切れるなら、うるう年です。 if (Math.floor(year / 400) == (year / 400)){ return true; } // 4で割り切れ、400では割り切れないが、100で割り切れるなら、うるう年ではありません。 if (Math.floor(year / 100) == (year / 100)){ return false; } // 4で割り切れ、100でも 400でも割れない年は、うるう年です。 return true; }
Math オブジェクトの floor 関数は、
引数で指定した値を超えない整数値を返す関数です。
ここでは
また、(year / 4) のように、 わざわざカッコでくくっているのは、 おそらくなくても問題はないのですが、明示的に先に計算して比較対象にして欲しい、という書き方です。 year だけで比較して、その結果を 4 で割る・・・というのは、このケースでは考えられませんが、 条件式が複雑になるときは、明示したほうが安心、安全でしょう。
ウェブ開発に関するトピックは、「ウェブ開発トップ」にまとめられています。