From 63a8470edb0b5bce7af2cfc173c155b5fb09baa2 Mon Sep 17 00:00:00 2001 From: Pieter Asselbergs Date: Fri, 23 Oct 2026 15:11:47 +0200 Subject: [PATCH 1/3] feat(nl): support 't/m' and 'tot en met' as range connectors "t/m" and "tot en met" (up to and including) are the usual Dutch way to write an inclusive date range, next to "tot". Co-Authored-By: Claude Opus 5.5 (1M context) --- .../parsers/NLMonthNameMiddleEndianParser.ts | 2 +- .../nl/refiners/NLMergeDateRangeRefiner.ts | 4 +- test/nl/nl_month_name_little_endian.test.ts | 76 +++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/src/locales/nl/parsers/NLMonthNameMiddleEndianParser.ts b/src/locales/nl/parsers/NLMonthNameMiddleEndianParser.ts index e30c89eb4..d58a949d0 100644 --- a/src/locales/nl/parsers/NLMonthNameMiddleEndianParser.ts +++ b/src/locales/nl/parsers/NLMonthNameMiddleEndianParser.ts @@ -10,7 +10,7 @@ const PATTERN = new RegExp( "(?:on\\s*?)?" + `(${ORDINAL_NUMBER_PATTERN})` + "(?:\\s*" + - "(?:tot|\\-|\\–|until|through|till|\\s)\\s*" + + "(?:tot en met|t\\/m|tot|\\-|\\–|until|through|till|\\s)\\s*" + `(${ORDINAL_NUMBER_PATTERN})` + ")?" + "(?:-|/|\\s*(?:of)?\\s*)" + diff --git a/src/locales/nl/refiners/NLMergeDateRangeRefiner.ts b/src/locales/nl/refiners/NLMergeDateRangeRefiner.ts index 9591afa04..3a0979a67 100644 --- a/src/locales/nl/refiners/NLMergeDateRangeRefiner.ts +++ b/src/locales/nl/refiners/NLMergeDateRangeRefiner.ts @@ -8,10 +8,12 @@ import AbstractMergeDateRangeRefiner from "../../../common/refiners/AbstractMerg * Merging before and after results (see. AbstractMergeDateRangeRefiner) * This implementation should provide Dutch connecting phases * - 2020-02-13 [tot] 2020-02-13 + * - 2020-02-13 [t/m] 2020-02-13 + * - 2020-02-13 [tot en met] 2020-02-13 * - Wednesday [-] Friday */ export default class NLMergeDateRangeRefiner extends AbstractMergeDateRangeRefiner { patternBetween(): RegExp { - return /^\s*(tot|-)\s*$/i; + return /^\s*(tot en met|t\/m|tot|-)\s*$/i; } } diff --git a/test/nl/nl_month_name_little_endian.test.ts b/test/nl/nl_month_name_little_endian.test.ts index dc451d2e5..97329ceeb 100644 --- a/test/nl/nl_month_name_little_endian.test.ts +++ b/test/nl/nl_month_name_little_endian.test.ts @@ -179,6 +179,82 @@ test("Test - Range expression", () => { expect(result.end).toBeDate(new Date(2012, 8 - 1, 22, 12)); }); + testSingleCase(chrono.nl, "10 t/m 22 augustus 2012", new Date(2012, 7, 10), (result) => { + expect(result.index).toBe(0); + expect(result.text).toBe("10 t/m 22 augustus 2012"); + + expect(result.start).not.toBeNull(); + expect(result.start.get("year")).toBe(2012); + expect(result.start.get("month")).toBe(8); + expect(result.start.get("day")).toBe(10); + + expect(result.start).toBeDate(new Date(2012, 8 - 1, 10, 12)); + + expect(result.end).not.toBeNull(); + expect(result.end.get("year")).toBe(2012); + expect(result.end.get("month")).toBe(8); + expect(result.end.get("day")).toBe(22); + + expect(result.end).toBeDate(new Date(2012, 8 - 1, 22, 12)); + }); + + testSingleCase(chrono.nl, "10 tot en met 22 augustus 2012", new Date(2012, 7, 10), (result) => { + expect(result.index).toBe(0); + expect(result.text).toBe("10 tot en met 22 augustus 2012"); + + expect(result.start).not.toBeNull(); + expect(result.start.get("year")).toBe(2012); + expect(result.start.get("month")).toBe(8); + expect(result.start.get("day")).toBe(10); + + expect(result.start).toBeDate(new Date(2012, 8 - 1, 10, 12)); + + expect(result.end).not.toBeNull(); + expect(result.end.get("year")).toBe(2012); + expect(result.end.get("month")).toBe(8); + expect(result.end.get("day")).toBe(22); + + expect(result.end).toBeDate(new Date(2012, 8 - 1, 22, 12)); + }); + + testSingleCase(chrono.nl, "10 augustus t/m 12 september", new Date(2012, 7, 10), (result) => { + expect(result.index).toBe(0); + expect(result.text).toBe("10 augustus t/m 12 september"); + + expect(result.start).not.toBeNull(); + expect(result.start.get("year")).toBe(2012); + expect(result.start.get("month")).toBe(8); + expect(result.start.get("day")).toBe(10); + + expect(result.start).toBeDate(new Date(2012, 8 - 1, 10, 12)); + + expect(result.end).not.toBeNull(); + expect(result.end.get("year")).toBe(2012); + expect(result.end.get("month")).toBe(9); + expect(result.end.get("day")).toBe(12); + + expect(result.end).toBeDate(new Date(2012, 9 - 1, 12, 12)); + }); + + testSingleCase(chrono.nl, "10 augustus tot en met 12 september", new Date(2012, 7, 10), (result) => { + expect(result.index).toBe(0); + expect(result.text).toBe("10 augustus tot en met 12 september"); + + expect(result.start).not.toBeNull(); + expect(result.start.get("year")).toBe(2012); + expect(result.start.get("month")).toBe(8); + expect(result.start.get("day")).toBe(10); + + expect(result.start).toBeDate(new Date(2012, 8 - 1, 10, 12)); + + expect(result.end).not.toBeNull(); + expect(result.end.get("year")).toBe(2012); + expect(result.end.get("month")).toBe(9); + expect(result.end.get("day")).toBe(12); + + expect(result.end).toBeDate(new Date(2012, 9 - 1, 12, 12)); + }); + testSingleCase(chrono.nl, "10 augustus - 12 september", new Date(2012, 7, 10), (result) => { expect(result.index).toBe(0); expect(result.text).toBe("10 augustus - 12 september"); From d02ca5105663551ed7456db22678086d9b9251aa Mon Sep 17 00:00:00 2001 From: Pieter Asselbergs Date: Fri, 23 Oct 2026 15:11:47 +0200 Subject: [PATCH 2/3] feat(nl): support 'gister' as yesterday "gister" is the common spoken and written short form of "gisteren", like "morgend" next to "morgen". Co-Authored-By: Claude Opus 5.5 (1M context) --- src/locales/nl/parsers/NLCasualDateParser.ts | 3 ++- test/nl/nl_casual.test.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/locales/nl/parsers/NLCasualDateParser.ts b/src/locales/nl/parsers/NLCasualDateParser.ts index 666957343..fc93d6084 100644 --- a/src/locales/nl/parsers/NLCasualDateParser.ts +++ b/src/locales/nl/parsers/NLCasualDateParser.ts @@ -5,7 +5,7 @@ import * as references from "../../../common/casualReferences"; export default class NLCasualDateParser extends AbstractParserWithWordBoundaryChecking { innerPattern(context: ParsingContext): RegExp { - return /(nu|vandaag|morgen|morgend|gisteren)(?=\W|$)/i; + return /(nu|vandaag|morgen|morgend|gisteren|gister)(?=\W|$)/i; } innerExtract(context: ParsingContext, match: RegExpMatchArray): ParsingComponents | ParsingResult { @@ -24,6 +24,7 @@ export default class NLCasualDateParser extends AbstractParserWithWordBoundaryCh return references.tomorrow(context.reference); case "gisteren": + case "gister": return references.yesterday(context.reference); } diff --git a/test/nl/nl_casual.test.ts b/test/nl/nl_casual.test.ts index 7bdfa0d09..3948a983b 100644 --- a/test/nl/nl_casual.test.ts +++ b/test/nl/nl_casual.test.ts @@ -61,6 +61,18 @@ test("Test - Single Expression", () => { expect(result.start).toBeDate(new Date(2012, 7, 9, 12)); }); + testSingleCase(chrono.nl, "De deadline was gister", new Date(2012, 7, 10, 12), (result) => { + expect(result.index).toBe(16); + expect(result.text).toBe("gister"); + + expect(result.start).not.toBeNull(); + expect(result.start.get("year")).toBe(2012); + expect(result.start.get("month")).toBe(8); + expect(result.start.get("day")).toBe(9); + + expect(result.start).toBeDate(new Date(2012, 7, 9, 12)); + }); + testSingleCase(chrono.nl, "De Deadline was deze ochtend", new Date(2012, 7, 10, 12), (result) => { expect(result.index).toBe(16); expect(result.text).toBe("deze ochtend"); From ec4ea1378d39320037310469a17f26065983353e Mon Sep 17 00:00:00 2001 From: Pieter Asselbergs Date: Fri, 23 Oct 2026 15:11:47 +0200 Subject: [PATCH 3/3] feat(nl): support 'kwartaal' as a time unit Adds "kwartaal" / "kwartalen" (quarter / quarters) to the Dutch time unit dictionary, as English already has "quarter", so "volgend kwartaal" and "over 2 kwartalen" are recognised. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/locales/nl/constants.ts | 2 ++ test/nl/nl_relative.test.ts | 18 ++++++++++++++++++ test/nl/nl_time_units_later.test.ts | 13 +++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/locales/nl/constants.ts b/src/locales/nl/constants.ts index bc1cdfdaa..2b11d2689 100644 --- a/src/locales/nl/constants.ts +++ b/src/locales/nl/constants.ts @@ -158,6 +158,8 @@ export const TIME_UNIT_DICTIONARY: { [word: string]: Timeunit } = { weken: "week", maand: "month", maanden: "month", + kwartaal: "quarter", + kwartalen: "quarter", jaar: "year", jr: "year", jaren: "year", diff --git a/test/nl/nl_relative.test.ts b/test/nl/nl_relative.test.ts index f99c9515d..7328804a9 100644 --- a/test/nl/nl_relative.test.ts +++ b/test/nl/nl_relative.test.ts @@ -67,6 +67,15 @@ test("Test - Past relative expressions", () => { expect(result.start.get("day")).toBe(24); expect(result.start.get("hour")).toBe(12); }); + + // last quarter + testSingleCase(chrono.nl, "afgelopen kwartaal", new Date(2016, 10 - 1, 1, 12), (result, text) => { + expect(result.text).toBe(text); + expect(result.start.get("year")).toBe(2016); + expect(result.start.get("month")).toBe(7); + expect(result.start.get("day")).toBe(1); + expect(result.start.get("hour")).toBe(12); + }); }); test("Test - Future relative expressions", () => { @@ -125,6 +134,15 @@ test("Test - Future relative expressions", () => { expect(result.start.isCertain("hour")).toBe(false); }); + // next quarter + testSingleCase(chrono.nl, "volgend kwartaal", new Date(2016, 10 - 1, 1, 12), (result, text) => { + expect(result.text).toBe(text); + expect(result.start.get("year")).toBe(2017); + expect(result.start.get("month")).toBe(1); + expect(result.start.get("day")).toBe(1); + expect(result.start.get("hour")).toBe(12); + }); + // next year testSingleCase(chrono.nl, "volgend jaar", new Date(2020, 11 - 1, 22, 12, 11, 32, 6), (result, text) => { expect(result.text).toBe(text); diff --git a/test/nl/nl_time_units_later.test.ts b/test/nl/nl_time_units_later.test.ts index f43667a67..ab5e55b3f 100644 --- a/test/nl/nl_time_units_later.test.ts +++ b/test/nl/nl_time_units_later.test.ts @@ -174,6 +174,19 @@ test("Test - From now Expression", () => { expect(result.start).toBeDate(new Date(2012, 8 - 1, 15)); }); + // 2 Quarters from now, we did something + testSingleCase(chrono.nl, "Over 2 kwartalen hebben we iets gedaan", new Date(2012, 7, 10), (result) => { + expect(result.start).not.toBeNull(); + expect(result.start.get("year")).toBe(2013); + expect(result.start.get("month")).toBe(2); + expect(result.start.get("day")).toBe(10); + + expect(result.index).toBe(0); + expect(result.text).toBe("Over 2 kwartalen"); + + expect(result.start).toBeDate(new Date(2013, 2 - 1, 10)); + }); + // half An hour from now testSingleCase(chrono.nl, " half uur vanaf nu", new Date(2012, 7, 10, 12, 14), (result) => { expect(result.index).toBe(3);