diff --git a/README.md b/README.md index 04c2372..d9d8512 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,46 @@ But, You can specify `2016-12-30` is `ja-JP` text by options } ``` +- `useCurrentYearIfMissing`: boolean + - Default: false + - If true, when the year is missing in the date string (e.g. `4月23日(月)`), the current year will be automatically added for validation. + - This is useful for documents that often omit the year in dates. + +Example: + +```json +{ + "rules": { + "date-weekday-mismatch": { + "useCurrentYearIfMissing": true + } + } +} +``` + +If the text contains `4月23日(水)`, and the current year is 2025, it will be interpreted as `2025年4月23日(水)` for the weekday check. + +- `currentYear`: number + - Default: the current year (from system date) + - If specified, this value will be used as the year when supplementing missing years in date strings (used only when `useCurrentYearIfMissing` is true). + - This is useful for testing or for documents that should always use a specific year for validation. + +Example (using both options): + +```json +{ + "rules": { + "date-weekday-mismatch": { + "useCurrentYearIfMissing": true, + "currentYear": 2025 + } + } +} +``` + +If the text contains `4月23日(水)`, it will always be interpreted as `2025年4月23日(水)` for the weekday check, regardless of the actual system year. + + language format following ISO 639-1. e.g.) `en-US`, `en`, `ja` etc.. diff --git a/src/textlint-rule-date-weekday-mismatch.js b/src/textlint-rule-date-weekday-mismatch.js index 43db0c8..314f0d7 100644 --- a/src/textlint-rule-date-weekday-mismatch.js +++ b/src/textlint-rule-date-weekday-mismatch.js @@ -50,6 +50,66 @@ const detectLang = (tags, preferLang) => { const selectedLang = targetLangs[0]; return selectedLang[1]; }; +/** + * Add current year to date string if missing. + * @param {string} dateText + * @param {number} year + * @param {string} lang + * @returns {string} + */ +const addYearToDateText = (dateText, year, lang) => { + // Japanese: 4月23日(月) → 2024年4月23日(月) + if (lang === "ja") { + return `${year}年${dateText}`; + } + // Slash: 4/23(月) → 2024/4/23(月) + if (/^[0-9]{1,2}\/[0-9]{1,2}/.test(dateText)) { + return `${year}/${dateText}`; + } + // Dash: 4-23(Mon) → 2024-4-23(Mon) + if (/^[0-9]{1,2}-[0-9]{1,2}/.test(dateText)) { + return `${year}-${dateText}`; + } + // Default: prepend year and a space + return `${year} ${dateText}`; +} +/** + * Create chronoDates array, optionally supplementing year if needed. + * @param {string} text + * @param {Object} options + * @param {string} options.preferLang + * @param {boolean} options.useCurrentYearIfMissing + * @param {number} options.currentYear + * @returns {Array} + */ +function createChronoDates(text, options) { + const { preferLang, useCurrentYearIfMissing, currentYear } = options; + const chronoDates = chrono.parse(text); + if (!useCurrentYearIfMissing) { + return chronoDates; + } + chronoDates.forEach(chronoDate => { + // If year is not specified in the parsed result + if ( + chronoDate.start && + chronoDate.start.knownValues.year === undefined + ) { + // Detect language for the date string + const lang = detectLang(Object.keys(chronoDate.tags), preferLang); + if (!lang) { + return; + } + // Re-parse the text with the year added (using currentYear) + const newText = addYearToDateText(chronoDate.text, currentYear, lang); + const reparsed = chrono.parse(newText, undefined, {forwardDate: true}); + // If reparsed successfully, update knownValues with year/month/day + if (reparsed && reparsed[0] && reparsed[0].start && reparsed[0].start.knownValues) { + Object.assign(chronoDate.start.knownValues, reparsed[0].start.knownValues); + } + } + }); + return chronoDates; +} /** * * @param context @@ -57,6 +117,8 @@ const detectLang = (tags, preferLang) => { */ function reporter(context, config = {}) { const preferLang = config.lang; + const useCurrentYearIfMissing = config.useCurrentYearIfMissing; + const currentYearOption = config.currentYear; const {Syntax, RuleError, report, fixer, getSource} = context; if (typeof Intl === "undefined") { throw new Error("Not support your Node.js/browser. should be use latest version."); @@ -65,7 +127,13 @@ function reporter(context, config = {}) { return { [Syntax.Str](node){ const text = getSource(node); - const chronoDates = chrono.parse(text); + // Use helper function to create chronoDates + const currentYear = currentYearOption ?? (new Date()).getFullYear(); + const chronoDates = createChronoDates(text, { + preferLang, + useCurrentYearIfMissing, + currentYear + }); // ignore "今日" text // ignore not valid data const filteredChronoDates = chronoDates.filter(textIncludesNumber).filter(yearMonthDayShouldKnownValues); diff --git a/test/textlint-rule-date-weekday-mismatch-test.js b/test/textlint-rule-date-weekday-mismatch-test.js index 9ffdc65..d96bb4e 100644 --- a/test/textlint-rule-date-weekday-mismatch-test.js +++ b/test/textlint-rule-date-weekday-mismatch-test.js @@ -3,6 +3,7 @@ import TextLintTester from "textlint-tester"; const tester = new TextLintTester(); // rule const rule = require("../src/textlint-rule-date-weekday-mismatch"); + // ruleName, rule, { valid, invalid } tester.run("rule", rule, { valid: [ @@ -18,7 +19,18 @@ tester.run("rule", rule, { // invalid date should be ignored "11月 25日 (火曜日) ", // ignore relative word - "今日(火曜日)はどうしよう" + "今日(火曜日)はどうしよう", + // useCurrentYearIfMissing option: valid + { + text: "4月23日(水)", + options: { useCurrentYearIfMissing: true, currentYear: 2025, lang: "ja" }, + // 2025年4月23日は水曜日 + }, + { + text: "4/23(Wed)", + options: { useCurrentYearIfMissing: true, currentYear: 2025, lang: "en" }, + // 2025-04-23 is Wednesday + }, ], invalid: [ // single match @@ -122,6 +134,30 @@ tester.run("rule", rule, { } ] }, - + // useCurrentYearIfMissing option: invalid + { + text: "4月23日(金)", + output: "4月23日(水)", + options: { useCurrentYearIfMissing: true, currentYear: 2025, lang: "ja" }, + errors: [ + { + message: "4月23日(金) mismatch weekday.\n4月23日(金) => 4月23日(水)", + line: 1, + column: 7 + } + ] + }, + { + text: "4/23(Fri)", + output: "4/23(Wed)", + options: { useCurrentYearIfMissing: true, currentYear: 2025, lang: "en" }, + errors: [ + { + message: "4/23(Fri) mismatch weekday.\n4/23(Fri) => 4/23(Wed)", + line: 1, + column: 6 + } + ] + }, ] });