date-vir 3.0.0 → 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 (42) hide show
  1. package/dist/cjs/date-operations/diff-dates.js +57 -4
  2. package/dist/cjs/duration.js +30 -9
  3. package/dist/cjs/extra-utils/has-date-props.js +25 -0
  4. package/dist/cjs/extra-utils/js-date-conversion.js +2 -2
  5. package/dist/cjs/extra-utils/now.js +2 -2
  6. package/dist/cjs/formatting/formatted-string.js +34 -0
  7. package/dist/cjs/formatting/html-input.js +37 -0
  8. package/dist/cjs/formatting/relative-string.js +52 -0
  9. package/dist/cjs/formatting/simple-strings.js +58 -0
  10. package/dist/cjs/formatting/timestamp.js +21 -0
  11. package/dist/cjs/index.js +7 -3
  12. package/dist/esm/date-operations/diff-dates.d.ts +25 -6
  13. package/dist/esm/date-operations/diff-dates.js +56 -4
  14. package/dist/esm/duration.d.ts +42 -14
  15. package/dist/esm/duration.js +29 -8
  16. package/dist/esm/extra-utils/has-date-props.d.ts +8 -0
  17. package/dist/esm/extra-utils/has-date-props.js +20 -0
  18. package/dist/esm/extra-utils/js-date-conversion.js +1 -1
  19. package/dist/esm/extra-utils/now.d.ts +1 -1
  20. package/dist/esm/extra-utils/now.js +1 -1
  21. package/dist/esm/extra-utils/zero.d.ts +1 -1
  22. package/dist/esm/formatting/formatted-string.d.ts +14 -0
  23. package/dist/esm/formatting/formatted-string.js +29 -0
  24. package/dist/esm/{full-date/primitive-conversions.d.ts → formatting/html-input.d.ts} +2 -19
  25. package/dist/esm/formatting/html-input.js +33 -0
  26. package/dist/esm/formatting/relative-string.d.ts +25 -0
  27. package/dist/esm/formatting/relative-string.js +48 -0
  28. package/dist/esm/formatting/simple-strings.d.ts +71 -0
  29. package/dist/esm/formatting/simple-strings.js +51 -0
  30. package/dist/esm/formatting/timestamp.d.ts +6 -0
  31. package/dist/esm/formatting/timestamp.js +16 -0
  32. package/dist/esm/index.d.ts +7 -3
  33. package/dist/esm/index.js +7 -3
  34. package/package.json +23 -23
  35. package/dist/cjs/augments/type.js +0 -2
  36. package/dist/cjs/full-date/primitive-conversions.js +0 -102
  37. package/dist/esm/augments/type.d.ts +0 -2
  38. package/dist/esm/augments/type.js +0 -1
  39. package/dist/esm/full-date/primitive-conversions.js +0 -94
  40. /package/dist/cjs/{full-date → formatting}/string-format-types.js +0 -0
  41. /package/dist/esm/{full-date → formatting}/string-format-types.d.ts +0 -0
  42. /package/dist/esm/{full-date → formatting}/string-format-types.js +0 -0
@@ -1,18 +1,71 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isDateAfter = exports.diffDates = void 0;
3
+ exports.isDateAfter = exports.diffDatesAllUnits = exports.diffDates = void 0;
4
+ const common_1 = require("@augment-vir/common");
4
5
  const duration_1 = require("../duration");
5
6
  const luxon_date_time_conversion_1 = require("../full-date/luxon-date-time-conversion");
7
+ const conversionAccuracies = {
8
+ [duration_1.DurationUnit.Years]: 'longterm',
9
+ [duration_1.DurationUnit.Quarters]: 'longterm',
10
+ [duration_1.DurationUnit.Months]: 'longterm',
11
+ [duration_1.DurationUnit.Weeks]: 'casual',
12
+ [duration_1.DurationUnit.Days]: 'casual',
13
+ [duration_1.DurationUnit.Hours]: 'casual',
14
+ [duration_1.DurationUnit.Minutes]: 'casual',
15
+ [duration_1.DurationUnit.Seconds]: 'casual',
16
+ [duration_1.DurationUnit.Milliseconds]: 'casual',
17
+ };
18
+ /**
19
+ * Diffs two dates and returns the diff in the specific unit.
20
+ *
21
+ * Note: when years, quarters, or months are used for the unit then "long term" durations are used
22
+ * to calculate the diff. See more details here:
23
+ * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy
24
+ */
6
25
  function diffDates({ start, end, unit, }) {
7
26
  const luxonDateStart = (0, luxon_date_time_conversion_1.toLuxonDateTime)(start);
8
27
  const luxonDateEnd = (0, luxon_date_time_conversion_1.toLuxonDateTime)(end);
9
28
  const diff = luxonDateEnd
10
- .diff(luxonDateStart, unit)
29
+ .diff(luxonDateStart, unit, {
30
+ conversionAccuracy: conversionAccuracies[unit],
31
+ })
11
32
  .toObject();
12
33
  return diff;
13
34
  }
