@louis.jln/extract-date 3.0.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/package.json +21 -65
  2. package/src/calculateSpecificity.d.ts +2 -0
  3. package/src/calculateSpecificity.js +16 -0
  4. package/src/createFormats.d.ts +13 -0
  5. package/src/createFormats.js +140 -0
  6. package/src/createMovingChunks.d.ts +2 -0
  7. package/src/createMovingChunks.js +12 -0
  8. package/src/days.json +22005 -0
  9. package/src/extractDate.d.ts +3 -0
  10. package/src/extractDate.js +157 -0
  11. package/src/extractRelativeDate.d.ts +2 -0
  12. package/src/extractRelativeDate.js +23 -0
  13. package/src/index.d.ts +2 -0
  14. package/src/index.js +2 -0
  15. package/src/months.json +35738 -0
  16. package/src/normalizeInput.d.ts +2 -0
  17. package/src/normalizeInput.js +17 -0
  18. package/src/resolveLocalizedNames.d.ts +2 -0
  19. package/src/resolveLocalizedNames.js +82 -0
  20. package/src/types.d.ts +20 -0
  21. package/src/types.js +1 -0
  22. package/test/extract-date/calculateSpecificity.test.d.ts +1 -0
  23. package/test/extract-date/calculateSpecificity.test.js +39 -0
  24. package/test/extract-date/createMovingChunks.test.d.ts +1 -0
  25. package/test/extract-date/createMovingChunks.test.js +28 -0
  26. package/test/extract-date/extractDate/configuration.test.d.ts +1 -0
  27. package/test/extract-date/extractDate/configuration.test.js +36 -0
  28. package/test/extract-date/extractDate/edge-cases.test.d.ts +1 -0
  29. package/test/extract-date/extractDate/edge-cases.test.js +23 -0
  30. package/test/extract-date/extractDate/fixtures.test.d.ts +1 -0
  31. package/test/extract-date/extractDate/fixtures.test.js +42 -0
  32. package/test/extract-date/extractDate/general-formats.test.d.ts +1 -0
  33. package/test/extract-date/extractDate/general-formats.test.js +45 -0
  34. package/test/extract-date/extractDate/implied-year.test.d.ts +1 -0
  35. package/test/extract-date/extractDate/implied-year.test.js +105 -0
  36. package/test/extract-date/extractDate/localised.test.d.ts +1 -0
  37. package/test/extract-date/extractDate/localised.test.js +75 -0
  38. package/test/extract-date/extractDate/multiple-dates.test.d.ts +1 -0
  39. package/test/extract-date/extractDate/multiple-dates.test.js +24 -0
  40. package/test/extract-date/extractDate/relative-dates.test.d.ts +1 -0
  41. package/test/extract-date/extractDate/relative-dates.test.js +47 -0
  42. package/test/extract-date/extractRelativeDate.test.d.ts +1 -0
  43. package/test/extract-date/extractRelativeDate.test.js +29 -0
  44. package/test/extract-date/normalizeInput.test.d.ts +1 -0
  45. package/test/extract-date/normalizeInput.test.js +14 -0
  46. package/test/fixtures/dates.json +22574 -0
  47. package/calculateSpecificity.js +0 -23
  48. package/calculateSpecificity.js.flow +0 -21
  49. package/calculateSpecificity.js.map +0 -1
  50. package/createFormats.js +0 -165
  51. package/createFormats.js.flow +0 -373
  52. package/createFormats.js.map +0 -1
  53. package/createMovingChunks.js +0 -20
  54. package/createMovingChunks.js.flow +0 -19
  55. package/createMovingChunks.js.map +0 -1
  56. package/dictionary.json +0 -3792
  57. package/extractDate.js +0 -148
  58. package/extractDate.js.flow +0 -214
  59. package/extractDate.js.map +0 -1
  60. package/extractRelativeDate.js +0 -32
  61. package/extractRelativeDate.js.flow +0 -34
  62. package/extractRelativeDate.js.map +0 -1
  63. package/index.js +0 -11
  64. package/index.js.flow +0 -6
  65. package/index.js.map +0 -1
  66. package/louis.jln-extract-date-3.0.0.tgz +0 -0
  67. package/normalizeInput.js +0 -22
  68. package/normalizeInput.js.flow +0 -26
  69. package/normalizeInput.js.map +0 -1
  70. package/types.js +0 -2
  71. package/types.js.flow +0 -23
  72. package/types.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ declare const _default: (input: string) => string;
