@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
@@ -0,0 +1,28 @@
1
+ import { isRange } from '../helpers/types';
2
+ export function toGedcomX(model) {
3
+ const startDate = toSimpleDate(model.start);
4
+ const endDate = toSimpleDate(model.end);
5
+ if (isRange(model)) {
6
+ return `${model.approximate ? 'A' : ''}${startDate ?? ''}/${endDate ?? ''}`;
7
+ }
8
+ else {
9
+ return `${model.approximate ? 'A' : ''}${startDate}`;
10
+ }
11
+ }
12
+ function toSimpleDate(dateValue) {
13
+ if (dateValue === null)
14
+ return null;
15
+ const year = dateValue.min.getUTCFullYear();
16
+ const month = dateValue.min.getUTCMonth() + 1;
17
+ const day = dateValue.min.getUTCDate();
18
+ const sign = year >= 0 ? '+' : '-';
19
+ const yearAbs = Math.abs(year);
20
+ const yyyy = String(yearAbs).padStart(4, '0');
21
+ if (dateValue.precision === 'Year')
22
+ return `${sign}${yyyy}`;
23
+ const mm = String(month).padStart(2, '0');
24
+ if (dateValue.precision === 'Month' || dateValue.precision === 'Season')
25
+ return `${sign}${yyyy}-${mm}`;
26
+ const dd = String(day).padStart(2, '0');
27
+ return `${sign}${yyyy}-${mm}-${dd}`;
28
+ }
@@ -0,0 +1,2 @@
1
+ export declare const DATE_NEG_INFINITY: Date;
2
+ export declare const DATE_POS_INFINITY: Date;
@@ -0,0 +1,2 @@
1
+ export const DATE_NEG_INFINITY = new Date(-8640000000000000);
2
+ export const DATE_POS_INFINITY = new Date(8640000000000000);
@@ -0,0 +1,12 @@
1
+ export type SimpleDate = {
2
+ precision: Precision;
3
+ min: Date;
4
+ max: Date;
5
+ };
6
+ export type FuzzyDateModel = {
7
+ approximate: boolean;
8
+ start: SimpleDate | null;
9
+ end: SimpleDate | null;
10
+ };
11
+ export type Precision = 'Year' | 'Season' | 'Month' | 'Day' | 'Hour' | 'Minute' | 'Second';
12
+ export declare function isRange(model: FuzzyDateModel): boolean;
@@ -0,0 +1,3 @@
1
+ export function isRange(model) {
2
+ return (model.start?.min !== model.end?.min || model.start?.max !== model.end?.max);
3
+ }
@@ -0,0 +1,51 @@
1
+ import { isSeasonMonth, SEASON_MONTH_MAP } from '../parse/simpleDate/maps';
2
+ import { isRange } from '../helpers/types';
3
+ export function normalize(model) {
4
+ const startDate = normalizeSimple(model.start);
5
+ const endDate = normalizeSimple(model.end);
6
+ if (isRange(model)) {
7
+ if (startDate === null) {
8
+ return `${model.approximate ? 'approximately ' : ''}before ${endDate}`;
9
+ }
10
+ if (endDate === null) {
11
+ return `${model.approximate ? 'approximately ' : ''}after ${startDate}`;
12
+ }
13
+ if (model.approximate) {
14
+ return `between ${startDate} and ${endDate}`;
15
+ }
16
+ else {
17
+ return `from ${startDate} to ${endDate}`;
18
+ }
19
+ }
20
+ else {
21
+ return `${model.approximate ? 'about ' : ''}${startDate}`;
22
+ }
23
+ }
24
+ function normalizeSimple(input) {
25
+ if (input === null)
26
+ return null;
27
+ const precision = input.precision;
28
+ const date = input.min;
29
+ const options = { timeZone: 'UTC' };
30
+ switch (precision) {
31
+ case 'Year':
32
+ options.year = 'numeric';
33
+ break;
34
+ case 'Season': {
35
+ const month = date.getUTCMonth() + 1;
36
+ if (!isSeasonMonth(month))
37
+ return '';
38
+ return `${SEASON_MONTH_MAP[month]} ${date.getUTCFullYear()}`;
39
+ }
40
+ case 'Month':
41
+ options.month = 'long';
42
+ options.year = 'numeric';
43
+ break;
44
+ case 'Day':
45
+ options.day = 'numeric';
46
+ options.month = 'long';
47
+ options.year = 'numeric';
48
+ break;
49
+ }
50
+ return date.toLocaleDateString('en-GB', options);
51
+ }
@@ -0,0 +1,47 @@
1
+ export declare function parse(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: false;
12
+ error: "Invalid \"BETWEEN\" modifier.";
13
+ } | {
14
+ ok: false;
15
+ error: "Invalid \"FROM\" modifier.";
16
+ } | {
17
+ ok: true;
18
+ value: {
19
+ approximate: boolean;
20
+ start: {
21
+ readonly precision: "Year";
22
+ readonly min: Date;
23
+ readonly max: Date;
24
+ } | {
25
+ readonly precision: "Season" | "Month";
26
+ readonly min: Date;
27
+ readonly max: Date;
28
+ } | {
29
+ readonly precision: "Day";
30
+ readonly min: Date;
31
+ readonly max: Date;
32
+ } | null;
33
+ end: {
34
+ readonly precision: "Year";
35
+ readonly min: Date;
36
+ readonly max: Date;
37
+ } | {
38
+ readonly precision: "Season" | "Month";
39
+ readonly min: Date;
40
+ readonly max: Date;
41
+ } | {
42
+ readonly precision: "Day";
43
+ readonly min: Date;
44
+ readonly max: Date;
45
+ } | null;
46
+ };
47
+ };
@@ -0,0 +1,41 @@
1
+ import { ok } from '../helpers/result';
2
+ import { after, before, between, from, none } from './modifiers';
3
+ // Main Parse Function
4
+ export function parse(input) {
5
+ const cleanedInput = input
6
+ .toLowerCase() // lowercases
7
+ .replace(/(\d+)\s*(st|nd|rd|th|of)\b(\s*of)?/gi, '$1') // removes ordinal suffixes and 'of'
8
+ .replace(/[.,/\-_]+/g, ' ') // replaces separators with spaces
9
+ .replace(/\s+/g, ' ') // collapses multiple spaces to a single space
10
+ .trim(); // Removes trailing and leading spaces
11
+ let approximate = cleanedInput.startsWith('about ') ||
12
+ cleanedInput.startsWith('approximately ') ||
13
+ cleanedInput.startsWith('around ');
14
+ const removeApproximate = approximate
15
+ ? cleanedInput.slice(cleanedInput.indexOf(' ') + 1)
16
+ : cleanedInput;
17
+ let range;
18
+ if (removeApproximate.startsWith('before ')) {
19
+ range = before(removeApproximate);
20
+ }
21
+ else if (removeApproximate.startsWith('after ')) {
22
+ range = after(removeApproximate);
23
+ }
24
+ else if (removeApproximate.startsWith('between ')) {
25
+ range = between(removeApproximate);
26
+ approximate = true;
27
+ }
28
+ else if (removeApproximate.startsWith('from ')) {
29
+ range = from(removeApproximate);
30
+ }
31
+ else {
32
+ range = none(removeApproximate);
33
+ }
34
+ if (!range.ok)
35
+ return range;
36
+ return ok({
37
+ approximate,
38
+ start: range.value.start,
39
+ end: range.value.end,
40
+ });
41
+ }
@@ -0,0 +1,182 @@
1
+ export declare function none(rawInput: 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 start: {
14
+ readonly precision: "Year";
15
+ readonly min: Date;
16
+ readonly max: Date;
17
+ } | {
18
+ readonly precision: "Season" | "Month";
19
+ readonly min: Date;
20
+ readonly max: Date;
21
+ } | {
22
+ readonly precision: "Day";
23
+ readonly min: Date;
24
+ readonly max: Date;
25
+ };
26
+ readonly end: {
27
+ readonly precision: "Year";
28
+ readonly min: Date;
29
+ readonly max: Date;
30
+ } | {
31
+ readonly precision: "Season" | "Month";
32
+ readonly min: Date;
33
+ readonly max: Date;
34
+ } | {
35
+ readonly precision: "Day";
36
+ readonly min: Date;
37
+ readonly max: Date;
38
+ };
39
+ };
40
+ };
41
+ export declare function before(rawInput: string): {
42
+ ok: false;
43
+ error: "Year is required.";
44
+ } | {
45
+ ok: false;
46
+ error: "Unknown month.";
47
+ } | {
48
+ ok: false;
49
+ error: "Unknown date format.";
50
+ } | {
51
+ ok: true;
52
+ value: {
53
+ readonly start: null;
54
+ readonly end: {
55
+ readonly precision: "Year";
56
+ readonly min: Date;
57
+ readonly max: Date;
58
+ } | {
59
+ readonly precision: "Season" | "Month";
60
+ readonly min: Date;
61
+ readonly max: Date;
62
+ } | {
63
+ readonly precision: "Day";
64
+ readonly min: Date;
65
+ readonly max: Date;
66
+ };
67
+ };
68
+ };
69
+ export declare function after(rawInput: string): {
70
+ ok: false;
71
+ error: "Year is required.";
72
+ } | {
73
+ ok: false;
74
+ error: "Unknown month.";
75
+ } | {
76
+ ok: false;
77
+ error: "Unknown date format.";
78
+ } | {
79
+ ok: true;
80
+ value: {
81
+ readonly start: {
82
+ readonly precision: "Year";
83
+ readonly min: Date;
84
+ readonly max: Date;
85
+ } | {
86
+ readonly precision: "Season" | "Month";
87
+ readonly min: Date;
88
+ readonly max: Date;
89
+ } | {
90
+ readonly precision: "Day";
91
+ readonly min: Date;
92
+ readonly max: Date;
93
+ };
94
+ readonly end: null;
95
+ };
96
+ };
97
+ export declare function between(rawInput: string): {
98
+ ok: false;
99
+ error: "Year is required.";
100
+ } | {
101
+ ok: false;
102
+ error: "Unknown month.";
103
+ } | {
104
+ ok: false;
105
+ error: "Unknown date format.";
106
+ } | {
107
+ ok: false;
108
+ error: "Invalid \"BETWEEN\" modifier.";
109
+ } | {
110
+ ok: true;
111
+ value: {
112
+ readonly start: {
113
+ readonly precision: "Year";
114
+ readonly min: Date;
115
+ readonly max: Date;
116
+ } | {
117
+ readonly precision: "Season" | "Month";
118
+ readonly min: Date;
119
+ readonly max: Date;
120
+ } | {
121
+ readonly precision: "Day";
122
+ readonly min: Date;
123
+ readonly max: Date;
124
+ };
125
+ readonly end: {
126
+ readonly precision: "Year";
127
+ readonly min: Date;
128
+ readonly max: Date;
129
+ } | {
130
+ readonly precision: "Season" | "Month";
131
+ readonly min: Date;
132
+ readonly max: Date;
133
+ } | {
134
+ readonly precision: "Day";
135
+ readonly min: Date;
136
+ readonly max: Date;
137
+ };
138
+ };
139
+ };
140
+ export declare function from(rawInput: string): {
141
+ ok: false;
142
+ error: "Year is required.";
143
+ } | {
144
+ ok: false;
145
+ error: "Unknown month.";
146
+ } | {
147
+ ok: false;
148
+ error: "Unknown date format.";
149
+ } | {
150
+ ok: false;
151
+ error: "Invalid \"FROM\" modifier.";
152
+ } | {
153
+ ok: true;
154
+ value: {
155
+ readonly start: {
156
+ readonly precision: "Year";
157
+ readonly min: Date;
158
+ readonly max: Date;
159
+ } | {
160
+ readonly precision: "Season" | "Month";
161
+ readonly min: Date;
162
+ readonly max: Date;
163
+ } | {
164
+ readonly precision: "Day";
165
+ readonly min: Date;
166
+ readonly max: Date;
167
+ };
168
+ readonly end: {
169
+ readonly precision: "Year";
170
+ readonly min: Date;
171
+ readonly max: Date;
172
+ } | {
173
+ readonly precision: "Season" | "Month";
174
+ readonly min: Date;
175
+ readonly max: Date;
176
+ } | {
177
+ readonly precision: "Day";
178
+ readonly min: Date;
179
+ readonly max: Date;
180
+ };
181
+ };
182
+ };
@@ -0,0 +1,62 @@
1
+ import { err, ok } from '../helpers/result';
2
+ import { parseSimpleDate } from './simpleDate/parse';
3
+ export function none(rawInput) {
4
+ const result = parseSimpleDate(rawInput);
5
+ if (!result.ok)
6
+ return result;
7
+ const date = result.value;
8
+ return ok({
9
+ start: date,
10
+ end: date,
11
+ });
12
+ }
13
+ export function before(rawInput) {
14
+ const input = rawInput.slice('before '.length);
15
+ const result = parseSimpleDate(input);
16
+ if (!result.ok)
17
+ return result;
18
+ return ok({
19
+ start: null,
20
+ end: result.value,
21
+ });
22
+ }
23
+ export function after(rawInput) {
24
+ const input = rawInput.slice('after '.length);
25
+ const result = parseSimpleDate(input);
26
+ if (!result.ok)
27
+ return result;
28
+ return ok({
29
+ start: result.value,
30
+ end: null,
31
+ });
32
+ }
33
+ export function between(rawInput) {
34
+ const dates = rawInput.slice('between '.length).split(' and ');
35
+ if (dates.length !== 2)
36
+ return err('Invalid "BETWEEN" modifier.');
37
+ const startResult = parseSimpleDate(dates[0]);
38
+ if (!startResult.ok)
39
+ return startResult;
40
+ const endResult = parseSimpleDate(dates[1]);
41
+ if (!endResult.ok)
42
+ return endResult;
43
+ return ok({
44
+ start: startResult.value,
45
+ end: endResult.value,
46
+ });
47
+ }
48
+ export function from(rawInput) {
49
+ const dates = rawInput.slice('from '.length).split(' to ');
50
+ if (dates.length !== 2)
51
+ return err('Invalid "FROM" modifier.');
52
+ const startResult = parseSimpleDate(dates[0]);
53
+ if (!startResult.ok)
54
+ return startResult;
55
+ const endResult = parseSimpleDate(dates[1]);
56
+ if (!endResult.ok)
57
+ return endResult;
58
+ return ok({
59
+ start: startResult.value,
60
+ end: endResult.value,
61
+ });
62
+ }
@@ -10,9 +10,9 @@ export declare const year: (rawDate: string) => {
10
10
  } | {
11
11
  ok: true;
12
12
  value: {
13
- readonly format: "YYYY";
14
- readonly minDate: Date;
15
- readonly maxDate: Date;
13
+ readonly precision: "Year";
14
+ readonly min: Date;
15
+ readonly max: Date;
16
16
  };
17
17
  } | null;
