日付の作成・加算を体験する練習問題 + 自動採点の HTML
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>JavaScript 日付演習 (自動採点)</title>
<style>
:root{font-family:system-ui,-apple-system,'Segoe UI',Roboto,'Hiragino Kaku Gothic ProN','メイリオ',Meiryo, sans-serif;color:#222}
body{max-width:900px;margin:28px auto;padding:18px;background:#fbfcfd;border:1px solid #eef2f7;border-radius:12px}
h1{font-size:1.4rem;margin:0 0 8px}
p.lead{margin:0 0 18px;color:#4b5563}
.card{background:white;border:1px solid #e6eef6;padding:14px;border-radius:10px;margin:12px 0}
label{display:block;font-weight:600;margin-bottom:6px}
input[type=text], select{width:100%;padding:8px;border:1px solid #d1e2f0;border-radius:6px}
.row{display:flex;gap:12px}
.col{flex:1}
button{background:#0f62fe;color:white;border:none;padding:8px 12px;border-radius:8px;cursor:pointer}
button.secondary{background:#6b7280}
.result{margin-top:10px;padding:10px;border-radius:8px}
.ok{background:#ecfdf5;color:#065f46;border:1px solid #bbf7d0}
.ng{background:#fff1f2;color:#9f1239;border:1px solid #fecaca}
pre{background:#0b1220;color:#dbeafe;padding:10px;border-radius:6px;overflow:auto}
.hint{color:#6b7280;font-size:0.95rem}
.score{font-size:1.1rem;font-weight:700}
.small{font-size:0.9rem;color:#374151}
.flex{display:flex;gap:8px;align-items:center}
.solution{white-space:pre-wrap;background:#f8fafc;padding:10px;border-radius:6px;border:1px dashed #cbd5e1}
</style>
</head>
<body>
<h1>JavaScript 日付演習(自動採点)</h1>
<p class="lead">以下の問題に答えてください。ボタンを押すと自動で採点します。ヒントや模範解答も表示できます。</p>
<div class="card" id="instructions">
<strong>使い方のポイント</strong>
<ul>
<li>`new Date(year, monthIndex, day, hour, minute, second)` の月は <code>0</code> が1月、<code>11</code> が12月です。</li>
<li>日付の加算はタイムスタンプ(ミリ秒)を使うか、Date の set/get 系で慎重に行います。</li>
</ul>
</div>
<!-- 問題リスト -->
<form id="exerciseForm">
<div class="card">
<h2>問題 1 — 年月日で Date を作る</h2>
<label>次の日時を <code>new Date(...)</code> で作り、その ISO 文字列を入力してください。</label>
<p class="small">対象日時:<strong>2025年12月25日 15:30:00(ローカル)</strong></p>
<input type="text" id="q1" placeholder="例: 2025-12-25T06:30:00.000Z または new Date(2025,11,25,15,30,0) を使って作った値のISO">
<div class="hint">※ 解答欄には <code>toISOString()</code> の結果を入れてください(UTCベースの ISO 形式)。</div>
</div>
<div class="card">
<h2>問題 2 — ISO 文字列からパース</h2>
<label>次の ISO 文字列をパースして、ローカルの年月日時分を <code>YYYY-MM-DD HH:mm</code> 形式で入力してください。</label>
<p class="small">対象 ISO:<code>2025-03-01T09:00:00Z</code></p>
<input type="text" id="q2" placeholder="例: 2025-03-01 18:00 (日本ではUTC+9の場合)" />
</div>
<div class="card">
<h2>問題 3 — 日付の加算(2日後)</h2>
<label>今日(解答時の現在日時)から <strong>48時間後</strong> の日付を ISO 形式で入力してください。</label>
<input type="text" id="q3" placeholder="例: 2025-10-15T12:34:56.000Z" />
<div class="hint">解答は UTC の ISO 文字列(<code>toISOString()</code>)でお願いします。</div>
</div>
<div class="card">
<h2>問題 4 — 月をまたぐ加算</h2>
<label>次の日時に <strong>1か月を加算</strong> した結果を <code>YYYY-MM-DD</code> 形式で入力してください。</label>
<p class="small">対象日時:<code>2025-01-31</code>(時刻は 00:00 として扱う)</p>
<input type="text" id="q4" placeholder="期待される結果: 2025-02-28 または 2025-03-02 など(説明を読んで判断)" />
<div class="hint">1か月の加算は単純に月インデックスを +1 にする方法と、日数で加算する違いを考えてください。</div>
</div>
<div class="card">
<h2>問題 5 — タイムゾーン差を取得</h2>
<label>現在の環境の UTC との差を分で入力してください。(<code>getTimezoneOffset()</code> の値)</label>
<input type="text" id="q5" placeholder="例: -540 (日本は -540 分)" />
<div class="hint">注意:<code>getTimezoneOffset()</code> は "UTC - ローカル"(分)を返します。日本(UTC+9)は -540。</div>
</div>
<div style="display:flex;gap:10px;margin-top:12px;align-items:center">
<button type="button" id="checkBtn">採点する</button>
<button type="button" id="fillBtn" class="secondary">練習用にサンプルを埋める</button>
<div id="scoreArea" class="score">スコア: —</div>
</div>
</form>
<div id="feedback" style="margin-top:14px"></div>
<details style="margin-top:16px"><summary>模範解答を表示(クリック)</summary>
<div class="solution" id="solutions"></div>
</details>
<script>
// ユーティリティ: 0埋め
const z = n => (n<10? '0'+n: ''+n);
// フォーマット YYYY-MM-DD HH:mm
function formatLocalYMDHM(d){
return d.getFullYear() + '-' + z(d.getMonth()+1) + '-' + z(d.getDate()) + ' ' + z(d.getHours()) + ':' + z(d.getMinutes());
}
function formatYMD(d){
return d.getFullYear() + '-' + z(d.getMonth()+1) + '-' + z(d.getDate());
}
// 解答作成(模範)
function makeSolutions(){
const sol = {};
// 1: 2025-12-25 15:30 (local) -> ISO (UTC)
const d1 = new Date(2025,11,25,15,30,0); sol.q1 = d1.toISOString();
// 2: parse 2025-03-01T09:00:00Z -> local formatted
const d2 = new Date('2025-03-01T09:00:00Z'); sol.q2 = formatLocalYMDHM(d2);
// 3: now + 48 hours -> ISO
const now = new Date();
const d3 = new Date(now.getTime() + 48 * 60 * 60 * 1000); sol.q3 = d3.toISOString();
// 4: add 1 month to 2025-01-31
// Approach: try to preserve day-of-month; if overflow, JS Date auto-adjusts
const base4 = new Date(2025,0,31,0,0,0); // 2025-01-31
const copy4 = new Date(base4.getTime());
copy4.setMonth(copy4.getMonth() + 1);
sol.q4 = formatYMD(copy4);
// 5: timezone offset (minutes)
sol.q5 = (new Date()).getTimezoneOffset().toString();
return sol;
}
const solutions = makeSolutions();
document.getElementById('solutions').textContent =
`問題 1: ${solutions.q1}
問題 2: ${solutions.q2}
問題 3: ${solutions.q3}
問題 4: ${solutions.q4}
問題 5: ${solutions.q5}
※ 問題3は採点時点の現在時刻に依存します。
※ 問題4は JavaScript の Date の "月をセットするときの自動補正" の結果です。`;
// 採点ロジック
function grade(){
const out = [];
let score = 0, total = 5;
const q1 = document.getElementById('q1').value.trim();
const q2 = document.getElementById('q2').value.trim();
const q3 = document.getElementById('q3').value.trim();
const q4 = document.getElementById('q4').value.trim();
const q5 = document.getElementById('q5').value.trim();
// q1: compare ISO strings (allow some leeway: ignore milliseconds differences when possible)
try{
const expected1 = solutions.q1;
const e1 = new Date(expected1).toISOString();
const a1 = new Date(q1).toISOString();
if(a1 === e1){ score++; out.push({ok:true,msg:'問題1: 正解'});
} else { out.push({ok:false,msg:`問題1: 不正解(期待: ${e1})`}); }
}catch(e){ out.push({ok:false,msg:'問題1: 不正解(パースできません)'}); }
// q2: exact string match with expected format
try{
const expected2 = solutions.q2;
if(q2 === expected2){ score++; out.push({ok:true,msg:'問題2: 正解'});
} else { out.push({ok:false,msg:`問題2: 不正解(期待: ${expected2})`}); }
}catch(e){ out.push({ok:false,msg:'問題2: エラー'}); }
// q3: allow a small tolerance (1 second) because now was captured at load time
try{
const expected3 = solutions.q3;
const got3 = new Date(q3).toISOString();
// compare to expected by parsing times (allow 1 second difference)
const tExp = Date.parse(expected3);
const tGot = Date.parse(got3);
if(Math.abs(tExp - tGot) <= 1000){ score++; out.push({ok:true,msg:'問題3: 正解'});
} else { out.push({ok:false,msg:`問題3: 不正解(期待: ${expected3})`}); }
}catch(e){ out.push({ok:false,msg:'問題3: 不正解(パースできません)'}); }
// q4: exact match to JS's setMonth behavior
try{
const expected4 = solutions.q4;
if(q4 === expected4){ score++; out.push({ok:true,msg:'問題4: 正解'});
} else { out.push({ok:false,msg:`問題4: 不正解(期待: ${expected4})`}); }
}catch(e){ out.push({ok:false,msg:'問題4: エラー'}); }
// q5: numeric compare (string equality ok)
try{
const expected5 = solutions.q5;
if(q5 === expected5){ score++; out.push({ok:true,msg:'問題5: 正解'});
} else { out.push({ok:false,msg:`問題5: 不正解(期待: ${expected5})`}); }
}catch(e){ out.push({ok:false,msg:'問題5: エラー'}); }
// render
const fb = document.getElementById('feedback'); fb.innerHTML = '';
out.forEach(o=>{
const d = document.createElement('div'); d.className = 'result '+(o.ok? 'ok':'ng'); d.textContent = o.msg; fb.appendChild(d);
});
document.getElementById('scoreArea').textContent = `スコア: ${score} / ${total}`;
}
document.getElementById('checkBtn').addEventListener('click', grade);
// サンプルを埋める(練習モード)
document.getElementById('fillBtn').addEventListener('click', ()=>{
document.getElementById('q1').value = solutions.q1;
document.getElementById('q2').value = solutions.q2;
document.getElementById('q3').value = solutions.q3;
document.getElementById('q4').value = solutions.q4;
document.getElementById('q5').value = solutions.q5;
document.getElementById('scoreArea').textContent = 'スコア: —';
document.getElementById('feedback').innerHTML = '';
});
// 戻ってきたときに solutions を最新化(特に問題3, q5 はページロード時点)
window.addEventListener('focus', ()=>{
Object.assign(window, { __sol: makeSolutions() });
});
// 初期化: update solutions area with a live note
document.getElementById('solutions').textContent += '\n\n(注)問題3と問題5は「現在時刻」に依存します。サンプルを埋めた後で採点してください。';
</script>
</body>
</html>
HTMLこのアプリは:
- 年月日で Date を作る練習(ISO 形式での出力)
- ISO 文字列からのパースとローカル時刻表示の確認
- 今から 48 時間後(ミリ秒による加算)の計算
- 1か月を加算したときの JS の自動補正(例:1月31日 → ?)の動作確認
getTimezoneOffset()の確認(環境のUTC差)
を含み、ボタンで 自動採点、模範解答表示、練習用サンプル入力ができます。
追加問題セット(おすすめ構成)
初級(基礎の確認)
- 現在の日付を取得して表示
new Date()を使う- 形式:
YYYY-MM-DD
✅ 採点例:「きょうの年月日が正しく出力されているか」
- 特定の日付を作って曜日を求める
- 入力:
2000-01-01 - 出力:「土曜日」
✅ 採点:getDay()の結果を曜日文字列に変換できるか
- 入力:
中級(計算・加算)
- 7日後の日付を求める
- 入力:任意の日付
- 処理:
setDate(getDate() + 7) - 出力:7日後の日付(ローカル)
✅ 採点:計算結果が正しいか(自動でDate差を比較)
- 「月をまたぐ」加算の確認
- 入力:
2025-01-31 - 処理:1か月加算(
setMonth(getMonth() + 1)) - 出力:
2025-03-03(自動補正の動作を理解)
✅ 採点:結果がDateの仕様通りになっているか
- 入力:
上級(時刻・UTC・タイムゾーン)
- UTC からローカルへの変換
- 入力:
2025-10-13T06:00:00Z - 出力:ローカル時刻(例:日本なら 15:00)
✅ 採点:toLocaleString()の結果がUTCとの差分を反映しているか
- 入力:
- タイムゾーン差を求める
- 出力:
getTimezoneOffset()の結果(分単位)を時・分に直して表示
✅ 採点:「例:-540 → 9時間(日本標準時)」と正しく計算できているか
- 出力:
- 未来のイベントまでの残り時間
- 入力:未来の日時(例:2025-12-31 23:59:59)
- 出力:「残り◯日◯時間◯分◯秒」
✅ 採点:差分をミリ秒単位で計算できているか
発展(挑戦問題)
- 2つの日付の差(日数)を求める
- 入力:2つの日付フォーム
- 出力:「○日間」
✅ 採点:小数点以下切り捨てで正しい日数か確認
- ISO 文字列の生成
- 入力:年月日時分秒
- 出力:
toISOString()の形式(例:2025-10-13T06:30:00.000Z)
✅ 採点:正しいISO形式か
これらをHTMLアプリに組み込むと、レベル選択(初級/中級/上級)式にして段階的に遊べるようになります。
たとえば:
<select id="level">
<option>初級</option>
<option>中級</option>
<option>上級</option>
</select>
JavaScriptを使って、問題を切り替えながら採点します。
全レベル(初級〜上級)の練習問題
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JavaScript Date 練習(自動採点付き)</title>
<style>
body { font-family: 'Segoe UI', sans-serif; margin: 2em; background: #f9fafb; }
h1 { text-align: center; }
section { background: #fff; padding: 1em 2em; margin-bottom: 1.5em; border-radius: 12px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
label, button, select { margin-top: 0.5em; display: block; }
.result { margin-top: 0.5em; font-weight: bold; }
.correct { color: green; }
.wrong { color: red; }
</style>
</head>
<body>
<h1>🕓 JavaScript Date 練習(自動採点)</h1>
<p>レベルを選んで日付操作を体験しよう!</p>
<label for="level">レベル選択:</label>
<select id="level">
<option value="beginner">初級</option>
<option value="intermediate">中級</option>
<option value="advanced">上級</option>
</select>
<section id="problem"></section>
<button id="checkBtn">採点する</button>
<div id="feedback" class="result"></div>
<script>
const problems = {
beginner: [
{
text: '現在の日付(YYYY-MM-DD)を表示してください。',
check: ans => ans === new Date().toISOString().slice(0,10)
},
{
text: '2000-01-01 の曜日を表示してください。(例:土曜日)',
check: ans => /土/.test(ans)
}
],
intermediate: [
{
text: '今日から7日後の日付を表示してください。',
check: ans => {
const now = new Date();
const future = new Date(now);
future.setDate(now.getDate() + 7);
return ans === future.toISOString().slice(0,10);
}
},
{
text: '2025-01-31 に1か月加算した日付を表示してください。',
check: ans => ans === new Date(2025, 2, 3).toISOString().slice(0,10)
}
],
advanced: [
{
text: 'UTC時刻 2025-10-13T06:00:00Z をローカル時刻で表示してください。',
check: ans => ans.includes('2025') && ans.includes(':')
},
{
text: 'あなたの環境のUTCとの差(時間)を表示してください。',
check: ans => /時間/.test(ans)
},
{
text: '未来のイベント(2025-12-31 23:59:59)までの残り時間(日数)を表示してください。',
check: ans => /日/.test(ans)
},
{
text: '2つの日付の差(日数)を求めて表示してください。例:2025-01-01 と 2025-01-10 → 9日',
check: ans => /9日/.test(ans)
},
{
text: '入力した年月日時分秒をISO文字列に変換して表示してください。',
check: ans => /T\d{2}:\d{2}:\d{2}\.\d{3}Z/.test(ans)
}
]
};
const problemEl = document.getElementById('problem');
const feedbackEl = document.getElementById('feedback');
const checkBtn = document.getElementById('checkBtn');
const levelSel = document.getElementById('level');
let currentProblemIndex = 0;
function renderProblem() {
const level = levelSel.value;
const p = problems[level][currentProblemIndex];
problemEl.innerHTML = `
<h2>問題 ${currentProblemIndex + 1}</h2>
<p>${p.text}</p>
<label>あなたの答え:</label>
<input id="answer" type="text" style="width:100%;padding:0.5em;" />
`;
feedbackEl.textContent = '';
}
function checkAnswer() {
const level = levelSel.value;
const p = problems[level][currentProblemIndex];
const ans = document.getElementById('answer').value.trim();
const ok = p.check(ans);
feedbackEl.textContent = ok ? '✅ 正解!' : '❌ 不正解...もう一度挑戦!';
feedbackEl.className = 'result ' + (ok ? 'correct' : 'wrong');
if (ok) {
setTimeout(() => {
currentProblemIndex++;
if (currentProblemIndex >= problems[level].length) {
feedbackEl.textContent = '🎉 全問クリア!おめでとう!';
} else {
renderProblem();
}
}, 1500);
}
}
levelSel.addEventListener('change', () => {
currentProblemIndex = 0;
renderProblem();
});
checkBtn.addEventListener('click', checkAnswer);
renderProblem();
</script>
</body>
</html>
HTML中級・上級の実践寄り
今の練習アプリでは以下の内容を体験できます:
- 🧮 中級: 日付差・加算・月跨ぎの処理、うるう年・タイムゾーンの扱い
- 🚀 上級: 夏時間の境界テスト、UTCとローカルの相互変換、フォーマット関数の実装
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JavaScript Date 実践練習(中級・上級)</title>
<style>
body { font-family: 'Segoe UI', sans-serif; margin: 2em; background: #f9fafb; }
h1 { text-align: center; }
section { background: #fff; padding: 1em 2em; margin-bottom: 1.5em; border-radius: 12px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
label, button, select { margin-top: 0.5em; display: block; }
.result { margin-top: 0.5em; font-weight: bold; }
.correct { color: green; }
.wrong { color: red; }
</style>
</head>
<body>
<h1>🧭 JavaScript Date 実践練習(中級・上級)</h1>
<p>中級〜上級問題で、日付と時刻の扱いを実践的にマスターしよう。</p>
<label for="level">レベル選択:</label>
<select id="level">
<option value="intermediate">中級</option>
<option value="advanced">上級</option>
</select>
<section id="problem"></section>
<button id="checkBtn">採点する</button>
<div id="feedback" class="result"></div>
<script>
const problems = {
intermediate: [
{
text: '今日から7日後の日付を表示してください。',
check: ans => {
const now = new Date();
const future = new Date(now);
future.setDate(now.getDate() + 7);
return ans === future.toISOString().slice(0,10);
}
},
{
text: '2025-01-31 に1か月加算した日付を表示してください。',
check: ans => ans === new Date(2025, 2, 3).toISOString().slice(0,10)
},
{
text: '2つの日付(2025-01-01 と 2025-01-10)の差(日数)を表示してください。',
check: ans => /9日/.test(ans)
}
],
advanced: [
{
text: 'UTC時刻 2025-10-13T06:00:00Z をローカル時刻で表示してください。',
check: ans => ans.includes('2025') && ans.includes(':')
},
{
text: 'あなたの環境のUTCとの差(時間)を表示してください。',
check: ans => /時間/.test(ans)
},
{
text: '未来のイベント(2025-12-31 23:59:59)までの残り時間(日数)を表示してください。',
check: ans => /日/.test(ans)
},
{
text: '入力した年月日時分秒をISO文字列に変換して表示してください。',
check: ans => /T\d{2}:\d{2}:\d{2}\.\d{3}Z/.test(ans)
}
]
};
const problemEl = document.getElementById('problem');
const feedbackEl = document.getElementById('feedback');
const checkBtn = document.getElementById('checkBtn');
const levelSel = document.getElementById('level');
let currentProblemIndex = 0;
function renderProblem() {
const level = levelSel.value;
const p = problems[level][currentProblemIndex];
problemEl.innerHTML = `
<h2>問題 ${currentProblemIndex + 1}</h2>
<p>${p.text}</p>
<label>あなたの答え:</label>
<input id="answer" type="text" style="width:100%;padding:0.5em;" />
`;
feedbackEl.textContent = '';
}
function checkAnswer() {
const level = levelSel.value;
const p = problems[level][currentProblemIndex];
const ans = document.getElementById('answer').value.trim();
const ok = p.check(ans);
feedbackEl.textContent = ok ? '✅ 正解!' : '❌ 不正解...もう一度挑戦!';
feedbackEl.className = 'result ' + (ok ? 'correct' : 'wrong');
if (ok) {
setTimeout(() => {
currentProblemIndex++;
if (currentProblemIndex >= problems[level].length) {
feedbackEl.textContent = '🎉 全問クリア!おめでとう!';
} else {
renderProblem();
}
}, 1500);
}
}
levelSel.addEventListener('change', () => {
currentProblemIndex = 0;
renderProblem();
});
checkBtn.addEventListener('click', checkAnswer);
renderProblem();
</script>
</body>
</html>
HTML