feat: 日付パースロジックを追加
This commit is contained in:
parent
0bee14f18c
commit
3573d1b78f
92
NodeSrv/apps/healthcheck-survey-bot/src/lib/dateParser.js
Normal file
92
NodeSrv/apps/healthcheck-survey-bot/src/lib/dateParser.js
Normal file
@ -0,0 +1,92 @@
|
||||
const ERA_INFO = {
|
||||
"令和": 2018,
|
||||
"平成": 1988,
|
||||
"昭和": 1925,
|
||||
"大正": 1911,
|
||||
};
|
||||
|
||||
const ERA_ALIASES = { R: "令和", H: "平成", S: "昭和", T: "大正" };
|
||||
|
||||
function pad2(value) {
|
||||
return String(value).padStart(2, "0");
|
||||
}
|
||||
|
||||
function finalizeDateParts(year, month, day) {
|
||||
if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day)) {
|
||||
throw new Error("日付形式で回答してください(例: 2026-03-01)");
|
||||
}
|
||||
const date = new Date(year, month - 1, day);
|
||||
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
|
||||
throw new Error("存在しない日付です");
|
||||
}
|
||||
return `${year}-${pad2(month)}-${pad2(day)}`;
|
||||
}
|
||||
|
||||
function tryParseEraDate(compact) {
|
||||
const match = compact.match(/^(令和|平成|昭和|大正|R|H|S|T)(.+)$/i);
|
||||
if (!match) return null;
|
||||
|
||||
let era = match[1];
|
||||
if (/^[RHST]$/i.test(era)) {
|
||||
era = ERA_ALIASES[era.toUpperCase()] || era;
|
||||
}
|
||||
if (!ERA_INFO[era]) return null;
|
||||
|
||||
const normalized = match[2]
|
||||
.replace(/年/g, "-")
|
||||
.replace(/月/g, "-")
|
||||
.replace(/日/g, "")
|
||||
.replace(/[.\/]/g, "-");
|
||||
const parts = normalized.split("-").filter((part) => part.length > 0);
|
||||
if (parts.length < 3) {
|
||||
throw new Error("月と日まで入力してください(例: 令和6年3月1日)");
|
||||
}
|
||||
|
||||
const eraYear = Number(parts[0]);
|
||||
const month = Number(parts[1]);
|
||||
const day = Number(parts[2]);
|
||||
if (!Number.isFinite(eraYear) || !Number.isFinite(month) || !Number.isFinite(day)) {
|
||||
throw new Error("日付形式で回答してください(例: 令和6年3月1日)");
|
||||
}
|
||||
|
||||
return finalizeDateParts(ERA_INFO[era] + eraYear, month, day);
|
||||
}
|
||||
|
||||
function tryParseMonthDay(compact) {
|
||||
const match = compact.match(/^(\d{1,2})(?:月|\/|-|\.)(\d{1,2})(?:日)?$/);
|
||||
if (!match) return null;
|
||||
const currentYear = new Date().getFullYear();
|
||||
return finalizeDateParts(currentYear, Number(match[1]), Number(match[2]));
|
||||
}
|
||||
|
||||
function parseDateInput(value) {
|
||||
const trimmed = String(value ?? "").trim();
|
||||
if (!trimmed) {
|
||||
throw new Error("日付が空です");
|
||||
}
|
||||
const compact = trimmed.replace(/\s+/g, "");
|
||||
|
||||
const eraResult = tryParseEraDate(compact);
|
||||
if (eraResult) return eraResult;
|
||||
|
||||
const monthDayResult = tryParseMonthDay(compact);
|
||||
if (monthDayResult) return monthDayResult;
|
||||
|
||||
const normalized = compact
|
||||
.replace(/年/g, "-")
|
||||
.replace(/月/g, "-")
|
||||
.replace(/日/g, "")
|
||||
.replace(/[.\/]/g, "-");
|
||||
const isoParts = normalized.split("-").filter((part) => part.length > 0);
|
||||
if (isoParts.length === 3 && isoParts[0].length >= 4) {
|
||||
return finalizeDateParts(Number(isoParts[0]), Number(isoParts[1]), Number(isoParts[2]));
|
||||
}
|
||||
|
||||
const parsed = new Date(trimmed);
|
||||
if (Number.isNaN(parsed.getTime())) {
|
||||
throw new Error("日付形式で回答してください(例: 2026-03-01 や 令和6年3月1日)");
|
||||
}
|
||||
return finalizeDateParts(parsed.getFullYear(), parsed.getMonth() + 1, parsed.getDate());
|
||||
}
|
||||
|
||||
module.exports = { parseDateInput };
|
||||
37
NodeSrv/apps/healthcheck-survey-bot/test/dateParser.test.js
Normal file
37
NodeSrv/apps/healthcheck-survey-bot/test/dateParser.test.js
Normal file
@ -0,0 +1,37 @@
|
||||
// test/dateParser.test.js
|
||||
const { test } = require("node:test");
|
||||
const assert = require("node:assert");
|
||||
const { parseDateInput } = require("../src/lib/dateParser");
|
||||
|
||||
test("ISO形式の日付をそのまま解釈する", () => {
|
||||
assert.strictEqual(parseDateInput("2026-03-01"), "2026-03-01");
|
||||
});
|
||||
|
||||
test("スラッシュ区切りの日付を解釈する", () => {
|
||||
assert.strictEqual(parseDateInput("2026/3/1"), "2026-03-01");
|
||||
});
|
||||
|
||||
test("和暦(令和)を西暦に変換する", () => {
|
||||
assert.strictEqual(parseDateInput("令和6年3月1日"), "2024-03-01");
|
||||
});
|
||||
|
||||
test("和暦の略記(R)を西暦に変換する", () => {
|
||||
assert.strictEqual(parseDateInput("R6.3.1"), "2024-03-01");
|
||||
});
|
||||
|
||||
test("月日のみの入力は今年として解釈する", () => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
assert.strictEqual(parseDateInput("3/1"), `${currentYear}-03-01`);
|
||||
});
|
||||
|
||||
test("空文字はエラーになる", () => {
|
||||
assert.throws(() => parseDateInput(""), /日付が空です/);
|
||||
});
|
||||
|
||||
test("存在しない日付はエラーになる", () => {
|
||||
assert.throws(() => parseDateInput("2026-02-30"), /存在しない日付です/);
|
||||
});
|
||||
|
||||
test("解釈不能な文字列はエラーになる", () => {
|
||||
assert.throws(() => parseDateInput("あいうえお"));
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user