@fhss-web-team/fuzzy-dates 1.2.3 → 2.1.1

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 +44 -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} +70 -91
  20. package/dist/src/parse/simpleDate/helpers.d.ts +21 -0
  21. package/dist/src/parse/simpleDate/helpers.js +53 -0
  22. package/dist/{fuzzyDate/helpers → src/parse/simpleDate}/maps.d.ts +0 -16
  23. package/dist/{fuzzyDate/helpers → src/parse/simpleDate}/maps.js +0 -20
  24. package/dist/src/parse/simpleDate/parse.d.ts +31 -0
  25. package/dist/{fuzzyDate/parse/stringToDate.js → src/parse/simpleDate/parse.js} +4 -5
  26. package/package.json +1 -11
  27. package/dist/fuzzyDate/collate/collate.d.ts +0 -2
  28. package/dist/fuzzyDate/collate/collate.js +0 -15
  29. package/dist/fuzzyDate/fuzzyDate.js +0 -246
  30. package/dist/fuzzyDate/fuzzyDate.spec.d.ts +0 -1
  31. package/dist/fuzzyDate/fuzzyDate.spec.js +0 -158
  32. package/dist/fuzzyDate/gedcomX/toGedcomX.js +0 -31
  33. package/dist/fuzzyDate/helpers/constants.d.ts +0 -4
  34. package/dist/fuzzyDate/helpers/constants.js +0 -20
  35. package/dist/fuzzyDate/helpers/schemas.d.ts +0 -36
  36. package/dist/fuzzyDate/helpers/schemas.js +0 -12
  37. package/dist/fuzzyDate/helpers/types.d.ts +0 -16
  38. package/dist/fuzzyDate/helpers/types.js +0 -1
  39. package/dist/fuzzyDate/normalize/normalize.js +0 -47
  40. package/dist/fuzzyDate/parse/index.d.ts +0 -178
  41. package/dist/fuzzyDate/parse/index.js +0 -92
  42. package/dist/fuzzyDate/parse/modifiers.d.ts +0 -231
  43. package/dist/fuzzyDate/parse/modifiers.js +0 -185
  44. package/dist/fuzzyDate/parse/stringToDate.d.ts +0 -38
  45. /package/dist/{fuzzyDate → src}/gedcomX/toGedcomX.d.ts +0 -0
  46. /package/dist/{fuzzyDate → src}/helpers/result.d.ts +0 -0
  47. /package/dist/{fuzzyDate → src}/helpers/result.js +0 -0
  48. /package/dist/{fuzzyDate → src}/normalize/normalize.d.ts +0 -0
@@ -1,11 +1,9 @@
1
- import { calculateMaxDate, parseDateGroups } from '.';
2
- import { isSeason } from '../helpers/maps';
3
- import { ok } from '../helpers/result';
1
+ import { ok } from '../../helpers/result';
2
+ import { calculateMaxDate, parseDateGroups } from './helpers';
4
3
  const DAY = '(?<day>\\d{1,2})';
5
4
  const MONTH_DIGIT = '(?<month>\\d{1,2})';
6
5
  const MONTH_STRING = '(?<month>[a-z]+)';
7
6
  const YEAR = '(?<year>\\d{4})';
8
- const DECADE = '(?<decade>\\d{4}s)';
9
7
  // YYYY