14
35
  exports.diffDates = diffDates;
15
- function isDateAfter({ start, end }) {
16
- return diffDates({ start, end, unit: duration_1.DurationUnit.Milliseconds }).milliseconds >= 0;
36
+ /**
37
+ * Diffs two dates and returns that diff in each unit. Note that the unit in the returned object are
38
+ * not meant to be added together, each unit is instead the same value but calculated in that given
39
+ * unit.
40
+ *
41
+ * Note: when years, quarters, or months are used for the unit then "long term" durations are used
42
+ * to calculate the diff. See more details here:
43
+ * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy
44
+ */
45
+ function diffDatesAllUnits({ start, end }) {
46
+ const allUnitDiffEntries = duration_1.orderedDurationUnits.map((durationUnit) => {
47
+ const diff = diffDates({
48
+ start,
49
+ end,
50
+ unit: durationUnit,
51
+ });
52
+ const duration = diff[durationUnit];
53
+ /** Ignore this edge case in coverage cause its for type guarding. */
54
+ /* c8 ignore next 5 */
55
+ if (duration == undefined) {
56
+ throw new Error(`Internal date-vir error: failed to find duration for '${durationUnit}'`);
57
+ }
58
+ return [
59
+ durationUnit,
60
+ duration,
61
+ ];
62
+ });
63
+ const allDurations = (0, common_1.typedObjectFromEntries)(allUnitDiffEntries);
64
+ return allDurations;
65
+ }
66
+ exports.diffDatesAllUnits = diffDatesAllUnits;
67
+ function isDateAfter({ relativeTo, fullDate, }) {
68
+ return (diffDates({ start: relativeTo, end: fullDate, unit: duration_1.DurationUnit.Milliseconds })
69
+ .milliseconds >= 0);
17
70
  }
18
71
  exports.isDateAfter = isDateAfter;
@@ -1,16 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DurationUnit = void 0;
4
- /**
5
- * Exclude the higher order units of time measure for diffing because they vary significantly,
6
- * causing inaccuracies and many issues. See
7
- * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy for more info.
8
- *
9
- * If you REALLY want to use these higher order units, convert the dates to luxon objects with
10
- * toLuxonDateTime and then run diff calculations there.
11
- */
3
+ exports.orderedDurationUnits = exports.singularDurationUnitNames = exports.DurationUnit = void 0;
12
4
  var DurationUnit;
13
5
  (function (DurationUnit) {
6
+ DurationUnit["Years"] = "years";
7
+ DurationUnit["Quarters"] = "quarters";
8
+ DurationUnit["Months"] = "months";
14
9
  DurationUnit["Weeks"] = "weeks";
15
10
  DurationUnit["Days"] = "days";
16
11
  DurationUnit["Hours"] = "hours";
@@ -18,3 +13,29 @@ var DurationUnit;
18
13
  DurationUnit["Seconds"] = "seconds";
19
14
  DurationUnit["Milliseconds"] = "milliseconds";
20
15
  })(DurationUnit || (exports.DurationUnit = DurationUnit = {}));
16
+ exports.singularDurationUnitNames = {
17
+ [DurationUnit.Milliseconds]: 'millisecond',
18
+ [DurationUnit.Seconds]: 'second',
19
+ [DurationUnit.Minutes]: 'minute',
20
+ [DurationUnit.Hours]: 'hour',
21
+ [DurationUnit.Days]: 'day',
22
+ [DurationUnit.Weeks]: 'week',
23
+ [DurationUnit.Months]: 'month',
24
+ [DurationUnit.Quarters]: 'quarter',
25
+ [DurationUnit.Years]: 'year',
26
+ };
27
+ /**
28
+ * Array of DurationUnit values: from smallest unit (milliseconds at index 0) to longest unit
29
+ * (years).
30
+ */
31
+ exports.orderedDurationUnits = [
32
+ DurationUnit.Milliseconds,
33
+ DurationUnit.Seconds,
34
+ DurationUnit.Minutes,
35
+ DurationUnit.Hours,
36
+ DurationUnit.Days,
37
+ DurationUnit.Weeks,
38
+ DurationUnit.Months,
39
+ DurationUnit.Quarters,
40
+ DurationUnit.Years,
41
+ ];
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertHasDateProps = exports.hasDateProps = void 0;
4
+ function hasDateProps(dateInput, keys) {
5
+ try {
6
+ assertHasDateProps(dateInput, keys);
7
+ return true;
8
+ }
9
+ catch (error) {
10
+ return false;
11
+ }
12
+ }
13
+ exports.hasDateProps = hasDateProps;
14
+ function assertHasDateProps(dateInput, keys) {
15
+ const missingKeys = [];
16
+ keys.forEach((key) => {
17
+ if (dateInput[key] == undefined) {
18
+ missingKeys.push(key);
19
+ }
20
+ });
21
+ if (missingKeys.length) {
22
+ throw new Error(`Missing required FullDate key(s): ${missingKeys.join(', ')}`);
23
+ }
24
+ }
25
+ exports.assertHasDateProps = assertHasDateProps;
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toJsDate = void 0;
4
- const primitive_conversions_1 = require("../full-date/primitive-conversions");
4
+ const timestamp_1 = require("../formatting/timestamp");
5
5
  function toJsDate(fullDate) {
6
- return new Date((0, primitive_conversions_1.toTimestamp)(fullDate));
6
+ return new Date((0, timestamp_1.toTimestamp)(fullDate));
7
7
  }
8
8
  exports.toJsDate = toJsDate;
@@ -1,15 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getNowInUserTimezone = exports.getNowInIsoString = void 0;
4
+ const timestamp_1 = require("../formatting/timestamp");
4
5
  const create_full_date_1 = require("../full-date/create-full-date");
5
- const primitive_conversions_1 = require("../full-date/primitive-conversions");
6
6
  const timezones_1 = require("../timezone/timezones");
7
7
  function getNowInIsoString() {
8
8
  /**
9
9
  * The time zone doesn't matter here because the ISO string will be identical for a given date
10
10
  * no matter what its timezone is.
11
11
  */
12
- return (0, primitive_conversions_1.toIsoString)((0, create_full_date_1.getNowFullDate)(timezones_1.utcTimezone));
12
+ return (0, timestamp_1.toIsoString)((0, create_full_date_1.getNowFullDate)(timezones_1.utcTimezone));
13
13
  }
14
14
  exports.getNowInIsoString = getNowInIsoString;
15
15
  function getNowInUserTimezone() {
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toFormattedString = exports.toLocaleString = void 0;
4
+ const luxon_date_time_conversion_1 = require("../full-date/luxon-date-time-conversion");
5
+ const locales_1 = require("../locales");
6
+ /**
7
+ * Chromium has a weird behavior where the "NARROW NO-BREAK SPACE" character (code 8239) is used
8
+ * instead of a space (code 32) before AM/PM.
9
+ */
10
+ function fixChromiumSpace(original) {
11
+ return original.replace(new RegExp(String.fromCharCode(8239), 'g'), ' ');
12
+ }
13
+ /** Converts the given FullDate into a string in the user's locale, or the given localeOverride. */
14
+ function toLocaleString(fullDate, formatOptions) {
15
+ const localeString = (0, luxon_date_time_conversion_1.toLuxonDateTime)(fullDate).toLocaleString({
16
+ ...formatOptions,
17
+ }, {
18
+ locale: formatOptions?.locale ?? locales_1.userLocale,
19
+ });
20
+ return fixChromiumSpace(localeString);
21
+ }
22
+ exports.toLocaleString = toLocaleString;
23
+ /**
24
+ * Provides arbitrary date formatting using Luxon. For full details on the options for the format
25
+ * string input, see https://moment.github.io/luxon/#/formatting?id=table-of-tokens
26
+ *
27
+ * You probably should **prefer toLocaleString** so your users see the format they expect. It
28
+ * already has many formatting options.
29
+ */
30
+ function toFormattedString(fullDate, format, localeOverride = locales_1.userLocale) {
31
+ const formattedString = (0, luxon_date_time_conversion_1.toLuxonDateTime)(fullDate).toFormat(format, { locale: localeOverride });
32
+ return fixChromiumSpace(formattedString);
33
+ }
34
+ exports.toFormattedString = toFormattedString;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toHtmlInputString = void 0;
4
+ const common_1 = require("@augment-vir/common");
5
+ const has_date_props_1 = require("../extra-utils/has-date-props");
6
+ const full_date_shape_1 = require("../full-date/full-date-shape");
7
+ const simple_strings_1 = require("./simple-strings");
8
+ function toHtmlInputString(fullDate, inputType, includeSeconds) {
9
+ const internalFullDate = fullDate;
10
+ if (inputType === full_date_shape_1.FullDatePartEnum.Date) {
11
+ (0, has_date_props_1.assertHasDateProps)(internalFullDate, [
12
+ 'year',
13
+ 'month',
14
+ 'day',
15
+ ]);
16
+ return (0, simple_strings_1.toSimpleDatePartString)(internalFullDate);
17
+ }
18
+ else if (inputType === full_date_shape_1.FullDatePartEnum.Time) {
19
+ if (includeSeconds && internalFullDate.second == undefined) {
20
+ throw new Error('Tried to include seconds in the time string but no seconds were provided.');
21
+ }
22
+ (0, has_date_props_1.assertHasDateProps)(internalFullDate, [
23
+ 'hour',
24
+ 'minute',
25
+ ]);
26
+ return (0, simple_strings_1.toSimpleTimePartString)(internalFullDate, !!includeSeconds);
27
+ }
28
+ else if (inputType === full_date_shape_1.FullDatePartEnum.DateTime) {
29
+ const datePart = toHtmlInputString(fullDate, full_date_shape_1.FullDatePartEnum.Date);
30
+ const timePart = toHtmlInputString(fullDate, full_date_shape_1.FullDatePartEnum.Time, includeSeconds);
31
+ return `${datePart}T${timePart}`;
32
+ }
33
+ else {
34
+ throw new Error(`Unexpected inputTyped: '${inputType}'. Expected usage of FullDatePartEnum or one of ${(0, common_1.getEnumTypedValues)(full_date_shape_1.FullDatePartEnum).join(', ')}.`);
35
+ }
36
+ }
37
+ exports.toHtmlInputString = toHtmlInputString;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toRelativeString = void 0;
4
+ const common_1 = require("@augment-vir/common");
5
+ const diff_dates_1 = require("../date-operations/diff-dates");
6
+ const duration_1 = require("../duration");
7
+ function toRelativeString({ fullDate, relativeTo, options = {}, }) {
8
+ const allUnitDiffs = (0, diff_dates_1.diffDatesAllUnits)({
9
+ start: fullDate,
10
+ end: relativeTo,
11
+ });
12
+ const unitsWithinBounds = (0, common_1.mapObjectValues)(allUnitDiffs, (durationUnit, duration) => {
13
+ const isAboveZero = Math.floor(Math.abs(duration)) > 0;
14
+ return isAboveZero;
15
+ });
16
+ const unitToUse = duration_1.orderedDurationUnits.reduce((chosenUnit, currentUnit) => {
17
+ const isUnitAllowed = !options.blockedRelativeUnits?.includes(currentUnit);
18
+ if (isUnitAllowed && unitsWithinBounds[currentUnit]) {
19
+ return currentUnit;
20
+ }
21
+ else {
22
+ return chosenUnit;
23
+ }
24
+ }, undefined);
25
+ const shouldUseJustNow = options.blockJustNow
26
+ ? false
27
+ : (unitToUse === duration_1.DurationUnit.Minutes && allUnitDiffs.minutes < 2) ||
28
+ (unitToUse === duration_1.DurationUnit.Seconds && allUnitDiffs.seconds < 11) ||
29
+ (unitToUse === duration_1.DurationUnit.Milliseconds && allUnitDiffs.milliseconds < 710);
30
+ /** Some short circuits. */
31
+ if (unitToUse == undefined) {
32
+ return undefined;
33
+ }
34
+ else if (shouldUseJustNow) {
35
+ return 'just now';
36
+ }
37
+ const duration = allUnitDiffs[unitToUse];
38
+ const roundedAbsoluteDuration = Math.abs((0, common_1.round)({ digits: options.decimalDigitCount || 0, number: duration }));
39
+ const isDurationSingular = roundedAbsoluteDuration === 1;
40
+ const unitName = [
41
+ duration_1.singularDurationUnitNames[unitToUse],
42
+ isDurationSingular ? '' : 's',
43
+ ].join('');
44
+ const value = isDurationSingular ? 'a' : roundedAbsoluteDuration;
45
+ if (duration < 0) {
46
+ return `in ${value} ${unitName}`;
47
+ }
48
+ else {
49
+ return `${value} ${unitName} ago`;
50
+ }
51
+ }
52
+ exports.toRelativeString = toRelativeString;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toSimpleString = exports.toSimplePartStrings = exports.toSimpleTimePartString = exports.toSimpleDatePartString = void 0;
4
+ const common_1 = require("@augment-vir/common");
5
+ /**
6
+ * Create a string based on just the date parts of a FullDate.
7
+ *
8
+ * CAUTION: this does not user locale setting and should therefore not be used for user-facing date
9
+ * formatting. Prefer using `toLocaleString` instead for user-facing strings.
10
+ *
11
+ * Example output: `'2023-12-05'`
12
+ */
13
+ function toSimpleDatePartString(fullDate) {
14
+ const dateParts = [
15
+ String(Math.abs(fullDate.year)).padStart(4, '0'),
16
+ String(Math.abs(fullDate.month)).padStart(2, '0'),
17
+ String(Math.abs(fullDate.day)).padStart(2, '0'),
18
+ ];
19
+ const dateString = dateParts.join('-');
20
+ return dateString;
21
+ }
22
+ exports.toSimpleDatePartString = toSimpleDatePartString;
23
+ function toSimpleTimePartString(fullDate, includeSeconds) {
24
+ const seconds = (0, common_1.typedHasProperty)(fullDate, 'second') && includeSeconds
25
+ ? String(Math.abs(fullDate.second)).padStart(2, '0')
26
+ : undefined;
27
+ const timeParts = [
28
+ String(Math.abs(fullDate.hour)).padStart(2, '0'),
29
+ String(Math.abs(fullDate.minute)).padStart(2, '0'),
30
+ seconds,
31
+ ].filter(common_1.isTruthy);
32
+ return timeParts.join(':');
33
+ }
34
+ exports.toSimpleTimePartString = toSimpleTimePartString;
35
+ function toSimplePartStrings(fullDate, options) {
36
+ return {
37
+ date: toSimpleDatePartString(fullDate),
38
+ time: toSimpleTimePartString(fullDate, options.includeSeconds),
39
+ timezone: fullDate.timezone,
40
+ };
41
+ }
42
+ exports.toSimplePartStrings = toSimplePartStrings;
43
+ /**
44
+ * Converts a date into a simple, unambiguous date string.
45
+ *
46
+ * CAUTION: this does not user locale setting and should therefore not be used for user-facing date
47
+ * formatting. Prefer using `toLocaleString` instead for user-facing strings.
48
+ */
49
+ function toSimpleString(fullDate, options) {
50
+ const parts = toSimplePartStrings(fullDate, options);
51
+ const stringParts = [
52
+ parts.date,
53
+ parts.time,
54
+ options.includeTimezone && `(${parts.timezone})`,
55
+ ].filter(common_1.isTruthy);
56
+ return stringParts.join(' ');
57
+ }
58
+ exports.toSimpleString = toSimpleString;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toIsoString = exports.toTimestamp = void 0;
4
+ const luxon_date_time_conversion_1 = require("../full-date/luxon-date-time-conversion");
5
+ const timezones_1 = require("../timezone/timezones");
6
+ /** Convert a FullDate into a unix timestamp with milliseconds. */
7
+ function toTimestamp(fullDate) {
8
+ return (0, luxon_date_time_conversion_1.toLuxonDateTime)(fullDate).toMillis();
9
+ }
10
+ exports.toTimestamp = toTimestamp;
11
+ /** Convert a FullDate into an ISO string in UTC; always ends in Z. */
12
+ function toIsoString(fullDate) {
13
+ const isoString = (0, luxon_date_time_conversion_1.toLuxonDateTime)(fullDate).setZone(timezones_1.utcTimezone).toISO();
14
+ /** Ignore this edge case in the types and idk how to trigger it. */
15
+ /* c8 ignore next 3 */
16
+ if (!isoString) {
17
+ throw new Error(`Unable to get ISO string for '${JSON.stringify(fullDate)}'`);
18
+ }
19
+ return isoString;
20
+ }
21
+ exports.toIsoString = toIsoString;
package/dist/cjs/index.js CHANGED
@@ -14,15 +14,21 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./augments/type"), exports);
18
17
  __exportStar(require("./date-operations/calculate-relative-date"), exports);
19
18
  __exportStar(require("./date-operations/combine-dates"), exports);
20
19
  __exportStar(require("./date-operations/diff-dates"), exports);
21
20
  __exportStar(require("./duration"), exports);
22
21
  __exportStar(require("./extra-utils/date-transforms"), exports);
22
+ __exportStar(require("./extra-utils/has-date-props"), exports);
23
23
  __exportStar(require("./extra-utils/js-date-conversion"), exports);
24
24
  __exportStar(require("./extra-utils/now"), exports);
25
25
  __exportStar(require("./extra-utils/zero"), exports);
26
+ __exportStar(require("./formatting/formatted-string"), exports);
27
+ __exportStar(require("./formatting/html-input"), exports);
28
+ __exportStar(require("./formatting/relative-string"), exports);
29
+ __exportStar(require("./formatting/simple-strings"), exports);
30
+ __exportStar(require("./formatting/string-format-types"), exports);
31
+ __exportStar(require("./formatting/timestamp"), exports);
26
32
  __exportStar(require("./full-date/create-full-date"), exports);
27
33
  __exportStar(require("./full-date/date-like"), exports);
28
34
  __exportStar(require("./full-date/format-presets"), exports);
@@ -30,8 +36,6 @@ __exportStar(require("./full-date/full-date-shape"), exports);
30
36
  __exportStar(require("./full-date/is-valid-full-date"), exports);
31
37
  __exportStar(require("./full-date/luxon-date-time-conversion"), exports);
32
38
  __exportStar(require("./full-date/parsing"), exports);
33
- __exportStar(require("./full-date/primitive-conversions"), exports);
34
- __exportStar(require("./full-date/string-format-types"), exports);
35
39
  __exportStar(require("./locales"), exports);
36
40
  __exportStar(require("./timezone/timezone-checks"), exports);
37
41
  __exportStar(require("./timezone/timezone-names"), exports);
@@ -1,12 +1,31 @@
1
- import { MaybeTuple } from '../augments/type';
2
- import { Duration, DurationUnit } from '../duration';
1
+ import { AllDurations, Duration, DurationUnit } from '../duration';
3
2
  import { FullDate } from '../full-date/full-date-shape';
4
- export declare function diffDates<const DurationKeys extends MaybeTuple<DurationUnit> = MaybeTuple<DurationUnit>>({ start, end, unit, }: {
3
+ /**
4
+ * Diffs two dates and returns the diff in the specific unit.
5
+ *
6
+ * Note: when years, quarters, or months are used for the unit then "long term" durations are used
7
+ * to calculate the diff. See more details here:
8
+ * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy
9
+ */
10
+ export declare function diffDates<const ChosenDurationKey extends DurationUnit>({ start, end, unit, }: {
5
11
  start: FullDate;
6
12
  end: FullDate;
7
- unit: DurationKeys;
8
- }): Duration<DurationKeys>;
9
- export declare function isDateAfter({ start, end }: {
13
+ unit: ChosenDurationKey;
14
+ }): Duration<ChosenDurationKey>;
15
+ /**
16
+ * Diffs two dates and returns that diff in each unit. Note that the unit in the returned object are
17
+ * not meant to be added together, each unit is instead the same value but calculated in that given
18
+ * unit.
19
+ *
20
+ * Note: when years, quarters, or months are used for the unit then "long term" durations are used
21
+ * to calculate the diff. See more details here:
22
+ * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy
23
+ */
24
+ export declare function diffDatesAllUnits({ start, end }: {
10
25
  start: FullDate;
11
26
  end: FullDate;
27
+ }): AllDurations;
28
+ export declare function isDateAfter({ relativeTo, fullDate, }: {
29
+ relativeTo: FullDate;
30
+ fullDate: FullDate;
12
31
  }): boolean;
@@ -1,13 +1,65 @@
1
- import { DurationUnit } from '../duration';
1
+ import { typedObjectFromEntries } from '@augment-vir/common';
2
+ import { DurationUnit, orderedDurationUnits } from '../duration';
2
3
  import { toLuxonDateTime } from '../full-date/luxon-date-time-conversion';
4
+ const conversionAccuracies = {
5
+ [DurationUnit.Years]: 'longterm',
6
+ [DurationUnit.Quarters]: 'longterm',
7
+ [DurationUnit.Months]: 'longterm',
8
+ [DurationUnit.Weeks]: 'casual',
9
+ [DurationUnit.Days]: 'casual',
10
+ [DurationUnit.Hours]: 'casual',
11
+ [DurationUnit.Minutes]: 'casual',
12
+ [DurationUnit.Seconds]: 'casual',
13
+ [DurationUnit.Milliseconds]: 'casual',
14
+ };
15
+ /**
16
+ * Diffs two dates and returns the diff in the specific unit.
17
+ *
18
+ * Note: when years, quarters, or months are used for the unit then "long term" durations are used
19
+ * to calculate the diff. See more details here:
20
+ * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy
21
+ */
3
22
  export function diffDates({ start, end, unit, }) {
4
23
  const luxonDateStart = toLuxonDateTime(start);
5
24
  const luxonDateEnd = toLuxonDateTime(end);
6
25
  const diff = luxonDateEnd
7
- .diff(luxonDateStart, unit)
26
+ .diff(luxonDateStart, unit, {
27
+ conversionAccuracy: conversionAccuracies[unit],
28
+ })
8
29
  .toObject();
9
30
  return diff;
10
31
  }
11
- export function isDateAfter({ start, end }) {
12
- return diffDates({ start, end, unit: DurationUnit.Milliseconds }).milliseconds >= 0;
32
+ /**
33
+ * Diffs two dates and returns that diff in each unit. Note that the unit in the returned object are
34
+ * not meant to be added together, each unit is instead the same value but calculated in that given
35
+ * unit.
36
+ *
37
+ * Note: when years, quarters, or months are used for the unit then "long term" durations are used
38
+ * to calculate the diff. See more details here:
39
+ * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy
40
+ */
41
+ export function diffDatesAllUnits({ start, end }) {
42
+ const allUnitDiffEntries = orderedDurationUnits.map((durationUnit) => {
43
+ const diff = diffDates({
44
+ start,
45
+ end,
46
+ unit: durationUnit,
47
+ });
48
+ const duration = diff[durationUnit];
49
+ /** Ignore this edge case in coverage cause its for type guarding. */
50
+ /* c8 ignore next 5 */
51
+ if (duration == undefined) {
52
+ throw new Error(`Internal date-vir error: failed to find duration for '${durationUnit}'`);
53
+ }
54
+ return [
55
+ durationUnit,
56
+ duration,
57
+ ];
58
+ });
59
+ const allDurations = typedObjectFromEntries(allUnitDiffEntries);
60
+ return allDurations;
61
+ }
62
+ export function isDateAfter({ relativeTo, fullDate, }) {
63
+ return (diffDates({ start: relativeTo, end: fullDate, unit: DurationUnit.Milliseconds })
64
+ .milliseconds >= 0);
13
65
  }
@@ -1,15 +1,8 @@
1
- import { AtLeastTuple, RequiredAndNotNull } from '@augment-vir/common';
2
- import { DurationObjectUnits } from 'luxon';
3
- import { MaybeTuple } from './augments/type';
4
- /**
5
- * Exclude the higher order units of time measure for diffing because they vary significantly,
6
- * causing inaccuracies and many issues. See
7
- * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy for more info.
8
- *
9
- * If you REALLY want to use these higher order units, convert the dates to luxon objects with
10
- * toLuxonDateTime and then run diff calculations there.
11
- */
1
+ import { RequiredAndNotNull } from '@augment-vir/common';
12
2
  export declare enum DurationUnit {
3
+ Years = "years",
4
+ Quarters = "quarters",
5
+ Months = "months",
13
6
  Weeks = "weeks",
14
7
  Days = "days",
15
8
  Hours = "hours",
@@ -17,11 +10,46 @@ export declare enum DurationUnit {
17
10
  Seconds = "seconds",
18
11
  Milliseconds = "milliseconds"
19
12
  }
20
- /** All possible options for the more strict Duration type. */
21
- export type AnyDuration = DurationObjectUnits;
13
+ export declare const singularDurationUnitNames: {
14
+ readonly milliseconds: "millisecond";
15
+ readonly seconds: "second";
16
+ readonly minutes: "minute";
17
+ readonly hours: "hour";
18
+ readonly days: "day";
19
+ readonly weeks: "week";
20
+ readonly months: "month";
21
+ readonly quarters: "quarter";
22
+ readonly years: "year";
23
+ };
24
+ /**
25
+ * Array of DurationUnit values: from smallest unit (milliseconds at index 0) to longest unit
26
+ * (years).
27
+ */
28
+ export declare const orderedDurationUnits: readonly [DurationUnit.Milliseconds, DurationUnit.Seconds, DurationUnit.Minutes, DurationUnit.Hours, DurationUnit.Days, DurationUnit.Weeks, DurationUnit.Months, DurationUnit.Quarters, DurationUnit.Years];
29
+ /**
30
+ * All possible options for the more strict Duration type. Matches the DurationObjectUnits type from
31
+ * the luxon package.
32
+ */
33
+ export type AnyDuration = {
34
+ years?: number | undefined;
35
+ quarters?: number | undefined;
36
+ months?: number | undefined;
37
+ weeks?: number | undefined;
38
+ days?: number | undefined;
39
+ hours?: number | undefined;
40
+ minutes?: number | undefined;
41
+ seconds?: number | undefined;
42
+ milliseconds?: number | undefined;
43
+ };
44
+ /**
45
+ * All possible duration units at once. Whether these duration units should be added together to
46
+ * form the whole picture or whether each duration is the same value but calculated in different
47
+ * units depends on the context of this type's usage.
48
+ */
49
+ export type AllDurations = RequiredAndNotNull<AnyDuration>;
22
50
  /**
23
51
  * Not a single date instance, but a description of a date duration. Used to calculate diffs between
24
52
  * dates, or add offsets to an existing date, or describe a single time duration. Usually only one
25
53
  * property is set on this at any given time.
26
54
  */
27
- export type Duration<DurationKeys extends MaybeTuple<DurationUnit>> = MaybeTuple<DurationUnit> extends DurationKeys ? Pick<AnyDuration, DurationUnit> : Pick<RequiredAndNotNull<AnyDuration>, DurationKeys extends AtLeastTuple<infer InnerValue, 1> ? InnerValue : DurationKeys>;
55
+ export type Duration<DurationKeys extends DurationUnit = DurationUnit> = DurationUnit extends DurationKeys ? AnyDuration : Pick<AllDurations, `${DurationKeys}`>;
@@ -1,13 +1,8 @@
1
- /**
2
- * Exclude the higher order units of time measure for diffing because they vary significantly,
3
- * causing inaccuracies and many issues. See
4
- * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy for more info.
5
- *
6
- * If you REALLY want to use these higher order units, convert the dates to luxon objects with
7
- * toLuxonDateTime and then run diff calculations there.
8
- */
9
1
  export var DurationUnit;
10
2
  (function (DurationUnit) {
3
+ DurationUnit["Years"] = "years";
4
+ DurationUnit["Quarters"] = "quarters";
5
+ DurationUnit["Months"] = "months";
11
6
  DurationUnit["Weeks"] = "weeks";
12
7
  DurationUnit["Days"] = "days";
13
8
  DurationUnit["Hours"] = "hours";
@@ -15,3 +10,29 @@ export var DurationUnit;
15
10
  DurationUnit["Seconds"] = "seconds";
16
11
  DurationUnit["Milliseconds"] = "milliseconds";
17
12
  })(DurationUnit || (DurationUnit = {}));
13
+ export const singularDurationUnitNames = {
14
+ [DurationUnit.Milliseconds]: 'millisecond',
15
+ [DurationUnit.Seconds]: 'second',
16
+ [DurationUnit.Minutes]: 'minute',
17
+ [DurationUnit.Hours]: 'hour',
18
+ [DurationUnit.Days]: 'day',
19
+ [DurationUnit.Weeks]: 'week',
20
+ [DurationUnit.Months]: 'month',
21
+ [DurationUnit.Quarters]: 'quarter',
22
+ [DurationUnit.Years]: 'year',
23
+ };
24
+ /**
25
+ * Array of DurationUnit values: from smallest unit (milliseconds at index 0) to longest unit
26
+ * (years).
27
+ */
28
+ export const orderedDurationUnits = [
29
+ DurationUnit.Milliseconds,
30
+ DurationUnit.Seconds,
31
+ DurationUnit.Minutes,
32
+ DurationUnit.Hours,
33
+ DurationUnit.Days,
34
+ DurationUnit.Weeks,
35
+ DurationUnit.Months,
36
+ DurationUnit.Quarters,
37
+ DurationUnit.Years,
38
+ ];
@@ -0,0 +1,8 @@
1
+ import { PartialAndUndefined, RequiredAndNotNullBy } from '@augment-vir/common';
2
+ import { FullDate } from '../full-date/full-date-shape';
3
+ export declare function hasDateProps<PartialFullDate extends PartialAndUndefined<FullDate>,
4
+ /** A union of strings. */
5
+ RequiredKeysUnion extends keyof FullDate>(dateInput: PartialAndUndefined<FullDate>, keys: ReadonlyArray<RequiredKeysUnion>): dateInput is RequiredAndNotNullBy<PartialFullDate, RequiredKeysUnion>;
6
+ export declare function assertHasDateProps<PartialFullDate extends PartialAndUndefined<FullDate>,
7
+ /** A union of strings. */
8
+ RequiredKeysUnion extends keyof FullDate>(dateInput: PartialAndUndefined<FullDate>, keys: ReadonlyArray<RequiredKeysUnion>): asserts dateInput is RequiredAndNotNullBy<PartialFullDate, RequiredKeysUnion>;