@autometa/datetime 0.1.15 → 1.0.0-rc.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.
package/dist/index.js CHANGED
@@ -1,605 +1,473 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var __accessCheck = (obj, member, msg) => {
20
- if (!member.has(obj))
21
- throw TypeError("Cannot " + msg);
22
- };
23
- var __privateGet = (obj, member, getter) => {
24
- __accessCheck(obj, member, "read from private field");
25
- return getter ? getter.call(obj) : member.get(obj);
26
- };
27
- var __privateAdd = (obj, member, value) => {
28
- if (member.has(obj))
29
- throw TypeError("Cannot add the same private member more than once");
30
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
31
- };
32
- var __privateMethod = (obj, member, method) => {
33
- __accessCheck(obj, member, "access private method");
34
- return method;
35
- };
36
-
37
- // src/index.ts
38
- var src_exports = {};
39
- __export(src_exports, {
40
- DateFactory: () => DateFactory,
41
- Dates: () => Dates,
42
- FmtDateFactory: () => FmtDateFactory,
43
- IsoDateFactory: () => IsoDateFactory,
44
- Time: () => Time,
45
- TimeDiff: () => TimeDiff,
46
- TimeObject: () => TimeObject
47
- });
48
- module.exports = __toCommonJS(src_exports);
1
+ import { AutomationError } from '@autometa/errors';
2
+ import { isValidDate, isValidISODateString, isValidTime, isValidYearMonth } from 'iso-datestring-validator';
3
+ import { assertKey } from '@autometa/asserters';
49
4
 
50
5
  // src/dates/date-factory.ts
51
- var import_phrases = require("@autometa/phrases");
52
6
 