10
8
  export const year = (rawDate) => {
11
9
  const pattern = new RegExp(String.raw `^${YEAR}$`);
@@ -15,16 +13,16 @@ export const year = (rawDate) => {
15
13
  const result = parseDateGroups(match.groups);
16
14
  if (!result.ok)
17
15
  return result;
18
- const format = 'YYYY';
19
- const minDate = result.value.date;
20
- const maxDate = calculateMaxDate(minDate, format);
16
+ const precision = 'Year';
17
+ const min = result.value.date;
18
+ const max = calculateMaxDate(min, precision);
21
19
  return ok({
22
- format: format,
23
- minDate: minDate,
24
- maxDate: maxDate,
20
+ precision,
21
+ min,
22
+ max,
25
23
  });
26
24
  };
27
- // MMM(M) YYYY || SEASON YYYY
25
+ // MMM(M) YYYY
28
26
  export const monthStringYear = (rawDate) => {
29
27
  const pattern = new RegExp(String.raw `^${MONTH_STRING}\s${YEAR}$`);
30
28
  const match = pattern.exec(rawDate);
@@ -34,16 +32,16 @@ export const monthStringYear = (rawDate) => {
34
32
  if (!result.ok)
35
33
  return result;
36
34
  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);
35
+ const precision = 'Month';
36
+ const min = result.value.date;
37
+ const max = calculateMaxDate(min, precision);
40
38
  return ok({
41
- format: format,
42
- minDate: minDate,
43
- maxDate: maxDate,
39
+ precision,
40
+ min,
41
+ max,
44
42
  });
45
43
  };
46
- // YYYY MMM(M) || YYYY SEASON
44
+ // YYYY MMM(M)
47
45
  export const yearMonthString = (rawDate) => {
48
46
  const pattern = new RegExp(String.raw `^${YEAR}\s${MONTH_STRING}$`);
49
47
  const match = pattern.exec(rawDate);
@@ -52,14 +50,13 @@ export const yearMonthString = (rawDate) => {
52
50
  const result = parseDateGroups(match.groups);
53
51
  if (!result.ok)
54
52
  return result;
55
- 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);
53
+ const precision = 'Month';
54
+ const min = result.value.date;
55
+ const max = calculateMaxDate(min, precision);
59
56
  return ok({
60
- format: format,
61
- minDate: minDate,
62
- maxDate: maxDate,
57
+ precision,
58
+ min,
59
+ max,
63
60
  });
64
61
  };
65
62
  // D(D) MMM(M) YYYY
@@ -71,13 +68,13 @@ export const dayMonthStringYear = (rawDate) => {
71
68
  const result = parseDateGroups(match.groups);
72
69
  if (!result.ok)
73
70
  return result;
74
- const format = 'D_MMMM_YYYY';
75
- const minDate = result.value.date;
76
- const maxDate = calculateMaxDate(minDate, format);
71
+ const precision = 'Day';
72
+ const min = result.value.date;
73
+ const max = calculateMaxDate(min, precision);
77
74
  return ok({
78
- format: format,
79
- minDate: minDate,
80
- maxDate: maxDate,
75
+ precision,
76
+ min,
77
+ max,
81
78
  });
82
79
  };
83
80
  // MMM(M) D(D) YYYY
@@ -89,13 +86,13 @@ export const monthStringDayYear = (rawDate) => {
89
86
  const result = parseDateGroups(match.groups);
90
87
  if (!result.ok)
91
88
  return result;
92
- const format = 'D_MMMM_YYYY';
93
- const minDate = result.value.date;
94
- const maxDate = calculateMaxDate(minDate, format);
89
+ const precision = 'Day';
90
+ const min = result.value.date;
91
+ const max = calculateMaxDate(min, precision);
95
92
  return ok({
96
- format: format,
97
- minDate: minDate,
98
- maxDate: maxDate,
93
+ precision,
94
+ min,
95
+ max,
99
96
  });
100
97
  };
101
98
  // YYYY MMM(M) D(D)
@@ -107,13 +104,13 @@ export const yearMonthStringDay = (rawDate) => {
107
104
  const result = parseDateGroups(match.groups);
108
105
  if (!result.ok)
109
106
  return result;
110
- const format = 'D_MMMM_YYYY';
111
- const minDate = result.value.date;
112
- const maxDate = calculateMaxDate(minDate, format);
107
+ const precision = 'Day';
108
+ const min = result.value.date;
109
+ const max = calculateMaxDate(min, precision);
113
110
  return ok({
114
- format: format,
115
- minDate: minDate,
116
- maxDate: maxDate,
111
+ precision,
112
+ min,
113
+ max,
117
114
  });
118
115
  };
119
116
  // YYYY D(D) MMM(M)
@@ -125,13 +122,13 @@ export const yearDayMonthString = (rawDate) => {
125
122
  const result = parseDateGroups(match.groups);
126
123
  if (!result.ok)
127
124
  return result;
128
- const format = 'D_MMMM_YYYY';
129
- const minDate = result.value.date;
130
- const maxDate = calculateMaxDate(minDate, format);
125
+ const precision = 'Day';
126
+ const min = result.value.date;
127
+ const max = calculateMaxDate(min, precision);
131
128
  return ok({
132
- format: format,
133
- minDate: minDate,
134
- maxDate: maxDate,
129
+ precision,
130
+ min,
131
+ max,
135
132
  });
136
133
  };
137
134
  // M(M) YYYY
@@ -143,13 +140,13 @@ export const monthDigitYear = (rawDate) => {
143
140
  const result = parseDateGroups(match.groups);
144
141
  if (!result.ok)
145
142
  return result;
146
- const format = 'MMMM_YYYY';
147
- const minDate = result.value.date;
148
- const maxDate = calculateMaxDate(minDate, format);
143
+ const precision = 'Month';
144
+ const min = result.value.date;
145
+ const max = calculateMaxDate(min, precision);
149
146
  return ok({
150
- format: format,
151
- minDate: minDate,
152
- maxDate: maxDate,
147
+ precision,
148
+ min,
149
+ max,
153
150
  });
154
151
  };
155
152
  // YYYY M(M)
@@ -161,13 +158,13 @@ export const yearMonthDigit = (rawDate) => {
161
158
  const result = parseDateGroups(match.groups);
162
159
  if (!result.ok)
163
160
  return result;
164
- const format = 'MMMM_YYYY';
165
- const minDate = result.value.date;
166
- const maxDate = calculateMaxDate(minDate, format);
161
+ const precision = 'Month';
162
+ const min = result.value.date;
163
+ const max = calculateMaxDate(min, precision);
167
164
  return ok({
168
- format: format,
169
- minDate: minDate,
170
- maxDate: maxDate,
165
+ precision,
166
+ min,
167
+ max,
171
168
  });
172
169
  };
173
170
  // D(D) M(M) YYYY
@@ -179,13 +176,13 @@ export const dayMonthDigitYear = (rawDate) => {
179
176
  const result = parseDateGroups(match.groups);
180
177
  if (!result.ok)
181
178
  return result;
182
- const format = 'D_MMMM_YYYY';
183
- const minDate = result.value.date;
184
- const maxDate = calculateMaxDate(minDate, format);
179
+ const precision = 'Day';
180
+ const min = result.value.date;
181
+ const max = calculateMaxDate(min, precision);
185
182
  return ok({
186
- format: format,
187
- minDate: minDate,
188
- maxDate: maxDate,
183
+ precision,
184
+ min,
185
+ max,
189
186
  });
190
187
  };
191
188
  // YYYY M(M) D(D)
@@ -197,30 +194,12 @@ export const yearMonthDigitDay = (rawDate) => {
197
194
  const result = parseDateGroups(match.groups);
198
195
  if (!result.ok)
199
196
  return result;
200
- const format = 'D_MMMM_YYYY';
201
- const minDate = result.value.date;
202
- const maxDate = calculateMaxDate(minDate, format);
203
- 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);
197
+ const precision = 'Day';
198
+ const min = result.value.date;
199
+ const max = calculateMaxDate(min, precision);
221
200
  return ok({
222
- format: format,
223
- minDate: minDate,
224
- maxDate: maxDate,
201
+ precision,
202
+ min,
203
+ max,
225
204
  });
226
205
  };
