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