53
- // src/dates/midnight.ts
54
- function midnight() {
55
- const date = /* @__PURE__ */ new Date();
56
- date.setDate(date.getDate() + 1);
57
- date.setHours(0, 0, 0, 0);
58
- return date;
7
+ // src/dates/clock.ts
8
+ var systemClock = {
9
+ now: () => /* @__PURE__ */ new Date()
10
+ };
11
+ function cloneDate(date) {
12
+ return new Date(date.getTime());
13
+ }
14
+ var UNIT_ALIASES = {
15
+ millisecond: { unit: "milliseconds", scale: 1 },
16
+ milliseconds: { unit: "milliseconds", scale: 1 },
17
+ ms: { unit: "milliseconds", scale: 1 },
18
+ second: { unit: "seconds", scale: 1 },
19
+ seconds: { unit: "seconds", scale: 1 },
20
+ sec: { unit: "seconds", scale: 1 },
21
+ secs: { unit: "seconds", scale: 1 },
22
+ minute: { unit: "minutes", scale: 1 },
23
+ minutes: { unit: "minutes", scale: 1 },
24
+ min: { unit: "minutes", scale: 1 },
25
+ mins: { unit: "minutes", scale: 1 },
26
+ hour: { unit: "hours", scale: 1 },
27
+ hours: { unit: "hours", scale: 1 },
28
+ hr: { unit: "hours", scale: 1 },
29
+ hrs: { unit: "hours", scale: 1 },
30
+ day: { unit: "days", scale: 1 },
31
+ days: { unit: "days", scale: 1 },
32
+ week: { unit: "weeks", scale: 1 },
33
+ weeks: { unit: "weeks", scale: 1 },
34
+ fortnight: { unit: "days", scale: 14 },
35
+ fortnights: { unit: "days", scale: 14 },
36
+ month: { unit: "months", scale: 1 },
37
+ months: { unit: "months", scale: 1 },
38
+ year: { unit: "years", scale: 1 },
39
+ years: { unit: "years", scale: 1 }
40
+ };
41
+ function resolveTimeUnit(input) {
42
+ const key = input.trim().toLowerCase();
43
+ const resolved = UNIT_ALIASES[key];
44
+ if (!resolved) {
45
+ throw new AutomationError(
46
+ `Unsupported time unit '${input}'. Expected one of ${Object.keys(UNIT_ALIASES).sort().join(", ")}.`
47
+ );
48
+ }
49
+ return resolved;
50
+ }
51
+
52
+ // src/shared/phrases.ts
53
+ var SEPARATORS = /[\s_-]+/g;
54
+ function capitalize(word) {
55
+ return word.charAt(0).toUpperCase() + word.slice(1);
56
+ }
57
+ function splitWords(phrase) {
58
+ return phrase.trim().split(SEPARATORS).filter(Boolean).map((word) => word.toLowerCase());
59
+ }
60
+ function toCamelKey(phrase) {
61
+ const words = splitWords(phrase);
62
+ if (words.length === 0) {
63
+ return "";
64
+ }
65
+ const [first, ...rest] = words;
66
+ return first + rest.map(capitalize).join("");
67
+ }
68
+ function normalizeToken(phrase) {
69
+ return splitWords(phrase).join("");
59
70
  }
60
71
 
61
72
  // src/dates/date-factory.ts
62
- var import_errors = require("@autometa/errors");
63
- var import_asserters = require("@autometa/asserters");
64
- var import_iso_datestring_validator = require("iso-datestring-validator");
65
- function isTimeShortcut(text) {
66
- const converted = (0, import_phrases.convertPhrase)(text, import_phrases.camel);
67
- const includes = [
68
- "now",
69
- "beforeYesterday",
70
- "yesterday",
71
- "today",
72
- "tomorrow",
73
- "afterTomorrow",
74
- "midnight",
75
- "lastWeek",
76
- "nextWeek",
77
- "lastFortnight",
78
- "nextFortnight"
79
- ].includes(converted);
80
- if (!includes) {
81
- return false;
82
- }
83
- return true;
73
+ var SHORTCUT_LOOKUP = /* @__PURE__ */ new Set([
74
+ "now",
75
+ "beforeYesterday",
76
+ "yesterday",
77
+ "today",
78
+ "tomorrow",
79
+ "afterTomorrow",
80
+ "midnight",
81
+ "lastWeek",
82
+ "nextWeek",
83
+ "lastFortnight",
84
+ "nextFortnight"
85
+ ]);
86
+ var FUTURE_PATTERN = /^(\d+)\s+([A-Za-z\s]+?)\s+from\s+now$/i;
87
+ var PAST_PATTERN = /^(\d+)\s+([A-Za-z\s]+?)\s+ago$/i;
88
+ function invalidDate() {
89
+ return new Date(Number.NaN);
90
+ }
91
+ function isDateShortcut(value) {
92
+ return SHORTCUT_LOOKUP.has(value);
84
93
  }
85
- var _extractTimeFromPhrase, extractTimeFromPhrase_fn, _extractFutureFromPhrase, extractFutureFromPhrase_fn, _extractPastFromPhrase, extractPastFromPhrase_fn, _addYears, addYears_fn, _addMonths, addMonths_fn, _addWeeks, addWeeks_fn, _addDays, addDays_fn, _addSeconds, addSeconds_fn, _addMilliseconds, addMilliseconds_fn, _addHours, addHours_fn, _addMinutes, addMinutes_fn;
86
94
  var DateFactory = class {
87
- constructor() {
88
- __privateAdd(this, _extractTimeFromPhrase);
89
- __privateAdd(this, _extractFutureFromPhrase);
90
- __privateAdd(this, _extractPastFromPhrase);
91
- __privateAdd(this, _addYears);
92
- __privateAdd(this, _addMonths);
93
- __privateAdd(this, _addWeeks);
94
- __privateAdd(this, _addDays);
95
- __privateAdd(this, _addSeconds);
96
- __privateAdd(this, _addMilliseconds);
97
- __privateAdd(this, _addHours);
98
- __privateAdd(this, _addMinutes);
99
- this.phraseMap = /* @__PURE__ */ new Map([
100
- ["now", () => this.make(0, "days")],
101
- ["beforeYesterday", () => this.make(-2, "days")],
102
- ["yesterday", () => this.make(-1, "days")],
103
- ["today", () => this.make(0, "days")],
104
- ["tomorrow", () => this.make(1, "days")],
105
- ["afterTomorrow", () => this.make(2, "days")],
106
- ["nextWeek", () => this.make(7, "days")],
107
- ["lastWeek", () => this.make(-7, "days")],
108
- ["nextFortnight", () => this.make(14, "days")],
109
- ["lastFortnight", () => this.make(-14, "days")],
110
- ["midnight", midnight]
111
- ]);
112
- }
113
- // eslint-disable-next-line @typescript-eslint/ban-types
114
- find(name) {
115
- const result = this.phraseMap.get(name);
116
- if (result) {
117
- return result();
95
+ constructor(options = {}) {
96
+ this.clock = options.clock ?? systemClock;
97
+ }
98
+ find(shortcut) {
99
+ switch (shortcut) {
100
+ case "now":
101
+ return this.currentDate();
102
+ case "beforeYesterday":
103
+ return this.make(-2, "days");
104
+ case "yesterday":
105
+ return this.make(-1, "days");
106
+ case "today":
107
+ return this.make(0, "days");
108
+ case "tomorrow":
109
+ return this.make(1, "days");
110
+ case "afterTomorrow":
111
+ return this.make(2, "days");
112
+ case "midnight":
113
+ return this.midnight();
114
+ case "lastWeek":
115
+ return this.make(-1, "weeks");
116
+ case "nextWeek":
117
+ return this.make(1, "weeks");
118
+ case "lastFortnight":
119
+ return this.make(-14, "days");
120
+ case "nextFortnight":
121
+ return this.make(14, "days");
122
+ default:
123
+ throw new AutomationError(
124
+ `Unsupported date shortcut '${shortcut}'.`
125
+ );
118
126
  }
119
- (0, import_errors.raise)(
120
- `Could not find date shortcut ${name}. Please use a valid date shortcut from the following list: ${Array.from(
121
- this.phraseMap.keys()
122
- ).join(", ")}`
123
- );
124
127
  }
125
128
  fromPhraseSafe(phrase) {
126
- const name = (0, import_phrases.convertPhrase)(phrase, import_phrases.camel);
127
- if (isTimeShortcut(name)) {
128
- if (this.phraseMap.has(name)) {
129
- return this.phraseMap.get(name)?.call(this);
130
- }
129
+ if (typeof phrase !== "string") {
130
+ return invalidDate();
131
+ }
132
+ const trimmed = phrase.trim();
133
+ if (trimmed.length === 0) {
134
+ return invalidDate();
131
135
  }
132
- const matchDay = __privateMethod(this, _extractTimeFromPhrase, extractTimeFromPhrase_fn).call(this, phrase);
133
- if (matchDay) {
134
- return matchDay;
136
+ const shortcutCandidate = toCamelKey(trimmed);
137
+ if (isDateShortcut(shortcutCandidate)) {
138
+ return this.find(shortcutCandidate);
135
139
  }
136
- const first = (0, import_iso_datestring_validator.isValidDate)(phrase);
137
- const second = (0, import_iso_datestring_validator.isValidISODateString)(phrase);
138
- const third = (0, import_iso_datestring_validator.isValidTime)(phrase);
139
- const fourth = (0, import_iso_datestring_validator.isValidYearMonth)(phrase);
140
- if (!first && !second && !third && !fourth) {
141
- return /* @__PURE__ */ new Date("null");
140
+ const relative = this.extractRelative(trimmed);
141
+ if (relative) {
142
+ return this.make(relative.offset, relative.unit);
142
143
  }
143
- const parsed = new Date(phrase);
144
- if (isNaN(parsed.getTime())) {
145
- return new Date(Date.parse(phrase));
144
+ if (this.isDateLike(trimmed)) {
145
+ return this.parseDate(trimmed);
146
146
  }
147
- return parsed;
147
+ return invalidDate();
148
148
  }
149
149
  fromPhrase(phrase) {
150
150
  const result = this.fromPhraseSafe(phrase);
151
- if (result) {
152
- return result;
151
+ if (Number.isNaN(result.getTime())) {
152
+ throw new AutomationError(
153
+ `Could not parse date from phrase '${String(phrase)}'. Valid shortcuts are: ${[...SHORTCUT_LOOKUP].join(", ")}.`
154
+ );
153
155
  }
154
- (0, import_errors.raise)(
155
- `Could not parse date from phrase ${phrase}. Please use a valid date string or a phrase from the following list: ${Array.from(
156
- this.phraseMap.keys()
157
- ).join(", ")}`
158
- );
156
+ return result;
159
157
  }
160
- make(timeOffset, timeunit) {
161
- const unit = timeunit.endsWith("s") ? timeunit : `${timeunit}s`;
158
+ make(timeOffset, timeUnit) {
159
+ if (!Number.isFinite(timeOffset)) {
160
+ throw new AutomationError(
161
+ `Time offset must be finite. Received '${timeOffset}'.`
162
+ );
163
+ }
164
+ const { unit, scale } = resolveTimeUnit(String(timeUnit));
165
+ const offset = timeOffset * scale;
166
+ const date = this.currentDate();
162
167
  switch (unit) {
163
168
  case "years":
164
- return __privateMethod(this, _addYears, addYears_fn).call(this, timeOffset);
169
+ date.setFullYear(date.getFullYear() + offset);
170
+ break;
165
171
  case "months":
166
- return __privateMethod(this, _addMonths, addMonths_fn).call(this, timeOffset);
172
+ date.setMonth(date.getMonth() + offset);
173
+ break;
167
174
  case "weeks":
168
- return __privateMethod(this, _addWeeks, addWeeks_fn).call(this, timeOffset);
175
+ date.setDate(date.getDate() + offset * 7);
176
+ break;
169
177
  case "days":
170
- return __privateMethod(this, _addDays, addDays_fn).call(this, timeOffset);
171
- case "seconds":
172
- return __privateMethod(this, _addSeconds, addSeconds_fn).call(this, timeOffset);
173
- case "milliseconds":
174
- return __privateMethod(this, _addMilliseconds, addMilliseconds_fn).call(this, timeOffset);
178
+ date.setDate(date.getDate() + offset);
179
+ break;
175
180
  case "hours":
176
- return __privateMethod(this, _addHours, addHours_fn).call(this, timeOffset);
181
+ date.setHours(date.getHours() + offset);
182
+ break;
177
183
  case "minutes":
178
- return __privateMethod(this, _addMinutes, addMinutes_fn).call(this, timeOffset);
184
+ date.setMinutes(date.getMinutes() + offset);
185
+ break;
186
+ case "seconds":
187
+ date.setSeconds(date.getSeconds() + offset);
188
+ break;
189
+ case "milliseconds":
190
+ date.setMilliseconds(date.getMilliseconds() + offset);
191
+ break;
179
192
  default:
180
- throw new import_errors.AutomationError(
181
- `Invalid timeunit ${timeunit}, options are 'days', 'seconds', 'milliseconds', 'hours', 'minutes'. Non plural equivalents such as 'day' or 'week' are also accepted.`
182
- );
193
+ throw new AutomationError(`Unsupported time unit '${unit}'.`);
183
194
  }
195
+ return date;
184
196
  }
185
- };
186
- _extractTimeFromPhrase = new WeakSet();
187
- extractTimeFromPhrase_fn = function(phrase) {
188
- return __privateMethod(this, _extractFutureFromPhrase, extractFutureFromPhrase_fn).call(this, phrase) ?? __privateMethod(this, _extractPastFromPhrase, extractPastFromPhrase_fn).call(this, phrase);
189
- };
190
- _extractFutureFromPhrase = new WeakSet();
191
- extractFutureFromPhrase_fn = function(phrase) {
192
- const pastPattern = /^(\d+) (.*)s? from now?/;
193
- const pastMatch = pastPattern.exec(phrase);
194
- if ((0, import_asserters.ConfirmDefined)(pastMatch) && (0, import_asserters.ConfirmLengthAtLeast)(pastMatch, 3)) {
195
- const [_, value, unit] = pastMatch;
196
- const timeunit = (0, import_phrases.convertPhrase)(unit, import_phrases.collapse);
197
- return this.make(Number(value), timeunit);
197
+ currentDate() {
198
+ return cloneDate(this.clock.now());
198
199
  }
199
- };
200
- _extractPastFromPhrase = new WeakSet();
201
- extractPastFromPhrase_fn = function(phrase) {
202
- const pastPattern = /^(\d+) (.*)(s)? ago?/gm;
203
- const pastMatch = pastPattern.exec(phrase);
204
- if ((0, import_asserters.ConfirmDefined)(pastMatch) && (0, import_asserters.ConfirmLengthAtLeast)(pastMatch, 3)) {
205
- const [_, value, unit] = pastMatch;
206
- const timeunit = (0, import_phrases.convertPhrase)(unit, import_phrases.camel);
207
- return this.make(-Number(value), timeunit);
200
+ midnight() {
201
+ const date = this.currentDate();
202
+ date.setUTCDate(date.getUTCDate() + 1);
203
+ date.setUTCHours(0, 0, 0, 0);
204
+ return date;
205
+ }
206
+ parseDate(value) {
207
+ const parsed = new Date(value);
208
+ if (!Number.isNaN(parsed.getTime())) {
209
+ return parsed;
210
+ }
211
+ return new Date(Date.parse(value));
212
+ }
213
+ isDateLike(value) {
214
+ if (!Number.isNaN(new Date(value).getTime())) {
215
+ return true;
216
+ }
217
+ return isValidDate(value) || isValidISODateString(value) || isValidTime(value) || isValidYearMonth(value);
218
+ }
219
+ extractRelative(phrase) {
220
+ const futureMatch = FUTURE_PATTERN.exec(phrase);
221
+ if (futureMatch) {
222
+ return this.buildRelativeMatch(futureMatch, 1);
223
+ }
224
+ const pastMatch = PAST_PATTERN.exec(phrase);
225
+ if (pastMatch) {
226
+ return this.buildRelativeMatch(pastMatch, -1);
227
+ }
228
+ return void 0;
229
+ }
230
+ buildRelativeMatch(match, direction) {
231
+ const value = match[1];
232
+ const unit = match[2];
233
+ if (!value || !unit) {
234
+ return void 0;
235
+ }
236
+ const magnitude = Number.parseInt(value, 10);
237
+ if (!Number.isFinite(magnitude)) {
238
+ return void 0;
239
+ }
240
+ const convertedUnit = normalizeToken(unit);
241
+ const { unit: normalisedUnit, scale } = resolveTimeUnit(convertedUnit);
242
+ const offset = magnitude * scale * direction;
243
+ return {
244
+ offset,
245
+ unit: normalisedUnit
246
+ };
208
247
  }
209
- };
210
- _addYears = new WeakSet();
211
- addYears_fn = function(yearsOffset) {
212
- const date = /* @__PURE__ */ new Date();
213
- date.setFullYear(date.getFullYear() + yearsOffset);
214
- return date;
215
- };
216
- _addMonths = new WeakSet();
217
- addMonths_fn = function(monthOffset) {
218
- const date = /* @__PURE__ */ new Date();
219
- date.setMonth(date.getMonth() + monthOffset);
220
- return date;
221
- };
222
- _addWeeks = new WeakSet();
223
- addWeeks_fn = function(weekOffset) {
224
- const date = /* @__PURE__ */ new Date();
225
- date.setMonth(date.getMonth() + weekOffset / 4);
226
- return date;
227
- };
228
- _addDays = new WeakSet();
229
- addDays_fn = function(daysOffset) {
230
- const date = /* @__PURE__ */ new Date();
231
- date.setDate(date.getDate() + daysOffset);
232
- return date;
233
- };
234
- _addSeconds = new WeakSet();
235
- addSeconds_fn = function(secondsOffset) {
236
- const date = /* @__PURE__ */ new Date();
237
- date.setSeconds(date.getSeconds() + secondsOffset);
238
- return date;
239
- };
240
- _addMilliseconds = new WeakSet();
241
- addMilliseconds_fn = function(millisecondsOffset) {
242
- const date = /* @__PURE__ */ new Date();
243
- date.setMilliseconds(date.getMilliseconds() + millisecondsOffset);
244
- return date;
245
- };
246
- _addHours = new WeakSet();
247
- addHours_fn = function(hoursOffset) {
248
- const date = /* @__PURE__ */ new Date();
249
- date.setHours(date.getHours() + hoursOffset);
250
- return date;
251
- };
252
- _addMinutes = new WeakSet();
253
- addMinutes_fn = function(minutesOffset) {
254
- const date = /* @__PURE__ */ new Date();
255
- date.setMinutes(date.getMinutes() + minutesOffset);
256
- return date;
257
248
  };
258
249
 
259
- // src/dates/iso-date-factory.ts
260
- var IsoDateFactory = class {
261
- constructor(dateFactory) {
250
+ // src/dates/formatted-date-factory.ts
251
+ var FormattedDateFactory = class {
252
+ constructor(dateFactory, options = {}) {
262
253
  this.dateFactory = dateFactory;
263
- this.phraseMap = /* @__PURE__ */ new Map([
264
- ["beforeYesterday", this.make(-2, "days")],
265
- ["yesterday", this.make(-1, "days")],
266
- ["today", this.make(0, "days")],
267
- ["tomorrow", this.make(1, "days")],
268
- ["afterTomorrow", this.make(2, "days")],
269
- ["nextWeek", this.make(7, "days")],
270
- ["lastWeek", this.make(-7, "days")],
271
- ["nextFortnight", this.make(14, "days")],
272
- ["lastFortnight", this.make(-14, "days")],
273
- ["midnight", midnight().toISOString()]
274
- ]);
275
- }
276
- make(daysOffset, timeunit) {
277
- return this.dateFactory.make(daysOffset, timeunit).toISOString();
278
- }
279
- /**
280
- * Attempts to parse a phrase into a date.
281
- * @param phrase
282
- * @returns
283
- */
254
+ this.format = options.formatter ?? ((date) => date.toISOString().substring(0, 10));
255
+ }
256
+ make(offset, unit) {
257
+ return this.format(this.dateFactory.make(offset, unit));
258
+ }
284
259
  fromPhrase(phrase) {
285
- return this.dateFactory.fromPhrase(phrase)?.toISOString();
260
+ return this.format(this.dateFactory.fromPhrase(phrase));
286
261
  }
287
- /**
288
- * Returns the date and time of the day before yesterday
289
- */
290
262
  get beforeYesterday() {
291
- return this.phraseMap.get("beforeYesterday");
263
+ return this.fromShortcut("beforeYesterday");
292
264
  }
293
- /**
294
- * Returns the date and time of yesterday
295
- */
296
265
  get yesterday() {
297
- return this.phraseMap.get("yesterday");
266
+ return this.fromShortcut("yesterday");
298
267
  }
299
- /**
300
- * Returns the date and time of today
301
- */
302
268
  get today() {
303
- return this.phraseMap.get("today");
269
+ return this.fromShortcut("today");
304
270
  }
305
- /**
306
- * Returns the date and time of tomorrow
307
- */
308
271
  get tomorrow() {
309
- return this.phraseMap.get("tomorrow");
272
+ return this.fromShortcut("tomorrow");
310
273
  }
311
- /**
312
- * Returns the date and time of the day after tomorrow
313
- */
314
274
  get afterTomorrow() {
315
- return this.phraseMap.get("afterTomorrow");
275
+ return this.fromShortcut("afterTomorrow");
316
276
  }
317
- /**
318
- * Returns the date and time of midnight today
319
- */
320
277
  get midnight() {
321
- return this.phraseMap.get("midnight");
278
+ return this.fromShortcut("midnight");
322
279
  }
323
- /**
324
- * Returns the date and time of today 1 week ago
325
- */
326
280
  get lastWeek() {
327
- return this.phraseMap.get("lastWeek");
281
+ return this.fromShortcut("lastWeek");
328
282
  }
329
- /**
330
- * Returns the date and time of today 1 week from now
331
- */
332
283
  get nextWeek() {
333
- return this.phraseMap.get("nextWeek");
284
+ return this.fromShortcut("nextWeek");
334
285
  }
335
286
  get lastFortnight() {
336
- return this.phraseMap.get("lastFortnight");
287
+ return this.fromShortcut("lastFortnight");
337
288
  }
338
- /**
339
- * Returns the date and time of today 1 fortnight from now
340
- */
341
289
  get nextFortnight() {
342
- return this.phraseMap.get("nextFortnight");
290
+ return this.fromShortcut("nextFortnight");
291
+ }
292
+ fromShortcut(shortcut) {
293
+ return this.format(this.dateFactory.find(shortcut));
343
294
  }
344
295
  };
345
296
 
346
- // src/dates/formatted-date-factory.ts
347
- var FmtDateFactory = class {
348
- constructor(dateFactory) {
297
+ // src/dates/iso-date-factory.ts
298
+ var IsoDateFactory = class {
299
+ constructor(dateFactory, options = {}) {
349
300
  this.dateFactory = dateFactory;
350
- this.phraseMap = /* @__PURE__ */ new Map([
351
- ["beforeYesterday", this.make(-2, "days")],
352
- ["yesterday", this.make(-1, "days")],
353
- ["today", this.make(0, "days")],
354
- ["tomorrow", this.make(1, "days")],
355
- ["afterTomorrow", this.make(2, "days")],
356
- ["nextWeek", this.make(7, "days")],
357
- ["lastWeek", this.make(-7, "days")],
358
- ["nextFortnight", this.make(14, "days")],
359
- ["lastFortnight", this.make(-14, "days")],
360
- ["midnight", midnight().toISOString()]
361
- ]);
362
- }
363
- make(daysOffset, timeunit) {
364
- return this.dateFactory.make(daysOffset, timeunit).toISOString().split("T")[0];
365
- }
366
- /**
367
- * Attempts to parse a phrase into a date.
368
- * @param phrase
369
- * @returns
370
- */
301
+ this.serialise = options.serializer ?? ((date) => date.toISOString());
302
+ }
303
+ make(offset, unit) {
304
+ return this.serialise(this.dateFactory.make(offset, unit));
305
+ }
371
306
  fromPhrase(phrase) {
372
- return this.dateFactory.fromPhrase(phrase)?.toISOString().split("T")[0];
307
+ return this.serialise(this.dateFactory.fromPhrase(phrase));
373
308
  }
374
- /**
375
- * Returns the date and time of the day before yesterday
376
- */
377
309
  get beforeYesterday() {
378
- return this.phraseMap.get("beforeYesterday");
310
+ return this.fromShortcut("beforeYesterday");
379
311
  }
380
- /**
381
- * Returns the date and time of yesterday
382
- */
383
312
  get yesterday() {
384
- return this.phraseMap.get("yesterday");
313
+ return this.fromShortcut("yesterday");
385
314
  }
386
- /**
387
- * Returns the date and time of today
388
- */
389
315
  get today() {
390
- return this.phraseMap.get("today");
316
+ return this.fromShortcut("today");
391
317
  }
392
- /**
393
- * Returns the date and time of tomorrow
394
- */
395
318
  get tomorrow() {
396
- return this.phraseMap.get("tomorrow");
319
+ return this.fromShortcut("tomorrow");
397
320
  }
398
- /**
399
- * Returns the date and time of the day after tomorrow
400
- */
401
321
  get afterTomorrow() {
402
- return this.phraseMap.get("afterTomorrow");
322
+ return this.fromShortcut("afterTomorrow");
403
323
  }
404
- /**
405
- * Returns the date and time of midnight today
406
- */
407
324
  get midnight() {
408
- return this.phraseMap.get("midnight");
325
+ return this.fromShortcut("midnight");
409
326
  }
410
- /**
411
- * Returns the date and time of today 1 week ago
412
- */
413
327
  get lastWeek() {
414
- return this.phraseMap.get("lastWeek");
328
+ return this.fromShortcut("lastWeek");
415
329
  }
416
- /**
417
- * Returns the date and time of today 1 week from now
418
- */
419
330
  get nextWeek() {
420
- return this.phraseMap.get("nextWeek");
331
+ return this.fromShortcut("nextWeek");
421
332
  }
422
- /**
423
- * Returns the date and time of today 1 fortnight ago
424
- */
425
333
  get lastFortnight() {
426
- return this.phraseMap.get("lastFortnight");
334
+ return this.fromShortcut("lastFortnight");
427
335
  }
428
- /**
429
- * Returns the date and time of today 1 fortnight from now
430
- */
431
336
  get nextFortnight() {
432
- return this.phraseMap.get("nextFortnight");
337
+ return this.fromShortcut("nextFortnight");
338
+ }
339
+ fromShortcut(shortcut) {
340
+ return this.serialise(this.dateFactory.find(shortcut));
433
341
  }
434
342
  };
435
343
 
436
- // src/dates/dates.ts
437
- var _factory, _iso, _fmt;
438
- var DatesObject = class {
439
- constructor() {
440
- __privateAdd(this, _factory, new DateFactory());
441
- __privateAdd(this, _iso, new IsoDateFactory(__privateGet(this, _factory)));
442
- __privateAdd(this, _fmt, new FmtDateFactory(__privateGet(this, _factory)));
443
- }
444
- /**
445
- * Switches to the ISO factory, which offers an identical interface
446
- * but returns ISO strings instead of Date objects.
447
- *
448
- * Example: `2020-01-01T00:00:00.000Z`
449
- */
450
- get iso() {
451
- return __privateGet(this, _iso);
452
- }
453
- /**
454
- * Switches to the formatted factory, which offers an identical interface
455
- * but returns formatted strings instead of Date objects.
456
- *
457
- * Example: `2020-01-01`
458
- */
459
- get fmt() {
460
- return __privateGet(this, _fmt);
461
- }
462
- /**
463
- * Returns the current date and time.
464
- */
344
+ // src/dates/object.ts
345
+ var DatesFacade = class {
346
+ constructor(factory) {
347
+ this.factory = factory;
348
+ this.iso = new IsoDateFactory(factory);
349
+ this.fmt = new FormattedDateFactory(factory);
350
+ }
465
351
  get now() {
466
- return __privateGet(this, _factory).find("now");
352
+ return this.fromShortcut("now");
467
353
  }
468
- /**
469
- * Returns the date and time of the day before yesterday
470
- */
471
354
  get beforeYesterday() {
472
- return __privateGet(this, _factory).find("beforeYesterday");
355
+ return this.fromShortcut("beforeYesterday");
473
356
  }
474
- /**
475
- * Returns the date and time of yesterday
476
- */
477
357
  get yesterday() {
478
- return __privateGet(this, _factory).find("yesterday");
358
+ return this.fromShortcut("yesterday");
479
359
  }
480
- /**
481
- * Returns the date and time of today
482
- */
483
360
  get today() {
484
- return __privateGet(this, _factory).find("today");
361
+ return this.fromShortcut("today");
485
362
  }
486
- /**
487
- * Returns the date and time of tomorrow
488
- */
489
363
  get tomorrow() {
490
- return __privateGet(this, _factory).find("tomorrow");
364
+ return this.fromShortcut("tomorrow");
491
365
  }
492
- /**
493
- * Returns the date and time of the day after tomorrow
494
- */
495
366
  get afterTomorrow() {
496
- return __privateGet(this, _factory).find("afterTomorrow");
367
+ return this.fromShortcut("afterTomorrow");
497
368
  }
498
- /**
499
- * Returns the date and time of midnight today
500
- */
501
369
  get midnight() {
502
- return __privateGet(this, _factory).find("midnight");
370
+ return this.fromShortcut("midnight");
503
371
  }
504
- /**
505
- * Returns the date and time of today 1 week ago
506
- */
507
372
  get lastWeek() {
508
- return __privateGet(this, _factory).find("lastWeek");
373
+ return this.fromShortcut("lastWeek");
509
374
  }
510
- /**
511
- * Returns the date and time of today 1 week from now
512
- */
513
375
  get nextWeek() {
514
- return __privateGet(this, _factory).find("nextWeek");
376
+ return this.fromShortcut("nextWeek");
515
377
  }
516
- /**
517
- * Returns the date and time of today 2 weeks ago
518
- */
519
378
  get lastFortnight() {
520
- return __privateGet(this, _factory).find("lastFortnight");
379
+ return this.fromShortcut("lastFortnight");
521
380
  }
522
- /**
523
- * Returns the date and time of today 2 weeks from now
524
- */
525
381
  get nextFortnight() {
526
- return __privateGet(this, _factory).find("nextFortnight");
382
+ return this.fromShortcut("nextFortnight");
527
383
  }
528
- /**
529
- * Attempts to parse a phrase into a date.
530
- * @param phrase
531
- * @returns
532
- */
533
384
  fromPhrase(phrase) {
534
- return __privateGet(this, _factory).fromPhrase(phrase);
535
- }
536
- /**
537
- * Attempts to parse a phrase into a date.
538
- * If the phrase is invalid, it will return an invalid date instead of throwing.
539
- * @param phrase
540
- * @returns
541
- */
385
+ return this.factory.fromPhrase(phrase);
386
+ }
542
387
  fromPhraseSafe(phrase) {
543
- return __privateGet(this, _factory).fromPhraseSafe(phrase);
388
+ return this.factory.fromPhraseSafe(phrase);
544
389
  }
545
- make(timeOffset, timeUnit) {
546
- return __privateGet(this, _factory).make(timeOffset, timeUnit);
390
+ make(offset, unit) {
391
+ return this.factory.make(offset, unit);
392
+ }
393
+ fromShortcut(shortcut) {
394
+ return this.factory.find(shortcut);
547
395
  }
548
396
  };
549
- _factory = new WeakMap();
550
- _iso = new WeakMap();
551
- _fmt = new WeakMap();
552
- var Dates = new DatesObject();
553
-
554
- // src/time/time.ts
555
- var import_asserters2 = require("@autometa/asserters");
556
- var import_phrases2 = require("@autometa/phrases");
397
+ function createDates(options = {}) {
398
+ const factory = options.factory ?? new DateFactory(options);
399
+ return new DatesFacade(factory);
400
+ }
401
+ var Dates = createDates();
402
+ var SUPPORTED_METHODS = [
403
+ "minutes",
404
+ "seconds",
405
+ "millis",
406
+ "days",
407
+ "hours",
408
+ "weeks"
409
+ ];
410
+ function toTime(date) {
411
+ return new Date(date).getTime();
412
+ }
557
413
  var TimeDiff = class {
558
- minutes(date1, date2) {
559
- return this.millis(date1, date2) / 6e4;
414
+ minutes(start, end) {
415
+ return this.millis(start, end) / 6e4;
560
416
  }
561
- seconds(date1, date2) {
562
- return this.millis(date1, date2) / 1e3;
417
+ seconds(start, end) {
418
+ return this.millis(start, end) / 1e3;
563
419
  }
564
- millis(date1, date2) {
565
- const d1 = new Date(date1).getTime();
566
- const d2 = new Date(date2).getTime();
567
- return Math.abs(Math.round(d2 - d1));
420
+ millis(start, end) {
421
+ const distance = Math.abs(toTime(end) - toTime(start));
422
+ return Math.round(distance);
568
423
  }
569
- days(date1, date2) {
570
- return this.millis(date1, date2) / 864e5;
424
+ days(start, end) {
425
+ return this.millis(start, end) / 864e5;
571
426
  }
572
- hours(date1, date2) {
573
- return this.millis(date1, date2) / 36e5;
427
+ hours(start, end) {
428
+ return this.millis(start, end) / 36e5;
574
429
  }
575
- weeks(date1, date2) {
576
- return this.millis(date1, date2) / 6048e5;
430
+ weeks(start, end) {
431
+ return this.millis(start, end) / 6048e5;
577
432
  }
578
433
  fromPhrase(phrase) {
579
- const propertyKey = (0, import_phrases2.convertPhrase)(phrase, import_phrases2.lower);
580
- (0, import_asserters2.AssertKey)(this, propertyKey);
581
- return this[propertyKey].bind(this);
434
+ if (typeof phrase !== "string") {
435
+ throw new AutomationError(
436
+ `TimeDiff.fromPhrase expects a string, received '${typeof phrase}'.`
437
+ );
438
+ }
439
+ const key = normalizeToken(phrase);
440
+ if (!this.hasMethod(key)) {
441
+ throw new AutomationError(
442
+ `Unsupported diff unit '${phrase}'. Expected one of ${this.supportedMethods().join(", ")}.`
443
+ );
444
+ }
445
+ assertKey(this, key, "TimeDiff.fromPhrase");
446
+ const method = this[key];
447
+ if (typeof method !== "function") {
448
+ throw new AutomationError(`Diff method '${key}' is not callable.`);
449
+ }
450
+ return method.bind(this);
582
451
  }
583
- };
584
- var _diff;
585
- var TimeObject = class {
586
- constructor() {
587
- __privateAdd(this, _diff, new TimeDiff());
452
+ hasMethod(name) {
453
+ return SUPPORTED_METHODS.includes(name);
454
+ }
455
+ supportedMethods() {
456
+ return [...SUPPORTED_METHODS];
588
457
  }
589
- get diff() {
590
- return __privateGet(this, _diff);
458
+ };
459
+
460
+ // src/time/time.ts
461
+ var TimeFacade = class {
462
+ constructor(diff) {
463
+ this.diff = diff;
591
464
  }
592
465
  };
593
- _diff = new WeakMap();
594
- var Time = new TimeObject();
595
- // Annotate the CommonJS export names for ESM import in node:
596
- 0 && (module.exports = {
597
- DateFactory,
598
- Dates,
599
- FmtDateFactory,
600
- IsoDateFactory,
601
- Time,
602
- TimeDiff,
603
- TimeObject
604
- });
466
+ function createTime() {
467
+ return new TimeFacade(new TimeDiff());
468
+ }
469
+ var Time = createTime();
470
+
471
+ export { DateFactory, Dates, FormattedDateFactory, IsoDateFactory, Time, TimeDiff, createDates, createTime };
472
+ //# sourceMappingURL=out.js.map
605
473
  //# sourceMappingURL=index.js.map