@@ -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,53 @@
1
+ import { isMonth, MONTH_NAME_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 === 'Month') {
9
+ endDate.setUTCMonth(endDate.getUTCMonth() + 1);
10
+ }
11
+ else if (precision === 'Day') {
12
+ endDate.setUTCDate(endDate.getUTCDate() + 1);
13
+ }
14
+ else if (precision === 'Hour') {
15
+ endDate.setUTCHours(endDate.getUTCHours() + 1);
16
+ }
17
+ else if (precision === 'Minute') {
18
+ endDate.setUTCMinutes(endDate.getUTCMinutes() + 1);
19
+ }
20
+ else {
21
+ endDate.setUTCSeconds(endDate.getUTCSeconds() + 1);
22
+ }
23
+ endDate.setUTCMilliseconds(-1);
24
+ return endDate;
25
+ }
26
+ export function parseDateGroups(groups) {
27
+ if (!groups.year)
28
+ return err('Year is required.');
29
+ const year = parseInt(groups.year);
30
+ let month = 0; //default to January
31
+ if (groups.month) {
32
+ const result = resolveMonth(groups.month.toLowerCase());
33
+ if (!result.ok)
34
+ return result;
35
+ month = result.value.monthNumber;
36
+ }
37
+ let day = 1; //default to 1st day
38
+ if (groups.day) {
39
+ const dayToken = /^(?:0?[1-9]|[12][0-9]|3[01])$/.exec(groups.day)?.[0];
40
+ if (!dayToken)
41
+ return err('Unknown date format.');
42
+ day = Number(dayToken);
43
+ }
44
+ return ok({ date: new Date(Date.UTC(year, month, day)) });
45
+ }
46
+ function resolveMonth(rawMonth) {
47
+ const monthToken = /^(?:0?[1-9]|1[0-2])$/.exec(rawMonth)?.[0]; //Matches 01-09 or 1-9 or 10-12
48
+ if (monthToken)
49
+ return ok({ monthNumber: Number(monthToken) - 1 }); //months are zero-based
50
+ if (isMonth(rawMonth))
51
+ return ok({ monthNumber: MONTH_NAME_MAP[rawMonth] - 1 });
52
+ return err('Unknown month.');
53
+ }
@@ -5,22 +5,6 @@ export declare const MONTH_SEASON_MAP: {
5
5
  readonly autumn: 9;
6
6
  readonly winter: 12;
7
7
  };
