Skip to content

Commit 9a72370

Browse files
authored
feat: add useCurrentYearIfMissing option to support dates without year (#14)
* feat: add useCurrentYearIfMissing option to support dates without year * refactor: convert addYearToDateText to arrow function and improve readability * chore: add sinon for mocking current date to test `useCurrentYearIfMissing` option * add: currentYear option and remove sinon dependency * refactor: avoid using `let`
1 parent e7dd817 commit 9a72370

File tree

3 files changed

+147
-3
lines changed

3 files changed

+147
-3
lines changed

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,46 @@ But, You can specify `2016-12-30` is `ja-JP` text by options
8787
}
8888
```
8989

90+
- `useCurrentYearIfMissing`: boolean
91+
- Default: false
92+
- If true, when the year is missing in the date string (e.g. `4月23日(月)`), the current year will be automatically added for validation.
93+
- This is useful for documents that often omit the year in dates.
94+
95+
Example:
96+
97+
```json
98+
{
99+
"rules": {
100+
"date-weekday-mismatch": {
101+
"useCurrentYearIfMissing": true
102+
}
103+
}
104+
}
105+
```
106+
107+
If the text contains `4月23日(水)`, and the current year is 2025, it will be interpreted as `2025年4月23日(水)` for the weekday check.
108+
109+
- `currentYear`: number
110+
- Default: the current year (from system date)
111+
- If specified, this value will be used as the year when supplementing missing years in date strings (used only when `useCurrentYearIfMissing` is true).
112+
- This is useful for testing or for documents that should always use a specific year for validation.
113+
114+
Example (using both options):
115+
116+
```json
117+
{
118+
"rules": {
119+
"date-weekday-mismatch": {
120+
"useCurrentYearIfMissing": true,
121+
"currentYear": 2025
122+
}
123+
}
124+
}
125+
```
126+
127+
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.
128+
129+
90130
language format following ISO 639-1.
91131

92132
e.g.) `en-US`, `en`, `ja` etc..

src/textlint-rule-date-weekday-mismatch.js

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,75 @@ const detectLang = (tags, preferLang) => {
5050
const selectedLang = targetLangs[0];
5151
return selectedLang[1];
5252
};
53+
/**
54+
* Add current year to date string if missing.
55+
* @param {string} dateText
56+
* @param {number} year
57+
* @param {string} lang
58+
* @returns {string}
59+
*/
60+
const addYearToDateText = (dateText, year, lang) => {
61+
// Japanese: 4月23日(月) → 2024年4月23日(月)
62+
if (lang === "ja") {
63+
return `${year}${dateText}`;
64+
}
65+
// Slash: 4/23(月) → 2024/4/23(月)
66+
if (/^[0-9]{1,2}\/[0-9]{1,2}/.test(dateText)) {
67+
return `${year}/${dateText}`;
68+
}
69+
// Dash: 4-23(Mon) → 2024-4-23(Mon)
70+
if (/^[0-9]{1,2}-[0-9]{1,2}/.test(dateText)) {
71+
return `${year}-${dateText}`;
72+
}
73+
// Default: prepend year and a space
74+
return `${year} ${dateText}`;
75+
}
76+
/**
77+
* Create chronoDates array, optionally supplementing year if needed.
78+
* @param {string} text
79+
* @param {Object} options
80+
* @param {string} options.preferLang
81+
* @param {boolean} options.useCurrentYearIfMissing
82+
* @param {number} options.currentYear
83+
* @returns {Array}
84+
*/
85+
function createChronoDates(text, options) {
86+
const { preferLang, useCurrentYearIfMissing, currentYear } = options;
87+
const chronoDates = chrono.parse(text);
88+
if (!useCurrentYearIfMissing) {
89+
return chronoDates;
90+
}
91+
chronoDates.forEach(chronoDate => {
92+
// If year is not specified in the parsed result
93+
if (
94+
chronoDate.start &&
95+
chronoDate.start.knownValues.year === undefined
96+
) {
97+
// Detect language for the date string
98+
const lang = detectLang(Object.keys(chronoDate.tags), preferLang);
99+
if (!lang) {
100+
return;
101+
}
102+
// Re-parse the text with the year added (using currentYear)
103+
const newText = addYearToDateText(chronoDate.text, currentYear, lang);
104+
const reparsed = chrono.parse(newText, undefined, {forwardDate: true});
105+
// If reparsed successfully, update knownValues with year/month/day
106+
if (reparsed && reparsed[0] && reparsed[0].start && reparsed[0].start.knownValues) {
107+
Object.assign(chronoDate.start.knownValues, reparsed[0].start.knownValues);
108+
}
109+
}
110+
});
111+
return chronoDates;
112+
}
53113
/**
54114
*
55115
* @param context
56116
* @param {Object} [config]
57117
*/
58118
function reporter(context, config = {}) {
59119
const preferLang = config.lang;
120+
const useCurrentYearIfMissing = config.useCurrentYearIfMissing;
121+
const currentYearOption = config.currentYear;
60122
const {Syntax, RuleError, report, fixer, getSource} = context;
61123
if (typeof Intl === "undefined") {
62124
throw new Error("Not support your Node.js/browser. should be use latest version.");
@@ -65,7 +127,13 @@ function reporter(context, config = {}) {
65127
return {
66128
[Syntax.Str](node){
67129
const text = getSource(node);
68-
const chronoDates = chrono.parse(text);
130+
// Use helper function to create chronoDates
131+
const currentYear = currentYearOption ?? (new Date()).getFullYear();
132+
const chronoDates = createChronoDates(text, {
133+
preferLang,
134+
useCurrentYearIfMissing,
135+
currentYear
136+
});
69137
// ignore "今日" text
70138
// ignore not valid data
71139
const filteredChronoDates = chronoDates.filter(textIncludesNumber).filter(yearMonthDayShouldKnownValues);

test/textlint-rule-date-weekday-mismatch-test.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import TextLintTester from "textlint-tester";
33
const tester = new TextLintTester();
44
// rule
55
const rule = require("../src/textlint-rule-date-weekday-mismatch");
6+
67
// ruleName, rule, { valid, invalid }
78
tester.run("rule", rule, {
89
valid: [
@@ -18,7 +19,18 @@ tester.run("rule", rule, {
1819
// invalid date should be ignored
1920
"11月 25日 (火曜日) ",
2021
// ignore relative word
21-
"今日(火曜日)はどうしよう"
22+
"今日(火曜日)はどうしよう",
23+
// useCurrentYearIfMissing option: valid
24+
{
25+
text: "4月23日(水)",
26+
options: { useCurrentYearIfMissing: true, currentYear: 2025, lang: "ja" },
27+
// 2025年4月23日は水曜日
28+
},
29+
{
30+
text: "4/23(Wed)",
31+
options: { useCurrentYearIfMissing: true, currentYear: 2025, lang: "en" },
32+
// 2025-04-23 is Wednesday
33+
},
2234
],
2335
invalid: [
2436
// single match
@@ -122,6 +134,30 @@ tester.run("rule", rule, {
122134
}
123135
]
124136
},
125-
137+
// useCurrentYearIfMissing option: invalid
138+
{
139+
text: "4月23日(金)",
140+
output: "4月23日(水)",
141+
options: { useCurrentYearIfMissing: true, currentYear: 2025, lang: "ja" },
142+
errors: [
143+
{
144+
message: "4月23日(金) mismatch weekday.\n4月23日(金) => 4月23日(水)",
145+
line: 1,
146+
column: 7
147+
}
148+
]
149+
},
150+
{
151+
text: "4/23(Fri)",
152+
output: "4/23(Wed)",
153+
options: { useCurrentYearIfMissing: true, currentYear: 2025, lang: "en" },
154+
errors: [
155+
{
156+
message: "4/23(Fri) mismatch weekday.\n4/23(Fri) => 4/23(Wed)",
157+
line: 1,
158+
column: 6
159+
}
160+
]
161+
},
126162
]
127163
});

0 commit comments

Comments
 (0)