@fhss-web-team/fuzzy-dates 1.2.2 → 2.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 (48) hide show
  1. package/README.md +99 -36
  2. package/dist/index.d.ts +1 -3
  3. package/dist/index.js +1 -2
  4. package/dist/src/collation/collationKey.d.ts +2 -0
  5. package/dist/src/collation/collationKey.js +44 -0
  6. package/dist/{fuzzyDate → src}/fuzzyDate.d.ts +85 -69
  7. package/dist/src/fuzzyDate.js +238 -0
  8. package/dist/src/gedcomX/toGedcomX.js +28 -0
  9. package/dist/src/helpers/constants.d.ts +2 -0
  10. package/dist/src/helpers/constants.js +2 -0
  11. package/dist/src/helpers/types.d.ts +12 -0
  12. package/dist/src/helpers/types.js +3 -0
  13. package/dist/src/normalize/normalize.js +51 -0
  14. package/dist/src/parse/index.d.ts +47 -0
  15. package/dist/src/parse/index.js +41 -0
  16. package/dist/src/parse/modifiers.d.ts +182 -0
  17. package/dist/src/parse/modifiers.js +62 -0
  18. package/dist/{fuzzyDate/parse/inputDateFormats.d.ts → src/parse/simpleDate/formats.d.ts} +33 -50
  19. package/dist/{fuzzyDate/parse/inputDateFormats.js → src/parse/simpleDate/formats.js} +69 -88
  20. package/dist/src/parse/simpleDate/helpers.d.ts +21 -0
  21. package/dist/src/parse/simpleDate/helpers.js +58 -0
  22. package/dist/src/parse/simpleDate/parse.d.ts +31 -0
  23. package/dist/{fuzzyDate/parse/stringToDate.js → src/parse/simpleDate/parse.js} +4 -5
  24. package/package.json +1 -11
  25. package/dist/fuzzyDate/collate/collate.d.ts +0 -2
  26. package/dist/fuzzyDate/collate/collate.js +0 -15
  27. package/dist/fuzzyDate/fuzzyDate.js +0 -246
  28. package/dist/fuzzyDate/fuzzyDate.spec.d.ts +0 -1
  29. package/dist/fuzzyDate/fuzzyDate.spec.js +0 -158
  30. package/dist/fuzzyDate/gedcomX/toGedcomX.js +0 -31
  31. package/dist/fuzzyDate/helpers/constants.d.ts +0 -4
  32. package/dist/fuzzyDate/helpers/constants.js +0 -20
  33. package/dist/fuzzyDate/helpers/schemas.d.ts +0 -36
  34. package/dist/fuzzyDate/helpers/schemas.js +0 -12
  35. package/dist/fuzzyDate/helpers/types.d.ts +0 -16
  36. package/dist/fuzzyDate/helpers/types.js +0 -1
  37. package/dist/fuzzyDate/normalize/normalize.js +0 -47
  38. package/dist/fuzzyDate/parse/index.d.ts +0 -178
  39. package/dist/fuzzyDate/parse/index.js +0 -92
  40. package/dist/fuzzyDate/parse/modifiers.d.ts +0 -231
  41. package/dist/fuzzyDate/parse/modifiers.js +0 -185
  42. package/dist/fuzzyDate/parse/stringToDate.d.ts +0 -38
  43. /package/dist/{fuzzyDate → src}/gedcomX/toGedcomX.d.ts +0 -0
  44. /package/dist/{fuzzyDate → src}/helpers/result.d.ts +0 -0
  45. /package/dist/{fuzzyDate → src}/helpers/result.js +0 -0
  46. /package/dist/{fuzzyDate → src}/normalize/normalize.d.ts +0 -0
  47. /package/dist/{fuzzyDate/helpers → src/parse/simpleDate}/maps.d.ts +0 -0
  48. /package/dist/{fuzzyDate/helpers → src/parse/simpleDate}/maps.js +0 -0
@@ -1,11 +1,10 @@
1
- import { calculateMaxDate, parseDateGroups } from '.';
2
- import { isSeason } from '../helpers/maps';
3
- import { ok } from '../helpers/result';
1
+ import { isSeason } from './maps';
2
+ import { ok } from '../../helpers/result';
3
+ import { calculateMaxDate, parseDateGroups } from './helpers';
4
4
  const DAY = '(?<day>\\d{1,2})';
5
5
  const MONTH_DIGIT = '(?<month>\\d{1,2})';