8
- export declare function isSeason(input: string): input is keyof typeof MONTH_SEASON_MAP;
9
- export declare const SEASON_MONTH_MAP: {
10
- readonly 1: "winter";
11
- readonly 2: "winter";
12
- readonly 3: "spring";
13
- readonly 4: "spring";
14
- readonly 5: "spring";
15
- readonly 6: "summer";
16
- readonly 7: "summer";
17
- readonly 8: "summer";
18
- readonly 9: "fall";
19
- readonly 10: "fall";
20
- readonly 11: "fall";
21
- readonly 12: "winter";
22
- };
23
- export declare function isSeasonMonth(input: number): input is keyof typeof SEASON_MONTH_MAP;
24
8
  export declare const MONTH_NAME_MAP: {
25
9
  readonly jan: 1;
26
10
  readonly january: 1;
@@ -5,26 +5,6 @@ export const MONTH_SEASON_MAP = {
5
5
  autumn: 9,
6
6
  winter: 12,
7
7
  };
8
- export function isSeason(input) {
9
- return Object.keys(MONTH_SEASON_MAP).includes(input);
10
- }
11
- export const SEASON_MONTH_MAP = {
12
- 1: 'winter',
13
- 2: 'winter',
14
- 3: 'spring',
15
- 4: 'spring',
16
- 5: 'spring',
17
- 6: 'summer',
18
- 7: 'summer',
19
- 8: 'summer',
20
- 9: 'fall',
21
- 10: 'fall',
22
- 11: 'fall',
23
- 12: 'winter',
24
- };
25
- export function isSeasonMonth(input) {
26
- return input >= 1 && input <= 12;
27
- }
28
8
  export const MONTH_NAME_MAP = {
29
9
  jan: 1,
30
10
  january: 1,
@@ -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: "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.3",
3
+ "version": "2.1.1",
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
- }