2
+ export default _default;
@@ -0,0 +1,17 @@
1
+ export default (input) => {
2
+ let lastInput = input;
3
+ while (true) {
4
+ const result = lastInput
5
+ // 2019-02-12T00:00:00
6
+ .replace(/(\d+)T(\d+)/, '$1 $2')
7
+ .replace(/(\d+)\s\/\s(\d+)/, '$1/$2')
8
+ .replace(/[.:;] /g, ' ')
9
+ .trim()
10
+ .replace(/[.!#?]+$/g, '')
11
+ .replace(/ {2,}/g, ' ');
12
+ if (result === lastInput) {
13
+ return result;
14
+ }
15
+ lastInput = result;
16
+ }
17
+ };
@@ -0,0 +1,2 @@
1
+ export declare const replaceMonthName: (word: string, locale: string, accentless: boolean) => string | null;
2
+ export declare const replaceDayName: (word: string, locale: string, accentless: boolean) => string | null;
@@ -0,0 +1,82 @@
1
+ import monthsData from '@/months.json';
2
+ import daysData from '@/days.json';
3
+ const stripDiacritics = (text) => text.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
4
+ const enMonths = monthsData.en;
5
+ const enDays = daysData.en;
6
+ // Cache: locale -> Map<lowercaseName, englishName>
7
+ const monthLookupCache = {};
8
+ const dayLookupCache = {};
9
+ const buildMonthLookup = (locale) => {
10
+ if (monthLookupCache[locale]) {
11
+ return monthLookupCache[locale];
12
+ }
13
+ const localeData = monthsData[locale];
14
+ if (!localeData) {
15
+ monthLookupCache[locale] = {};
16
+ return monthLookupCache[locale];
17
+ }
18
+ const lookup = {};
19
+ for (const monthNum of Object.keys(localeData)) {
20
+ const names = localeData[monthNum];
21
+ const enNames = enMonths[monthNum];
22
+ for (let i = 0; i < names.length; i++) {
23
+ const enName = enNames[i < enNames.length ? i : 0];
24
+ lookup[names[i].toLowerCase()] = enName.charAt(0).toUpperCase() + enName.slice(1);
25
+ }
26
+ }
27
+ monthLookupCache[locale] = lookup;
28
+ return lookup;
29
+ };
30
+ const buildDayLookup = (locale) => {
31
+ if (dayLookupCache[locale]) {
32
+ return dayLookupCache[locale];
33
+ }
34
+ const localeData = daysData[locale];
35
+ if (!localeData) {
36
+ dayLookupCache[locale] = {};
37
+ return dayLookupCache[locale];
38
+ }
39
+ const lookup = {};
40
+ for (const dayNum of Object.keys(localeData)) {
41
+ const names = localeData[dayNum];
42
+ const enNames = enDays[dayNum];
43
+ for (let i = 0; i < names.length; i++) {
44
+ const enName = enNames[i < enNames.length ? i : 0];
45
+ lookup[names[i].toLowerCase()] = enName.charAt(0).toUpperCase() + enName.slice(1);
46
+ }
47
+ }
48
+ dayLookupCache[locale] = lookup;
49
+ return lookup;
50
+ };
51
+ export const replaceMonthName = (word, locale, accentless) => {
52
+ const lookup = buildMonthLookup(locale);
53
+ const lower = word.toLowerCase();
54
+ if (lookup[lower]) {
55
+ return lookup[lower];
56
+ }
57
+ if (accentless) {
58
+ const strippedWord = stripDiacritics(lower);
59
+ for (const key of Object.keys(lookup)) {
60
+ if (stripDiacritics(key) === strippedWord) {
61
+ return lookup[key];
62
+ }
63
+ }
64
+ }
65
+ return null;
66
+ };
67
+ export const replaceDayName = (word, locale, accentless) => {
68
+ const lookup = buildDayLookup(locale);
69
+ const lower = word.toLowerCase();
70
+ if (lookup[lower]) {
71
+ return lookup[lower];
72
+ }
73
+ if (accentless) {
74
+ const strippedWord = stripDiacritics(lower);
75
+ for (const key of Object.keys(lookup)) {
76
+ if (stripDiacritics(key) === strippedWord) {
77
+ return lookup[key];
78
+ }
79
+ }
80
+ }
81
+ return null;
82
+ };
package/src/types.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ export type DateMatchType = {
2
+ readonly date: string;
3
+ };
4
+ export type DirectionType = 'DM' | 'DMY' | 'DYM' | 'MD' | 'YDM' | 'YMD' | 'MDY';
5
+ export type UserConfigurationType = {
6
+ readonly translateAccentless?: boolean;
7
+ readonly direction?: DirectionType;
8
+ readonly locale?: string;
9
+ readonly maximumAge?: number;
10
+ readonly minimumAge?: number;
11
+ readonly timezone?: string;
12
+ };
13
+ export type ConfigurationType = {
14
+ readonly translateAccentless?: boolean;
15
+ readonly direction?: DirectionType;
16
+ readonly locale?: string;
17
+ readonly maximumAge: number;
18
+ readonly minimumAge: number;
19
+ readonly timezone?: string;
20
+ };
package/src/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ import { it, expect } from 'vitest';
2
+ import cartesian from 'cartesian';
3
+ import calculateSpecificity from '@/calculateSpecificity';
4
+ it('tokens including yyyy, (MMMM, MM, MM, M), (dd, d, do) have the highest specificity', () => {
5
+ const formats = cartesian([
6
+ [
7
+ 'yyyy',
8
+ ],
9
+ [
10
+ 'MMMM',
11
+ 'MMM',
12
+ 'MM',
13
+ 'M',
14
+ ],
15
+ [
16
+ 'dd',
17
+ 'd',
18
+ 'do',
19
+ ],
20
+ ]);
21
+ for (const tokens of formats) {
22
+ const format = tokens.join(' ');
23
+ expect(calculateSpecificity(format)).toBe(80 + format.length);
24
+ }
25
+ });
26
+ it('tokens without year, month or month date have the lowest specificity', () => {
27
+ const formats = [
28
+ 'EEEE',
29
+ 'EEE',
30
+ 'R',
31
+ ];
32
+ for (const format of formats) {
33
+ expect(calculateSpecificity(format)).toBe(format.length);
34
+ }
35
+ });
36
+ it('adds format length to the specificity', () => {
37
+ expect(calculateSpecificity('xxxx')).toBe(4);
38
+ expect(calculateSpecificity('xx')).toBe(2);
39
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import { it, expect } from 'vitest';
2
+ import createMovingChunks from '@/createMovingChunks';
3
+ it('creates an array of fixed length text slices, each offset by 1 character', () => {
4
+ expect(createMovingChunks(['a', 'b', 'c', 'd'], 2)).toEqual([
5
+ [
6
+ 'a',
7
+ 'b',
8
+ ],
9
+ [
10
+ 'b',
11
+ 'c',
12
+ ],
13
+ [
14
+ 'c',
15
+ 'd',
16
+ ],
17
+ ]);
18
+ expect(createMovingChunks(['a', 'b', 'c'], 2)).toEqual([
19
+ [
20
+ 'a',
21
+ 'b',
22
+ ],
23
+ [
24
+ 'b',
25
+ 'c',
26
+ ],
27
+ ]);
28
+ });
@@ -0,0 +1,36 @@
1
+ import { it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import extractDate from '@/extractDate';
3
+ beforeEach(() => {
4
+ vi.useFakeTimers();
5
+ });
6
+ afterEach(() => {
7
+ vi.useRealTimers();
8
+ });
9
+ it('throws an error if invalid `locale` is provided', () => {
10
+ expect(() => {
11
+ extractDate('foo', {
12
+ locale: 'bar',
13
+ });
14
+ }).toThrow('No translation available for the target locale.');
15
+ });
16
+ it('throws an error if invalid `timezone` is provided', () => {
17
+ expect(() => {
18
+ extractDate('foo', {
19
+ timezone: 'bar',
20
+ });
21
+ }).toThrow('Unrecognized timezone.');
22
+ });
23
+ it('throws an error if invalid `maximumAge` is a negative value', () => {
24
+ expect(() => {
25
+ extractDate('foo', {
26
+ maximumAge: -1,
27
+ });
28
+ }).toThrow('`maximumAge` must be a positive number.');
29
+ });
30
+ it('throws an error if invalid `minimumAge` is a negative value', () => {
31
+ expect(() => {
32
+ extractDate('foo', {
33
+ minimumAge: -1,
34
+ });
35
+ }).toThrow('`minimumAge` must be a positive number.');
36
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import { it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { parse as parseDate, } from 'date-fns';
3
+ import extractDate from '@/extractDate';
4
+ beforeEach(() => {
5
+ vi.useFakeTimers();
6
+ });
7
+ afterEach(() => {
8
+ vi.useRealTimers();
9
+ });
10
+ it('parses ISO 8601 date-time format', () => {
11
+ vi.setSystemTime(parseDate('2000-01-02', 'yyyy-MM-dd', new Date()).getTime());
12
+ expect(extractDate('2000-01-02T00:00')).toEqual([{ date: '2000-01-02', originalText: '2000-01-02' }]);
13
+ });
14
+ it('extracts date matching multiple formats once', () => {
15
+ vi.setSystemTime(parseDate('2000-01-02', 'yyyy-MM-dd', new Date()).getTime());
16
+ // 'foo bar' prefix is required to ensure that substring matching is correctly advancing.
17
+ expect(extractDate('foo bar 02/01/2000', { direction: 'DMY' })).toEqual([{ date: '2000-01-02', originalText: '02/01/2000' }]);
18
+ });
19
+ it('full-year formats are used regardless of whether direction defines Y', () => {
20
+ vi.setSystemTime(parseDate('2000-01-02', 'yyyy-MM-dd', new Date()).getTime());
21
+ expect(extractDate('02/01/2020', { direction: 'DM' })).toEqual([{ date: '2020-01-02', originalText: '02/01/2020' }]);
22
+ });
23
+ it.todo('interprets 24:00 time as the next date');
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,42 @@
1
+ import { it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { parse as parseDate, } from 'date-fns';
3
+ import fixtureDates from '@test/fixtures/dates.json';
4
+ import extractDate from '@/extractDate';
5
+ beforeEach(() => {
6
+ vi.useFakeTimers();
7
+ });
8
+ afterEach(() => {
9
+ vi.useRealTimers();
10
+ });
11
+ const normalizedFixtureDates = fixtureDates
12
+ .map((fixture) => {
13
+ return {
14
+ ...fixture,
15
+ subject: fixture.subject.trim(),
16
+ };
17
+ })
18
+ .map((fixture) => {
19
+ return JSON.stringify(fixture);
20
+ })
21
+ .filter((fixture, index, self) => {
22
+ return self.indexOf(fixture) === index;
23
+ })
24
+ .map((fixture) => {
25
+ return JSON.parse(fixture);
26
+ })
27
+ .map((fixture) => {
28
+ return {
29
+ ...fixture,
30
+ configuration: {
31
+ ...fixture.configuration,
32
+ maximumAge: Infinity,
33
+ minimumAge: Infinity,
34
+ },
35
+ };
36
+ });
37
+ for (const fixtureDate of normalizedFixtureDates) {
38
+ it('extracts dates from "' + fixtureDate.subject + '" fixture using ' + JSON.stringify(fixtureDate.configuration) + ' configuration on ' + fixtureDate.date + ' date', () => {
39
+ vi.setSystemTime(parseDate(fixtureDate.date, 'yyyy-MM-dd', new Date()).getTime());
40
+ expect(extractDate(fixtureDate.subject, fixtureDate.configuration)).toEqual(fixtureDate.matches);
41
+ });
42
+ }
@@ -0,0 +1,45 @@
1
+ import { it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { format as formatDate, parse as parseDate, } from 'date-fns';
3
+ import extractDate from '@/extractDate';
4
+ import createFormats from '@/createFormats';
5
+ // https://en.wikipedia.org/wiki/Date_format_by_country
6
+ // %w arbitrary white-space separated text
7
+ const describeConfiguration = (userConfiguration) => {
8
+ const configuration = {};
9
+ if (userConfiguration.direction) {
10
+ configuration.direction = userConfiguration.direction;
11
+ }
12
+ return JSON.stringify(configuration);
13
+ };
14
+ const formats = createFormats();
15
+ vi.useFakeTimers();
16
+ vi.setSystemTime(parseDate('2000-06-01', 'yyyy-MM-dd', new Date()).getTime());
17
+ const subjects = formats
18
+ .filter((format) => {
19
+ return format.test !== false;
20
+ })
21
+ .map((format) => {
22
+ const currentDate = new Date();
23
+ return {
24
+ date: formatDate(currentDate, 'yyyy-MM-dd'),
25
+ dateFnsFormat: format.dateFnsFormat,
26
+ direction: format.direction,
27
+ input: formatDate(currentDate, format.dateFnsFormat),
28
+ };
29
+ });
30
+ vi.useRealTimers();
31
+ beforeEach(() => {
32
+ vi.useFakeTimers();
33
+ });
34
+ afterEach(() => {
35
+ vi.useRealTimers();
36
+ });
37
+ for (const subject of subjects) {
38
+ it('extracts ' + subject.dateFnsFormat + ' from "' + subject.input + '" input using ' + describeConfiguration(subject) + ' configuration', () => {
39
+ vi.setSystemTime(parseDate('2000-06-01', 'yyyy-MM-dd', new Date()).getTime());
40
+ const actual = extractDate(subject.input, subject);
41
+ const expected = subject.date;
42
+ expect(actual.length).toBe(1);
43
+ expect(actual[0].date).toBe(expected);
44
+ });
45
+ }
@@ -0,0 +1,105 @@
1
+ import { it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import moment from 'moment';
3
+ import extractDate from '@/extractDate';
4
+ beforeEach(() => {
5
+ vi.useFakeTimers();
6
+ });
7
+ afterEach(() => {
8
+ vi.useRealTimers();
9
+ });
10
+ it('assumes last year if month difference is greater or equal to `maximumAge`', () => {
11
+ vi.setSystemTime(moment('2000-01-01').valueOf());
12
+ const configuration = {
13
+ direction: 'MD',
14
+ maximumAge: 10,
15
+ };
16
+ expect(extractDate('01-01', configuration)).toEqual([{ date: '2000-01-01', originalText: '01-01' }]);
17
+ expect(extractDate('02-01', configuration)).toEqual([{ date: '2000-02-01', originalText: '02-01' }]);
18
+ expect(extractDate('03-01', configuration)).toEqual([{ date: '2000-03-01', originalText: '03-01' }]);
19
+ expect(extractDate('04-01', configuration)).toEqual([{ date: '2000-04-01', originalText: '04-01' }]);
20
+ expect(extractDate('05-01', configuration)).toEqual([{ date: '2000-05-01', originalText: '05-01' }]);
21
+ expect(extractDate('06-01', configuration)).toEqual([{ date: '2000-06-01', originalText: '06-01' }]);
22
+ expect(extractDate('07-01', configuration)).toEqual([{ date: '2000-07-01', originalText: '07-01' }]);
23
+ expect(extractDate('08-01', configuration)).toEqual([{ date: '2000-08-01', originalText: '08-01' }]);
24
+ expect(extractDate('09-01', configuration)).toEqual([{ date: '2000-09-01', originalText: '09-01' }]);
25
+ expect(extractDate('10-01', configuration)).toEqual([{ date: '2000-10-01', originalText: '10-01' }]);
26
+ expect(extractDate('11-01', configuration)).toEqual([{ date: '1999-11-01', originalText: '11-01' }]);
27
+ expect(extractDate('12-01', configuration)).toEqual([{ date: '1999-12-01', originalText: '12-01' }]);
28
+ });
29
+ it('does not assume last year when `maximumAge` is `Infinity`', () => {
30
+ vi.setSystemTime(moment('2000-01-01').valueOf());
31
+ const configuration = {
32
+ direction: 'MD',
33
+ maximumAge: Infinity,
34
+ };
35
+ expect(extractDate('01-01', configuration)).toEqual([{ date: '2000-01-01', originalText: '01-01' }]);
36
+ expect(extractDate('02-01', configuration)).toEqual([{ date: '2000-02-01', originalText: '02-01' }]);
37
+ expect(extractDate('03-01', configuration)).toEqual([{ date: '2000-03-01', originalText: '03-01' }]);
38
+ expect(extractDate('04-01', configuration)).toEqual([{ date: '2000-04-01', originalText: '04-01' }]);
39
+ expect(extractDate('05-01', configuration)).toEqual([{ date: '2000-05-01', originalText: '05-01' }]);
40
+ expect(extractDate('06-01', configuration)).toEqual([{ date: '2000-06-01', originalText: '06-01' }]);
41
+ expect(extractDate('07-01', configuration)).toEqual([{ date: '2000-07-01', originalText: '07-01' }]);
42
+ expect(extractDate('08-01', configuration)).toEqual([{ date: '2000-08-01', originalText: '08-01' }]);
43
+ expect(extractDate('09-01', configuration)).toEqual([{ date: '2000-09-01', originalText: '09-01' }]);
44
+ expect(extractDate('10-01', configuration)).toEqual([{ date: '2000-10-01', originalText: '10-01' }]);
45
+ expect(extractDate('11-01', configuration)).toEqual([{ date: '2000-11-01', originalText: '11-01' }]);
46
+ expect(extractDate('12-01', configuration)).toEqual([{ date: '2000-12-01', originalText: '12-01' }]);
47
+ });
48
+ it('increments year value if month difference is greater or equal to `minimumAge`', () => {
49
+ vi.setSystemTime(moment('2000-12-01').valueOf());
50
+ const configuration = {
51
+ direction: 'MD',
52
+ minimumAge: 2,
53
+ };
54
+ expect(extractDate('01-01', configuration)).toEqual([{ date: '2001-01-01', originalText: '01-01' }]);
55
+ expect(extractDate('02-01', configuration)).toEqual([{ date: '2001-02-01', originalText: '02-01' }]);
56
+ expect(extractDate('03-01', configuration)).toEqual([{ date: '2001-03-01', originalText: '03-01' }]);
57
+ expect(extractDate('04-01', configuration)).toEqual([{ date: '2001-04-01', originalText: '04-01' }]);
58
+ expect(extractDate('05-01', configuration)).toEqual([{ date: '2001-05-01', originalText: '05-01' }]);
59
+ expect(extractDate('06-01', configuration)).toEqual([{ date: '2001-06-01', originalText: '06-01' }]);
60
+ expect(extractDate('07-01', configuration)).toEqual([{ date: '2001-07-01', originalText: '07-01' }]);
61
+ expect(extractDate('08-01', configuration)).toEqual([{ date: '2001-08-01', originalText: '08-01' }]);
62
+ expect(extractDate('09-01', configuration)).toEqual([{ date: '2001-09-01', originalText: '09-01' }]);
63
+ expect(extractDate('10-01', configuration)).toEqual([{ date: '2001-10-01', originalText: '10-01' }]);
64
+ expect(extractDate('11-01', configuration)).toEqual([{ date: '2000-11-01', originalText: '11-01' }]);
65
+ expect(extractDate('12-01', configuration)).toEqual([{ date: '2000-12-01', originalText: '12-01' }]);
66
+ });
67
+ it('does not increment year value if `minimumAge` is `Infinity`', () => {
68
+ vi.setSystemTime(moment('2000-12-01').valueOf());
69
+ const configuration = {
70
+ direction: 'MD',
71
+ minimumAge: Infinity,
72
+ };
73
+ expect(extractDate('01-01', configuration)).toEqual([{ date: '2000-01-01', originalText: '01-01' }]);
74
+ expect(extractDate('02-01', configuration)).toEqual([{ date: '2000-02-01', originalText: '02-01' }]);
75
+ expect(extractDate('03-01', configuration)).toEqual([{ date: '2000-03-01', originalText: '03-01' }]);
76
+ expect(extractDate('04-01', configuration)).toEqual([{ date: '2000-04-01', originalText: '04-01' }]);
77
+ expect(extractDate('05-01', configuration)).toEqual([{ date: '2000-05-01', originalText: '05-01' }]);
78
+ expect(extractDate('06-01', configuration)).toEqual([{ date: '2000-06-01', originalText: '06-01' }]);
79
+ expect(extractDate('07-01', configuration)).toEqual([{ date: '2000-07-01', originalText: '07-01' }]);
80
+ expect(extractDate('08-01', configuration)).toEqual([{ date: '2000-08-01', originalText: '08-01' }]);
81
+ expect(extractDate('09-01', configuration)).toEqual([{ date: '2000-09-01', originalText: '09-01' }]);
82
+ expect(extractDate('10-01', configuration)).toEqual([{ date: '2000-10-01', originalText: '10-01' }]);
83
+ expect(extractDate('11-01', configuration)).toEqual([{ date: '2000-11-01', originalText: '11-01' }]);
84
+ expect(extractDate('12-01', configuration)).toEqual([{ date: '2000-12-01', originalText: '12-01' }]);
85
+ });
86
+ it('`maximumAge` and `minimumAge` can be combined', () => {
87
+ vi.setSystemTime(moment('2000-06-01').valueOf());
88
+ const configuration = {
89
+ direction: 'MD',
90
+ maximumAge: 2,
91
+ minimumAge: 2,
92
+ };
93
+ expect(extractDate('01-01', configuration)).toEqual([{ date: '2001-01-01', originalText: '01-01' }]);
94
+ expect(extractDate('02-01', configuration)).toEqual([{ date: '2001-02-01', originalText: '02-01' }]);
95
+ expect(extractDate('03-01', configuration)).toEqual([{ date: '2001-03-01', originalText: '03-01' }]);
96
+ expect(extractDate('04-01', configuration)).toEqual([{ date: '2001-04-01', originalText: '04-01' }]);
97
+ expect(extractDate('05-01', configuration)).toEqual([{ date: '2000-05-01', originalText: '05-01' }]);
98
+ expect(extractDate('06-01', configuration)).toEqual([{ date: '2000-06-01', originalText: '06-01' }]);
99
+ expect(extractDate('07-01', configuration)).toEqual([{ date: '2000-07-01', originalText: '07-01' }]);
100
+ expect(extractDate('08-01', configuration)).toEqual([{ date: '1999-08-01', originalText: '08-01' }]);
101
+ expect(extractDate('09-01', configuration)).toEqual([{ date: '1999-09-01', originalText: '09-01' }]);
102
+ expect(extractDate('10-01', configuration)).toEqual([{ date: '1999-10-01', originalText: '10-01' }]);
103
+ expect(extractDate('11-01', configuration)).toEqual([{ date: '1999-11-01', originalText: '11-01' }]);
104
+ expect(extractDate('12-01', configuration)).toEqual([{ date: '1999-12-01', originalText: '12-01' }]);
105
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ import { it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { parse as parseDate, } from 'date-fns';
3
+ import extractDate from '@/extractDate';
4
+ beforeEach(() => {
5
+ vi.useFakeTimers();
6
+ });
7
+ afterEach(() => {
8
+ vi.useRealTimers();
9
+ });
10
+ it('extracts a localised date (English)', () => {
11
+ vi.setSystemTime(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime());
12
+ const configuration = {
13
+ locale: 'en',
14
+ };
15
+ expect(extractDate('May 1, 2017', configuration)).toEqual([{ date: '2017-05-01', originalText: 'May 1, 2017' }]);
16
+ });
17
+ it('extracts a localised date (French)', () => {
18
+ vi.setSystemTime(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime());
19
+ const configuration = {
20
+ locale: 'fr',
21
+ };
22
+ expect(extractDate('Mai 1, 2017', configuration)).toEqual([{ date: '2017-05-01', originalText: 'Mai 1, 2017' }]);
23
+ });
24
+ it('extracts a localised date with accentless mode (French)', () => {
25
+ vi.setSystemTime(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime());
26
+ const configuration = {
27
+ translateAccentless: true,
28
+ locale: 'fr',
29
+ };
30
+ expect(extractDate('fevrier 1, 2017', configuration)).toEqual([{ date: '2017-02-01', originalText: 'fevrier 1, 2017' }]);
31
+ });
32
+ it('extracts a localised date for a non-date-fns locale (Afrikaans)', () => {
33
+ vi.setSystemTime(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime());
34
+ const configuration = {
35
+ locale: 'af',
36
+ };
37
+ expect(extractDate('Januarie 1, 2017', configuration)).toEqual([{ date: '2017-01-01', originalText: 'Januarie 1, 2017' }]);
38
+ });
39
+ it('Translation works', () => {
40
+ vi.setSystemTime(parseDate('2018-01-01', 'yyyy-MM-dd', new Date()).getTime());
41
+ expect(extractDate('Va bene domani', {
42
+ translateAccentless: true,
43
+ locale: 'it',
44
+ })).toEqual([{ date: '2018-01-02', originalText: 'domani' }]);
45
+ expect(extractDate('Va bene domani', {
46
+ translateAccentless: true,
47
+ locale: 'fr',
48
+ })).toEqual([]);
49
+ expect(extractDate('Va bene domani', {
50
+ translateAccentless: true,
51
+ locale: 'en',
52
+ })).toEqual([]);
53
+ });
54
+ // Italian day names carry a grave accent (lunedì, martedì, venerdì…).
55
+ // accentless:true lets callers omit the accent and still get a match.
56
+ // 2018-02-05 is a Monday, so "lunedi 5 febbraio" unambiguously maps to that date.
57
+ it('extracts Italian date with accentless day name', () => {
58
+ vi.setSystemTime(parseDate('2018-01-01', 'yyyy-MM-dd', new Date()).getTime());
59
+ expect(extractDate('lunedi 5 febbraio', {
60
+ translateAccentless: true,
61
+ locale: 'it',
62
+ })).toEqual([{ date: '2018-02-05', originalText: 'lunedi 5 febbraio' }]);
63
+ expect(extractDate('lunedi 5 febbraio', {
64
+ translateAccentless: false,
65
+ locale: 'it',
66
+ })).toEqual([{ date: '2018-02-05', originalText: '5 febbraio' }]);
67
+ expect(extractDate('lunedì 5 febbraio', {
68
+ translateAccentless: true,
69
+ locale: 'it',
70
+ })).toEqual([{ date: '2018-02-05', originalText: 'lunedi 5 febbraio' }]);
71
+ expect(extractDate('lunedì 5 febbraio', {
72
+ translateAccentless: false,
73
+ locale: 'it',
74
+ })).toEqual([{ date: '2018-02-05', originalText: 'lunedi 5 febbraio' }]);
75
+ });
@@ -0,0 +1,24 @@
1
+ import { it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { addDays, format as formatDate, parse as parseDate, } from 'date-fns';
3
+ import extractDate from '@/extractDate';
4
+ beforeEach(() => {
5
+ vi.useFakeTimers();
6
+ });
7
+ afterEach(() => {
8
+ vi.useRealTimers();
9
+ });
10
+ it('extracts multiple dates', () => {
11
+ vi.setSystemTime(parseDate('2000-06-01', 'yyyy-MM-dd', new Date()).valueOf());
12
+ const actual = extractDate(formatDate(new Date(), 'yyyy-MM-dd') + ' ' + formatDate(addDays(new Date(), 1), 'yyyy-MM-dd'));
13
+ const expected = [
14
+ {
15
+ date: formatDate(new Date(), 'yyyy-MM-dd'),
16
+ originalText: formatDate(new Date(), 'yyyy-MM-dd'),
17
+ },
18
+ {
19
+ date: formatDate(addDays(new Date(), 1), 'yyyy-MM-dd'),
20
+ originalText: formatDate(addDays(new Date(), 1), 'yyyy-MM-dd'),
21
+ },
22
+ ];
23
+ expect(actual).toEqual(expected);
24
+ });
@@ -0,0 +1,47 @@
1
+ import { it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { parse as parseDate, } from 'date-fns';
3
+ import extractDate from '@/extractDate';
4
+ beforeEach(() => {
5
+ vi.useFakeTimers();
6
+ });
7
+ afterEach(() => {
8
+ vi.useRealTimers();
9
+ });
10
+ it('does not extract relative dates when locale is undefined', () => {
11
+ vi.setSystemTime(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime());
12
+ const configuration = {
13
+ timezone: 'Europe/London',
14
+ };
15
+ expect(extractDate('today', configuration)).toEqual([]);
16
+ });
17
+ it('extracts relative dates when timezone is undefined (uses local time)', () => {
18
+ vi.setSystemTime(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime());
19
+ const configuration = {
20
+ locale: 'en',
21
+ };
22
+ expect(extractDate('today', configuration)).toEqual([{ date: '2000-01-01', originalText: 'today' }]);
23
+ });
24
+ it('extracts relative date (yesterday)', () => {
25
+ vi.setSystemTime(parseDate('2000-01-02', 'yyyy-MM-dd', new Date()).getTime());
26
+ const configuration = {
27
+ locale: 'en',
28
+ timezone: 'Europe/London',
29
+ };
30
+ expect(extractDate('yesterday', configuration)).toEqual([{ date: '2000-01-01', originalText: 'yesterday' }]);
31
+ });
32
+ it('extracts relative date (today)', () => {
33
+ vi.setSystemTime(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime());
34
+ const configuration = {
35
+ locale: 'en',
36
+ timezone: 'Europe/London',
37
+ };
38
+ expect(extractDate('today', configuration)).toEqual([{ date: '2000-01-01', originalText: 'today' }]);
39
+ });
40
+ it('extracts relative date (tomorrow)', () => {
41
+ vi.setSystemTime(parseDate('2000-01-01', 'yyyy-MM-dd', new Date()).getTime());
42
+ const configuration = {
43
+ locale: 'en',
44
+ timezone: 'Europe/London',
45
+ };
46
+ expect(extractDate('tomorrow', configuration)).toEqual([{ date: '2000-01-02', originalText: 'tomorrow' }]);
47
+ });
@@ -0,0 +1 @@
1
+ export {};