6
6
  const MONTH_STRING = '(?<month>[a-z]+)';
7
7
  const YEAR = '(?<year>\\d{4})';
8
- const DECADE = '(?<decade>\\d{4}s)';
9
8
  // YYYY
10
9
  export const year = (rawDate) => {
11
10
  const pattern = new RegExp(String.raw `^${YEAR}$`);
@@ -15,13 +14,13 @@ export const year = (rawDate) => {
15
14
  const result = parseDateGroups(match.groups);
16
15
  if (!result.ok)
17
16
  return result;
18
- const format = 'YYYY';
19
- const minDate = result.value.date;
20
- const maxDate = calculateMaxDate(minDate, format);
17
+ const precision = 'Year';
18
+ const min = result.value.date;
19
+ const max = calculateMaxDate(min, precision);
21
20
  return ok({
22
- format: format,
23
- minDate: minDate,
24
- maxDate: maxDate,
21
+ precision,
22
+ min,
23
+ max,
25
24
  });
26
25
  };
27
26
  // MMM(M) YYYY || SEASON YYYY
@@ -34,13 +33,13 @@ export const monthStringYear = (rawDate) => {
34
33
  if (!result.ok)
35
34
  return result;
36
35
  const monthString = match.groups['month'].toLowerCase();
37
- const format = isSeason(monthString) ? 'SEASON_YYYY' : 'MMMM_YYYY';
38
- const minDate = result.value.date;
39
- const maxDate = calculateMaxDate(minDate, format);
36
+ const precision = isSeason(monthString) ? 'Season' : 'Month';
37
+ const min = result.value.date;
38
+ const max = calculateMaxDate(min, precision);
40
39
  return ok({
41
- format: format,
42
- minDate: minDate,
43
- maxDate: maxDate,
40
+ precision,
41
+ min,
42
+ max,
44
43
  });
45
44
  };
46
45
  // YYYY MMM(M) || YYYY SEASON
@@ -53,13 +52,13 @@ export const yearMonthString = (rawDate) => {
53
52
  if (!result.ok)
54
53
  return result;
55
54
  const monthString = match.groups['month'].toLowerCase();
56
- const format = isSeason(monthString) ? 'SEASON_YYYY' : 'MMMM_YYYY';
57
- const minDate = result.value.date;
58
- const maxDate = calculateMaxDate(minDate, format);
55
+ const precision = isSeason(monthString) ? 'Season' : 'Month';
56
+ const min = result.value.date;
57
+ const max = calculateMaxDate(min, precision);
59
58
  return ok({
60
- format: format,
61
- minDate: minDate,
62
- maxDate: maxDate,
59
+ precision,
60
+ min,
61
+ max,
63
62
  });
64
63
  };
65
64
  // D(D) MMM(M) YYYY
@@ -71,13 +70,13 @@ export const dayMonthStringYear = (rawDate) => {
71
70
  const result = parseDateGroups(match.groups);
72
71
  if (!result.ok)
73
72
  return result;
74
- const format = 'D_MMMM_YYYY';
75
- const minDate = result.value.date;
76
- const maxDate = calculateMaxDate(minDate, format);
73
+ const precision = 'Day';
74
+ const min = result.value.date;
75
+ const max = calculateMaxDate(min, precision);
77
76
  return ok({
78
- format: format,
79
- minDate: minDate,
80
- maxDate: maxDate,
77
+ precision,
78
+ min,
79
+ max,
81
80
  });
82
81
  };
83
82
  // MMM(M) D(D) YYYY
@@ -89,13 +88,13 @@ export const monthStringDayYear = (rawDate) => {
89
88
  const result = parseDateGroups(match.groups);
90
89
  if (!result.ok)
91
90
  return result;
92
- const format = 'D_MMMM_YYYY';
93
- const minDate = result.value.date;
94
- const maxDate = calculateMaxDate(minDate, format);
91
+ const precision = 'Day';
92
+ const min = result.value.date;
93
+ const max = calculateMaxDate(min, precision);
95
94
  return ok({
96
- format: format,
97
- minDate: minDate,
98
- maxDate: maxDate,
95
+ precision,
96
+ min,
97
+ max,
99
98
  });
100
99
  };
101
100
  // YYYY MMM(M) D(D)
@@ -107,13 +106,13 @@ export const yearMonthStringDay = (rawDate) => {
107
106
  const result = parseDateGroups(match.groups);
108
107
  if (!result.ok)
109
108
  return result;
110
- const format = 'D_MMMM_YYYY';
111
- const minDate = result.value.date;
112
- const maxDate = calculateMaxDate(minDate, format);
109
+ const precision = 'Day';
110
+ const min = result.value.date;
111
+ const max = calculateMaxDate(min, precision);
113
112
  return ok({
114
- format: format,
115
- minDate: minDate,
116
- maxDate: maxDate,
113
+ precision,
114
+ min,
115
+ max,
117
116
  });
118
117
  };
119
118
  // YYYY D(D) MMM(M)
@@ -125,13 +124,13 @@ export const yearDayMonthString = (rawDate) => {
125
124
  const result = parseDateGroups(match.groups);
126
125
  if (!result.ok)
127
126
  return result;
128
- const format = 'D_MMMM_YYYY';
129
- const minDate = result.value.date;
130
- const maxDate = calculateMaxDate(minDate, format);
127
+ const precision = 'Day';
128
+ const min = result.value.date;
129
+ const max = calculateMaxDate(min, precision);
131
130
  return ok({
132
- format: format,
133
- minDate: minDate,
134
- maxDate: maxDate,
131
+ precision,
132
+ min,
133
+ max,
135
134
  });
136
135
  };
137
136
  // M(M) YYYY
@@ -143,13 +142,13 @@ export const monthDigitYear = (rawDate) => {
143
142
  const result = parseDateGroups(match.groups);
144
143
  if (!result.ok)
145
144
  return result;
146
- const format = 'MMMM_YYYY';
147
- const minDate = result.value.date;
148
- const maxDate = calculateMaxDate(minDate, format);
145
+ const precision = 'Month';
146
+ const min = result.value.date;
147
+ const max = calculateMaxDate(min, precision);
149
148
  return ok({
150
- format: format,
151
- minDate: minDate,
152
- maxDate: maxDate,
149
+ precision,
150
+ min,
151
+ max,
153
152
  });
154
153
  };
155
154
  // YYYY M(M)
@@ -161,13 +160,13 @@ export const yearMonthDigit = (rawDate) => {
161
160
  const result = parseDateGroups(match.groups);
162
161
  if (!result.ok)
163
162
  return result;
164
- const format = 'MMMM_YYYY';
165
- const minDate = result.value.date;
166
- const maxDate = calculateMaxDate(minDate, format);
163
+ const precision = 'Month';
164
+ const min = result.value.date;
165
+ const max = calculateMaxDate(min, precision);
167
166
  return ok({
168
- format: format,
169
- minDate: minDate,
170
- maxDate: maxDate,
167
+ precision,
168
+ min,
169
+ max,
171
170
  });
172
171
  };
173
172
  // D(D) M(M) YYYY
@@ -179,13 +178,13 @@ export const dayMonthDigitYear = (rawDate) => {
179
178
  const result = parseDateGroups(match.groups);
180
179
  if (!result.ok)
181
180
  return result;
182
- const format = 'D_MMMM_YYYY';
183
- const minDate = result.value.date;
184
- const maxDate = calculateMaxDate(minDate, format);
181
+ const precision = 'Day';
182
+ const min = result.value.date;
183
+ const max = calculateMaxDate(min, precision);
185
184
  return ok({
186
- format: format,
187
- minDate: minDate,
188
- maxDate: maxDate,
185
+ precision,
186
+ min,
187
+ max,
189
188
  });
190
189
  };
191
190
  // YYYY M(M) D(D)
@@ -197,30 +196,12 @@ export const yearMonthDigitDay = (rawDate) => {
197
196
  const result = parseDateGroups(match.groups);
198
197
  if (!result.ok)
199
198
  return result;
200
- const format = 'D_MMMM_YYYY';
201
- const minDate = result.value.date;
202
- const maxDate = calculateMaxDate(minDate, format);
199
+ const precision = 'Day';
200
+ const min = result.value.date;
201
+ const max = calculateMaxDate(min, precision);
203
202
  return ok({
204
- format: format,
205
- minDate: minDate,
206
- maxDate: maxDate,
207
- });
208
- };
209
- // YYYYs
210
- export const decade = (rawDate) => {
211
- const pattern = new RegExp(String.raw `^${DECADE}$`);
212
- const match = pattern.exec(rawDate);
213
- if (!match?.groups)
214
- return null;
215
- const result = parseDateGroups(match.groups);
216
- if (!result.ok)
217
- return result;
218
- const format = 'YYYYs';
219
- const minDate = result.value.date;
220
- const maxDate = calculateMaxDate(minDate, format);
221
- return ok({
222
- format: format,
223
- minDate: minDate,
224
- maxDate: maxDate,
203
+ precision,
204
+ min,
205
+ max,
225
206
  });
226
207
  };
@@ -0,0 +1,21 @@
1
+ import { Precision } from '../../helpers/types';
2
+ export declare function calculateMaxDate(start: Date, precision: Precision): Date;
3
+ export declare function parseDateGroups(groups: {
4
+ day?: string;
5
+ month?: string;
6
+ year?: string;
7
+ }): {
8
+ ok: false;
9
+ error: "Year is required.";
10
+ } | {
11
+ ok: false;
12
+ error: "Unknown month.";
13
+ } | {
14
+ ok: false;
15
+ error: "Unknown date format.";
16
+ } | {
17
+ ok: true;
18
+ value: {
19
+ date: Date;
20
+ };
21
+ };
@@ -0,0 +1,58 @@
1
+ import { isMonth, isSeason, MONTH_NAME_MAP, MONTH_SEASON_MAP } from './maps';
2
+ import { err, ok } from '../../helpers/result';
3
+ export function calculateMaxDate(start, precision) {
4
+ const endDate = new Date(start);
5
+ if (precision === 'Year') {
6
+ endDate.setUTCFullYear(endDate.getUTCFullYear() + 1);
7
+ }
8
+ else if (precision === 'Season') {
9
+ endDate.setUTCMonth(endDate.getUTCMonth() + 3);
10
+ }
11
+ else if (precision === 'Month') {
12
+ endDate.setUTCMonth(endDate.getUTCMonth() + 1);
13
+ }
14
+ else if (precision === 'Day') {
15
+ endDate.setUTCDate(endDate.getUTCDate() + 1);
16
+ }
17
+ else if (precision === 'Hour') {
18
+ endDate.setUTCHours(endDate.getUTCHours() + 1);
19
+ }
20
+ else if (precision === 'Minute') {
21
+ endDate.setUTCMinutes(endDate.getUTCMinutes() + 1);
22
+ }
23
+ else {
24
+ endDate.setUTCSeconds(endDate.getUTCSeconds() + 1);
25
+ }
26
+ endDate.setUTCMilliseconds(-1);
27
+ return endDate;
28
+ }
29
+ export function parseDateGroups(groups) {
30
+ if (!groups.year)
31
+ return err('Year is required.');
32
+ const year = parseInt(groups.year);
33
+ let month = 0; //default to January
34
+ if (groups.month) {
35
+ const result = resolveMonth(groups.month.toLowerCase());
36
+ if (!result.ok)
37
+ return result;
38
+ month = result.value.monthNumber;
39
+ }
40
+ let day = 1; //default to 1st day
41
+ if (groups.day) {
42
+ const dayToken = /^(?:0?[1-9]|[12][0-9]|3[01])$/.exec(groups.day)?.[0];
43
+ if (!dayToken)
44
+ return err('Unknown date format.');
45
+ day = Number(dayToken);
46
+ }
47
+ return ok({ date: new Date(Date.UTC(year, month, day)) });
48
+ }
49
+ function resolveMonth(rawMonth) {
50
+ const monthToken = /^(?:0?[1-9]|1[0-2])$/.exec(rawMonth)?.[0]; //Matches 01-09 or 1-9 or 10-12
51
+ if (monthToken)
52
+ return ok({ monthNumber: Number(monthToken) - 1 }); //months are zero-based
53
+ if (isMonth(rawMonth))
54
+ return ok({ monthNumber: MONTH_NAME_MAP[rawMonth] - 1 });
55
+ if (isSeason(rawMonth))
56
+ return ok({ monthNumber: MONTH_SEASON_MAP[rawMonth] - 1 });
57
+ return err('Unknown month.');
58
+ }
@@ -0,0 +1,31 @@
1
+ export declare function parseSimpleDate(input: string): {
2
+ ok: false;
3
+ error: "Year is required.";
4
+ } | {
5
+ ok: false;
6
+ error: "Unknown month.";
7
+ } | {
8
+ ok: false;
9
+ error: "Unknown date format.";
10
+ } | {
11
+ ok: true;
12
+ value: {
13
+ readonly precision: "Year";
14
+ readonly min: Date;
15
+ readonly max: Date;
16
+ };
17
+ } | {
18
+ ok: true;
19
+ value: {
20
+ readonly precision: "Season" | "Month";
21
+ readonly min: Date;
22
+ readonly max: Date;
23
+ };
24
+ } | {
25
+ ok: true;
26
+ value: {
27
+ readonly precision: "Day";
28
+ readonly min: Date;
29
+ readonly max: Date;
30
+ };
31
+ };
@@ -1,6 +1,6 @@
1
- import { err } from '../helpers/result';
2
- import { dayMonthDigitYear, dayMonthStringYear, decade, monthDigitYear, monthStringDayYear, monthStringYear, year, yearMonthDigitDay, yearDayMonthString, yearMonthDigit, yearMonthString, yearMonthStringDay, } from './inputDateFormats';
3
- export function stringToDate(dateInput) {
1
+ import { err } from '../../helpers/result';
2
+ import { dayMonthDigitYear, dayMonthStringYear, monthDigitYear, monthStringDayYear, monthStringYear, year, yearMonthDigitDay, yearDayMonthString, yearMonthDigit, yearMonthString, yearMonthStringDay, } from './formats';
3
+ export function parseSimpleDate(input) {
4
4
  const parsers = [
5
5
  year,
6
6
  monthStringYear,
@@ -13,10 +13,9 @@ export function stringToDate(dateInput) {
13
13
  yearMonthDigit,
14
14
  dayMonthDigitYear,
15
15
  yearMonthDigitDay,
16
- decade,
17
16
  ];
18
17
  for (const parseFormat of parsers) {
19
- const result = parseFormat(dateInput);
18
+ const result = parseFormat(input);
20
19
  if (result)
21
20
  return result;
22
21
  }
package/package.json CHANGED
@@ -1,17 +1,13 @@
1
1
  {
2
2
  "name": "@fhss-web-team/fuzzy-dates",
3
- "version": "1.2.2",
3
+ "version": "2.0.0",
4
4
  "description": "Parses imprecise date strings into a structured form that can be normalized, indexed, searched, and ordered while preserving the original input.",
5
5
  "homepage": "https://github.com/FHSS-Web-Team/fuzzy-dates#readme",
6
- "bugs": {
7
- "url": "https://github.com/FHSS-Web-Team/fuzzy-dates/issues"
8
- },
9
6
  "repository": {
10
7
  "type": "git",
11
8
  "url": "git+https://github.com/FHSS-Web-Team/fuzzy-dates.git"
12
9
  },
13
10
  "main": "./dist/index.js",
14
- "module": "./dist/index.js",
15
11
  "types": "./dist/index.d.ts",
16
12
  "exports": {
17
13
  ".": {
@@ -25,25 +21,19 @@
25
21
  "LICENSE"
26
22
  ],
27
23
  "license": "MIT",
28
- "author": "",
29
24
  "type": "module",
30
25
  "scripts": {
31
26
  "build": "npm run clean && tsc",
32
27
  "test": "vitest",
33
28
  "test:run": "vitest run",
34
29
  "clean": "rm -rf dist",
35
- "prepublishOnly": "npm run clean && npm run build",
36
30
  "preversion": "npm run build",
37
31
  "postversion": "git push",
38
32
  "deploy": "npm run build && npm publish --access public"
39
33
  },
40
34
  "devDependencies": {
41
35
  "prettier": "^3.6.2",
42
- "ts-node": "^10.9.2",
43
36
  "typescript": "^5.0.0",
44
37
  "vitest": "^4.0.0"
45
- },
46
- "dependencies": {
47
- "zod": "^4.3.5"
48
38
  }
49
39
  }
@@ -1,2 +0,0 @@
1
- import { FuzzyDateModel } from '../helpers/types';
2
- export declare function collate(model: FuzzyDateModel): string;
@@ -1,15 +0,0 @@
1
- import { FORMAT_ORDER, MODIFIER_ORDER } from '../helpers/constants';
2
- export function collate(model) {
3
- const date1 = model.start.minDate.toISOString();
4
- const modifier = MODIFIER_ORDER.indexOf(model.modifier)
5
- .toString()
6
- .padStart(2, '0');
7
- const format1 = FORMAT_ORDER.indexOf(model.start.format)
8
- .toString()
9
- .padStart(2, '0');
10
- const date2 = model.end.minDate.toISOString();
11
- const format2 = FORMAT_ORDER.indexOf(model.end.format)
12
- .toString()
13
- .padStart(2, '0');
14
- return `${date1}|${modifier}|${format1}|${date2}|${format2}`;
15
- }