18
18
  export declare const monthStringYear: (rawDate: string) => {
@@ -27,9 +27,9 @@ export declare const monthStringYear: (rawDate: string) => {
27
27
  } | {
28
28
  ok: true;
29
29
  value: {
30
- readonly format: "SEASON_YYYY" | "MMMM_YYYY";
31
- readonly minDate: Date;
32
- readonly maxDate: Date;
30
+ readonly precision: "Season" | "Month";
31
+ readonly min: Date;
32
+ readonly max: Date;
33
33
  };
34
34
  } | null;
35
35
  export declare const yearMonthString: (rawDate: string) => {
@@ -44,9 +44,9 @@ export declare const yearMonthString: (rawDate: string) => {
44
44
  } | {
45
45
  ok: true;
46
46
  value: {
47
- readonly format: "SEASON_YYYY" | "MMMM_YYYY";
48
- readonly minDate: Date;
49
- readonly maxDate: Date;
47
+ readonly precision: "Season" | "Month";
48
+ readonly min: Date;
49
+ readonly max: Date;
50
50
  };
51
51
  } | null;
52
52
  export declare const dayMonthStringYear: (rawDate: string) => {
@@ -61,9 +61,9 @@ export declare const dayMonthStringYear: (rawDate: string) => {
61
61
  } | {
62
62
  ok: true;
63
63
  value: {
64
- readonly format: "D_MMMM_YYYY";
65
- readonly minDate: Date;
66
- readonly maxDate: Date;
64
+ readonly precision: "Day";
65
+ readonly min: Date;
66
+ readonly max: Date;
67
67
  };
68
68
  } | null;
69
69
  export declare const monthStringDayYear: (rawDate: string) => {
@@ -78,9 +78,9 @@ export declare const monthStringDayYear: (rawDate: string) => {
78
78
  } | {
79
79
  ok: true;
80
80
  value: {
81
- readonly format: "D_MMMM_YYYY";
82
- readonly minDate: Date;
83
- readonly maxDate: Date;
81
+ readonly precision: "Day";
82
+ readonly min: Date;
83
+ readonly max: Date;
84
84
  };
85
85
  } | null;
86
86
  export declare const yearMonthStringDay: (rawDate: string) => {
@@ -95,9 +95,9 @@ export declare const yearMonthStringDay: (rawDate: string) => {
95
95
  } | {
96
96
  ok: true;
97
97
  value: {
98
- readonly format: "D_MMMM_YYYY";
99
- readonly minDate: Date;
100
- readonly maxDate: Date;
98
+ readonly precision: "Day";
99
+ readonly min: Date;
100
+ readonly max: Date;
101
101
  };
102
102
  } | null;
103
103
  export declare const yearDayMonthString: (rawDate: string) => {
@@ -112,9 +112,9 @@ export declare const yearDayMonthString: (rawDate: string) => {
112
112
  } | {
113
113
  ok: true;
114
114
  value: {
115
- readonly format: "D_MMMM_YYYY";
116
- readonly minDate: Date;
117
- readonly maxDate: Date;
115
+ readonly precision: "Day";
116
+ readonly min: Date;
117
+ readonly max: Date;
118
118
  };
119
119
  } | null;
120
120
  export declare const monthDigitYear: (rawDate: string) => {
@@ -129,9 +129,9 @@ export declare const monthDigitYear: (rawDate: string) => {
129
129
  } | {
130
130
  ok: true;
131
131
  value: {
132
- readonly format: "MMMM_YYYY";
133
- readonly minDate: Date;
134
- readonly maxDate: Date;
132
+ readonly precision: "Month";
133
+ readonly min: Date;
134
+ readonly max: Date;
135
135
  };
136
136
  } | null;
137
137
  export declare const yearMonthDigit: (rawDate: string) => {
@@ -146,9 +146,9 @@ export declare const yearMonthDigit: (rawDate: string) => {
146
146
  } | {
147
147
  ok: true;
148
148
  value: {
149
- readonly format: "MMMM_YYYY";
150
- readonly minDate: Date;
151
- readonly maxDate: Date;
149
+ readonly precision: "Month";
150
+ readonly min: Date;
151
+ readonly max: Date;
152
152
  };
153
153
  } | null;
154
154
  export declare const dayMonthDigitYear: (rawDate: string) => {
@@ -163,9 +163,9 @@ export declare const dayMonthDigitYear: (rawDate: string) => {
163
163
  } | {
164
164
  ok: true;
165
165
  value: {
166
- readonly format: "D_MMMM_YYYY";
167
- readonly minDate: Date;
168
- readonly maxDate: Date;
166
+ readonly precision: "Day";
167
+ readonly min: Date;
168
+ readonly max: Date;
169
169
  };
170
170
  } | null;
171
171
  export declare const yearMonthDigitDay: (rawDate: string) => {
@@ -180,25 +180,8 @@ export declare const yearMonthDigitDay: (rawDate: string) => {
180
180
  } | {
181
181
  ok: true;
182
182
  value: {
183
- readonly format: "D_MMMM_YYYY";
184
- readonly minDate: Date;
185
- readonly maxDate: Date;
186
- };
187
- } | null;
188
- export declare const decade: (rawDate: string) => {
189
- ok: false;
190
- error: "Year is required.";
191
- } | {
192
- ok: false;
193
- error: "Unknown month.";
194
- } | {
195
- ok: false;
196
- error: "Unknown date format.";
197
- } | {
198
- ok: true;
199
- value: {
200
- readonly format: "YYYYs";
201
- readonly minDate: Date;
202
- readonly maxDate: Date;
183
+ readonly precision: "Day";
184
+ readonly min: Date;
185
+ readonly max: Date;
203
186
  };
204
187
  } | null;