date-vir 4.1.1 → 5.1.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.
@@ -0,0 +1,13 @@
1
+ const {baseConfig} = require('virmator/base-configs/base-cspell.js');
2
+
3
+ module.exports = {
4
+ ...baseConfig,
5
+ ignorePaths: [
6
+ ...baseConfig.ignorePaths,
7
+ ],
8
+ words: [
9
+ ...baseConfig.words,
10
+ 'datetime',
11
+ 'longterm',
12
+ ],
13
+ };
@@ -1,71 +1,105 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isDateAfter = exports.diffDatesAllUnits = exports.diffDates = void 0;
3
+ exports.isDateAfter = exports.diffDates = exports.DiffType = void 0;
4
4
  const common_1 = require("@augment-vir/common");
5
- const duration_1 = require("../duration");
5
+ const duration_unit_1 = require("../duration/duration-unit");
6
+ const duration_utils_1 = require("../duration/duration-utils");
6
7
  const luxon_date_time_conversion_1 = require("../full-date/luxon-date-time-conversion");
8
+ const accuracyPriority = {
9
+ longterm: 1,
10
+ casual: 0,
11
+ };
7
12
  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',
13
+ [duration_unit_1.DurationUnit.Years]: 'longterm',
14
+ [duration_unit_1.DurationUnit.Quarters]: 'longterm',
15
+ [duration_unit_1.DurationUnit.Months]: 'longterm',
16
+ [duration_unit_1.DurationUnit.Weeks]: 'casual',
17
+ [duration_unit_1.DurationUnit.Days]: 'casual',
18
+ [duration_unit_1.DurationUnit.Hours]: 'casual',
19
+ [duration_unit_1.DurationUnit.Minutes]: 'casual',
20
+ [duration_unit_1.DurationUnit.Seconds]: 'casual',
21
+ [duration_unit_1.DurationUnit.Milliseconds]: 'casual',
17
22
  };
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
- */
25
- function diffDates({ start, end, unit, }) {
23
+ function getHighestPriorityConversionAccuracy(units) {
24
+ const currentConversionAccuracies = units.map((unit) => conversionAccuracies[unit]);
25
+ const highestPriorityAccuracy = currentConversionAccuracies.reduce((priorityAccuracy, currentAccuracy) => {
26
+ if (accuracyPriority[currentAccuracy] > accuracyPriority[priorityAccuracy]) {
27
+ return currentAccuracy;
28
+ }
29
+ else {
30
+ return priorityAccuracy;
31
+ }
32
+ });
33
+ return highestPriorityAccuracy;
34
+ }
35
+ var DiffType;
36
+ (function (DiffType) {
37
+ /**
38
+ * Causes the diffDates function to output the diff split between each unit provided. To get the
39
+ * full duration, each output unit needs to be added together.
40
+ */
41
+ DiffType["AdditiveUnits"] = "additive-units";
42
+ /**
43
+ * Causes the diffDates function to output the diff in each unit provided with each unit being
44
+ * equivalent in value. Meaning, each unit will be the same duration of time converted into that
45
+ * unit.
46
+ */
47
+ DiffType["EquivalentUnits"] = "equivalent-units";
48
+ })(DiffType || (exports.DiffType = DiffType = {}));
49
+ function diffDates({ start, end, units: unitsInput, unit: unitInput, diffType: diffTypeInput, decimalCount, }) {
26
50
  const luxonDateStart = (0, luxon_date_time_conversion_1.toLuxonDateTime)(start);
27
51
  const luxonDateEnd = (0, luxon_date_time_conversion_1.toLuxonDateTime)(end);
28
- const diff = luxonDateEnd
29
- .diff(luxonDateStart, unit, {
30
- conversionAccuracy: conversionAccuracies[unit],
31
- })
32
- .toObject();
33
- return diff;
34
- }
35
- exports.diffDates = diffDates;
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,
52
+ const units = (unitsInput ?? [unitInput]);
53
+ const diffType = units.length === 1
54
+ ? /**
55
+ * When only one unit is provided, it doesn't matter which diff type we use so lets just use this
56
+ * one.
57
+ */
58
+ DiffType.EquivalentUnits
59
+ : diffTypeInput;
60
+ if (!diffType) {
61
+ throw new Error('Failed to diff dates: no diffType was provided.');
62
+ }
63
+ if (diffType === DiffType.AdditiveUnits) {
64
+ const conversionAccuracy = getHighestPriorityConversionAccuracy(units);
65
+ const additiveDiff = luxonDateEnd
66
+ .diff(luxonDateStart, (0, common_1.makeWritable)(units), {
67
+ conversionAccuracy,
68
+ })
69
+ .toObject();
70
+ return (0, duration_utils_1.roundDuration)(additiveDiff, decimalCount);
71
+ }
72
+ else if (diffType === DiffType.EquivalentUnits) {
73
+ const allUnitDiffEntries = units.map((durationUnit) => {
74
+ const diff = luxonDateEnd
75
+ .diff(luxonDateStart, durationUnit, {
76
+ conversionAccuracy: conversionAccuracies[durationUnit],
77
+ })
78
+ .toObject();
79
+ const duration = diff[durationUnit];
80
+ /** Ignore this edge case in coverage cause its for type guarding. */
81
+ /* c8 ignore next 5 */
82
+ if (duration == undefined) {
83
+ throw new Error(`Internal date-vir error: failed to find duration for '${durationUnit}'`);
84
+ }
85
+ return [
86
+ durationUnit,
87
+ duration,
88
+ ];
51
89
  });
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;
90
+ const equivalentDiff = (0, common_1.typedObjectFromEntries)(allUnitDiffEntries);
91
+ return (0, duration_utils_1.roundDuration)(equivalentDiff, decimalCount);
92
+ }
93
+ else {
94
+ throw new Error(`Failed to diff dates: provided diffType is invalid: '${diffType}'`);
95
+ }
65
96
  }
66
- exports.diffDatesAllUnits = diffDatesAllUnits;
97
+ exports.diffDates = diffDates;
67
98
  function isDateAfter({ relativeTo, fullDate, }) {
68
- return (diffDates({ start: relativeTo, end: fullDate, unit: duration_1.DurationUnit.Milliseconds })
69
- .milliseconds >= 0);
99
+ return (diffDates({
100
+ start: relativeTo,
101
+ end: fullDate,
102
+ unit: duration_unit_1.DurationUnit.Milliseconds,
103
+ }).milliseconds >= 0);
70
104
  }
71
105
  exports.isDateAfter = isDateAfter;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertDuration = exports.roundDuration = void 0;
4
+ const common_1 = require("@augment-vir/common");
5
+ const luxon_1 = require("luxon");
6
+ const run_time_assertions_1 = require("run-time-assertions");
7
+ /** Round every unit in a given duration to the given number of decimal points. */
8
+ function roundDuration(duration,
9
+ /**
10
+ * How many decimals to round to. Leave undefined to skip rounding. (Though if you skip
11
+ * rounding, why are you using this function?)
12
+ */
13
+ decimalCount) {
14
+ return (0, common_1.mapObjectValues)(duration, (key, value) => {
15
+ if (decimalCount == undefined || !(0, run_time_assertions_1.isRunTimeType)(value, 'number')) {
16
+ return value;
17
+ }
18
+ else {
19
+ return (0, common_1.round)({ number: value, digits: decimalCount });
20
+ }
21
+ });
22
+ }
23
+ exports.roundDuration = roundDuration;
24
+ /** Convert a duration from any combination of units into a single unit. */
25
+ function convertDuration(inputDuration, requiredUnit) {
26
+ const luxonDuration = luxon_1.Duration.fromObject(inputDuration);
27
+ const newUnitValue = luxonDuration.as(requiredUnit);
28
+ return { [requiredUnit]: newUnitValue };
29
+ }
30
+ exports.convertDuration = convertDuration;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.clearParts = exports.clearTime = exports.zeroTime = exports.zeroDate = void 0;
3
+ exports.clearParts = exports.clearTime = exports.zeroTime = exports.emptyDate = exports.zeroDate = void 0;
4
4
  const common_1 = require("@augment-vir/common");
5
5
  const full_date_shape_1 = require("../full-date/full-date-shape");
6
6
  const date_transforms_1 = require("./date-transforms");
@@ -13,6 +13,7 @@ exports.zeroDate = {
13
13
  second: 0,
14
14
  millisecond: 0,
15
15
  };
16
+ exports.emptyDate = exports.zeroDate;
16
17
  exports.zeroTime = (0, common_1.pickObjectKeys)(exports.zeroDate, full_date_shape_1.timeFullDateKeys);
17
18
  function clearTime(inputFullDate) {
18
19
  return clearParts(inputFullDate, full_date_shape_1.timeFullDateKeys);
@@ -3,23 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toRelativeString = void 0;
4
4
  const common_1 = require("@augment-vir/common");
5
5
  const diff_dates_1 = require("../date-operations/diff-dates");
6
- const duration_1 = require("../duration");
6
+ const duration_unit_1 = require("../duration/duration-unit");
7
7
  const maxDurations = {
8
- [duration_1.DurationUnit.Milliseconds]: 1000,
9
- [duration_1.DurationUnit.Seconds]: 60,
10
- [duration_1.DurationUnit.Minutes]: 60,
11
- [duration_1.DurationUnit.Hours]: 24,
12
- [duration_1.DurationUnit.Days]: 30,
13
- [duration_1.DurationUnit.Weeks]: 4,
14
- [duration_1.DurationUnit.Months]: 12,
15
- [duration_1.DurationUnit.Quarters]: 4,
16
- [duration_1.DurationUnit.Years]: Infinity,
8
+ [duration_unit_1.DurationUnit.Milliseconds]: 1000,
9
+ [duration_unit_1.DurationUnit.Seconds]: 60,
10
+ [duration_unit_1.DurationUnit.Minutes]: 60,
11
+ [duration_unit_1.DurationUnit.Hours]: 24,
12
+ [duration_unit_1.DurationUnit.Days]: 30,
13
+ [duration_unit_1.DurationUnit.Weeks]: 4,
14
+ [duration_unit_1.DurationUnit.Months]: 12,
15
+ [duration_unit_1.DurationUnit.Quarters]: 4,
16
+ [duration_unit_1.DurationUnit.Years]: Infinity,
17
17
  };
18
18
  function toRelativeString({ fullDate, relativeTo, options = {}, }) {
19
19
  const roundingDigits = options.decimalDigitCount || 0;
20
- const allUnitDiffs = (0, diff_dates_1.diffDatesAllUnits)({
20
+ const allUnitDiffs = (0, diff_dates_1.diffDates)({
21
21
  start: fullDate,
22
22
  end: relativeTo,
23
+ units: duration_unit_1.orderedDurationUnits,
24
+ diffType: diff_dates_1.DiffType.EquivalentUnits,
23
25
  });
24
26
  const unitsWithinBounds = (0, common_1.mapObjectValues)(allUnitDiffs, (durationUnit, duration) => {
25
27
  const roundedDuration = Math.floor(Math.abs((0, common_1.round)({ digits: 1, number: duration })));
@@ -29,7 +31,7 @@ function toRelativeString({ fullDate, relativeTo, options = {}, }) {
29
31
  : true;
30
32
  return isAboveZero && isBelowMax;
31
33
  });
32
- const unitToUse = duration_1.orderedDurationUnits.reduce((chosenUnit, currentUnit) => {
34
+ const unitToUse = duration_unit_1.orderedDurationUnits.reduce((chosenUnit, currentUnit) => {
33
35
  const isUnitAllowed = !options.blockedRelativeUnits?.includes(currentUnit);
34
36
  if (isUnitAllowed && unitsWithinBounds[currentUnit]) {
35
37
  return currentUnit;
@@ -41,9 +43,9 @@ function toRelativeString({ fullDate, relativeTo, options = {}, }) {
41
43
  const shouldUseJustNow = options.blockJustNow
42
44
  ? false
43
45
  : (0, common_1.areJsonEqual)(fullDate, relativeTo) ||
44
- (unitToUse === duration_1.DurationUnit.Minutes && allUnitDiffs.minutes < 2) ||
45
- (unitToUse === duration_1.DurationUnit.Seconds && allUnitDiffs.seconds < 11) ||
46
- (unitToUse === duration_1.DurationUnit.Milliseconds && allUnitDiffs.milliseconds < 710);
46
+ (unitToUse === duration_unit_1.DurationUnit.Minutes && allUnitDiffs.minutes < 2) ||
47
+ (unitToUse === duration_unit_1.DurationUnit.Seconds && allUnitDiffs.seconds < 11) ||
48
+ (unitToUse === duration_unit_1.DurationUnit.Milliseconds && allUnitDiffs.milliseconds < 710);
47
49
  /** Some short circuits. */
48
50
  if (shouldUseJustNow) {
49
51
  return 'just now';
@@ -55,7 +57,7 @@ function toRelativeString({ fullDate, relativeTo, options = {}, }) {
55
57
  const roundedAbsoluteDuration = Math.abs((0, common_1.round)({ digits: roundingDigits, number: duration }));
56
58
  const isDurationSingular = roundedAbsoluteDuration === 1;
57
59
  const unitName = [
58
- duration_1.singularDurationUnitNames[unitToUse],
60
+ duration_unit_1.singularDurationUnitNames[unitToUse],
59
61
  isDurationSingular ? '' : 's',
60
62
  ].join('');
61
63
  const value = isDurationSingular ? 'a' : roundedAbsoluteDuration;
package/dist/cjs/index.js CHANGED
@@ -17,7 +17,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./date-operations/calculate-relative-date"), exports);
18
18
  __exportStar(require("./date-operations/combine-dates"), exports);
19
19
  __exportStar(require("./date-operations/diff-dates"), exports);
20
- __exportStar(require("./duration"), exports);
20
+ __exportStar(require("./duration/duration"), exports);
21
+ __exportStar(require("./duration/duration-unit"), exports);
22
+ __exportStar(require("./duration/duration-utils"), exports);
21
23
  __exportStar(require("./extra-utils/date-transforms"), exports);
22
24
  __exportStar(require("./extra-utils/has-date-props"), exports);
23
25
  __exportStar(require("./extra-utils/js-date-conversion"), exports);
@@ -1,4 +1,4 @@
1
- import { AnyDuration } from '../duration';
1
+ import { AnyDuration } from '../duration/duration';
2
2
  import { FullDate } from '../full-date/full-date-shape';
3
3
  /**
4
4
  * Calculates a new date starting at the given fullDate and adding the given offsets. Offsets can be
@@ -1,5 +1,20 @@
1
- import { AllDurations, Duration, DurationUnit } from '../duration';
1
+ import { ArrayElement } from '@augment-vir/common';
2
+ import { Duration } from '../duration/duration';
3
+ import { DurationUnit } from '../duration/duration-unit';
2
4
  import { FullDate } from '../full-date/full-date-shape';
5
+ export declare enum DiffType {
6
+ /**
7
+ * Causes the diffDates function to output the diff split between each unit provided. To get the
8
+ * full duration, each output unit needs to be added together.
9
+ */
10
+ AdditiveUnits = "additive-units",
11
+ /**
12
+ * Causes the diffDates function to output the diff in each unit provided with each unit being
13
+ * equivalent in value. Meaning, each unit will be the same duration of time converted into that
14
+ * unit.
15
+ */
16
+ EquivalentUnits = "equivalent-units"
17
+ }
3
18
  /**
4
19
  * Diffs two dates and returns the diff in the specific unit.
5
20
  *
@@ -7,25 +22,45 @@ import { FullDate } from '../full-date/full-date-shape';
7
22
  * to calculate the diff. See more details here:
8
23
  * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy
9
24
  */
10
- export declare function diffDates<const ChosenDurationKey extends DurationUnit>({ start, end, unit, }: {
11
- start: FullDate;
12
- end: FullDate;
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 }: {
25
- start: FullDate;
26
- end: FullDate;
27
- }): AllDurations;
25
+ export declare function diffDates<const ChosenDurationUnits extends DurationUnit>(params: {
26
+ start: Readonly<FullDate>;
27
+ end: Readonly<FullDate>;
28
+ /** The unit which the output should contain. */
29
+ unit: ChosenDurationUnits;
30
+ /**
31
+ * Determine the diff type calculation. See DiffType values for explanations on each one.
32
+ * Optional when only one unit is provided, as the diffType will not change the output either
33
+ * way.
34
+ */
35
+ diffType?: DiffType | undefined;
36
+ /** How many decimals to round the outputs to. Leave undefined to skip rounding entirely. */
37
+ decimalCount?: number | undefined;
38
+ }): Duration<ChosenDurationUnits>;
39
+ export declare function diffDates<const ChosenDurationUnits extends Readonly<[DurationUnit]>>(params: {
40
+ start: Readonly<FullDate>;
41
+ end: Readonly<FullDate>;
42
+ /** Which units the output should contain. */
43
+ units: ChosenDurationUnits;
44
+ /**
45
+ * Determine the diff type calculation. See DiffType values for explanations on each one.
46
+ * Optional when only one unit is provided, as the diffType will not change the output either
47
+ * way.
48
+ */
49
+ diffType?: DiffType | undefined;
50
+ /** How many decimals to round the outputs to. Leave undefined to skip rounding entirely. */
51
+ decimalCount?: number | undefined;
52
+ }): Duration<ArrayElement<ChosenDurationUnits>>;
53
+ export declare function diffDates<const ChosenDurationUnits extends ReadonlyArray<DurationUnit>>(params: {
54
+ start: Readonly<FullDate>;
55
+ end: Readonly<FullDate>;
56
+ /** Which units the output should contain. */
57
+ units: ChosenDurationUnits;
58
+ /** Determine the diff type calculation. See DiffType values for explanations on each one. */
59
+ diffType: DiffType;
60
+ /** How many decimals to round the outputs to. Leave undefined to skip rounding entirely. */
61
+ decimalCount?: number | undefined;
62
+ }): Duration<ArrayElement<ChosenDurationUnits>>;
28
63
  export declare function isDateAfter({ relativeTo, fullDate, }: {
29
- relativeTo: FullDate;
30
- fullDate: FullDate;
64
+ relativeTo: Readonly<FullDate>;
65
+ fullDate: Readonly<FullDate>;
31
66
  }): boolean;
@@ -1,6 +1,11 @@
1
- import { typedObjectFromEntries } from '@augment-vir/common';
2
- import { DurationUnit, orderedDurationUnits } from '../duration';
1
+ import { makeWritable, typedObjectFromEntries } from '@augment-vir/common';
2
+ import { DurationUnit } from '../duration/duration-unit';
3
+ import { roundDuration } from '../duration/duration-utils';
3
4
  import { toLuxonDateTime } from '../full-date/luxon-date-time-conversion';
5
+ const accuracyPriority = {
6
+ longterm: 1,
7
+ casual: 0,
8
+ };
4
9
  const conversionAccuracies = {
5
10
  [DurationUnit.Years]: 'longterm',
6
11
  [DurationUnit.Quarters]: 'longterm',
@@ -12,54 +17,84 @@ const conversionAccuracies = {
12
17
  [DurationUnit.Seconds]: 'casual',
13
18
  [DurationUnit.Milliseconds]: 'casual',
14
19
  };
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
- */
22
- export function diffDates({ start, end, unit, }) {
20
+ function getHighestPriorityConversionAccuracy(units) {
21
+ const currentConversionAccuracies = units.map((unit) => conversionAccuracies[unit]);
22
+ const highestPriorityAccuracy = currentConversionAccuracies.reduce((priorityAccuracy, currentAccuracy) => {
23
+ if (accuracyPriority[currentAccuracy] > accuracyPriority[priorityAccuracy]) {
24
+ return currentAccuracy;
25
+ }
26
+ else {
27
+ return priorityAccuracy;
28
+ }
29
+ });
30
+ return highestPriorityAccuracy;
31
+ }
32
+ export var DiffType;
33
+ (function (DiffType) {
34
+ /**
35
+ * Causes the diffDates function to output the diff split between each unit provided. To get the
36
+ * full duration, each output unit needs to be added together.
37
+ */
38
+ DiffType["AdditiveUnits"] = "additive-units";
39
+ /**
40
+ * Causes the diffDates function to output the diff in each unit provided with each unit being
41
+ * equivalent in value. Meaning, each unit will be the same duration of time converted into that
42
+ * unit.
43
+ */
44
+ DiffType["EquivalentUnits"] = "equivalent-units";
45
+ })(DiffType || (DiffType = {}));
46
+ export function diffDates({ start, end, units: unitsInput, unit: unitInput, diffType: diffTypeInput, decimalCount, }) {
23
47
  const luxonDateStart = toLuxonDateTime(start);
24
48
  const luxonDateEnd = toLuxonDateTime(end);
25
- const diff = luxonDateEnd
26
- .diff(luxonDateStart, unit, {
27
- conversionAccuracy: conversionAccuracies[unit],
28
- })
29
- .toObject();
30
- return diff;
31
- }
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,
49
+ const units = (unitsInput ?? [unitInput]);
50
+ const diffType = units.length === 1
51
+ ? /**
52
+ * When only one unit is provided, it doesn't matter which diff type we use so lets just use this
53
+ * one.
54
+ */
55
+ DiffType.EquivalentUnits
56
+ : diffTypeInput;
57
+ if (!diffType) {
58
+ throw new Error('Failed to diff dates: no diffType was provided.');
59
+ }
60
+ if (diffType === DiffType.AdditiveUnits) {
61
+ const conversionAccuracy = getHighestPriorityConversionAccuracy(units);
62
+ const additiveDiff = luxonDateEnd
63
+ .diff(luxonDateStart, makeWritable(units), {
64
+ conversionAccuracy,
65
+ })
66
+ .toObject();
67
+ return roundDuration(additiveDiff, decimalCount);
68
+ }
69
+ else if (diffType === DiffType.EquivalentUnits) {
70
+ const allUnitDiffEntries = units.map((durationUnit) => {
71
+ const diff = luxonDateEnd
72
+ .diff(luxonDateStart, durationUnit, {
73
+ conversionAccuracy: conversionAccuracies[durationUnit],
74
+ })
75
+ .toObject();
76
+ const duration = diff[durationUnit];
77
+ /** Ignore this edge case in coverage cause its for type guarding. */
78
+ /* c8 ignore next 5 */
79
+ if (duration == undefined) {
80
+ throw new Error(`Internal date-vir error: failed to find duration for '${durationUnit}'`);
81
+ }
82
+ return [
83
+ durationUnit,
84
+ duration,
85
+ ];
47
86
  });
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;
87
+ const equivalentDiff = typedObjectFromEntries(allUnitDiffEntries);
88
+ return roundDuration(equivalentDiff, decimalCount);
89
+ }
90
+ else {
91
+ throw new Error(`Failed to diff dates: provided diffType is invalid: '${diffType}'`);
92
+ }
61
93
  }
62
94
  export function isDateAfter({ relativeTo, fullDate, }) {
63
- return (diffDates({ start: relativeTo, end: fullDate, unit: DurationUnit.Milliseconds })
64
- .milliseconds >= 0);
95
+ return (diffDates({
96
+ start: relativeTo,
97
+ end: fullDate,
98
+ unit: DurationUnit.Milliseconds,
99
+ }).milliseconds >= 0);
65
100
  }
@@ -0,0 +1,27 @@
1
+ export declare enum DurationUnit {
2
+ Years = "years",
3
+ Quarters = "quarters",
4
+ Months = "months",
5
+ Weeks = "weeks",
6
+ Days = "days",
7
+ Hours = "hours",
8
+ Minutes = "minutes",
9
+ Seconds = "seconds",
10
+ Milliseconds = "milliseconds"
11
+ }
12
+ export declare const singularDurationUnitNames: {
13
+ readonly milliseconds: "millisecond";
14
+ readonly seconds: "second";
15
+ readonly minutes: "minute";
16
+ readonly hours: "hour";
17
+ readonly days: "day";
18
+ readonly weeks: "week";
19
+ readonly months: "month";
20
+ readonly quarters: "quarter";
21
+ readonly years: "year";
22
+ };
23
+ /**
24
+ * Array of DurationUnit values: from smallest unit (milliseconds at index 0) to longest unit
25
+ * (years).
26
+ */
27
+ export declare const orderedDurationUnits: readonly [DurationUnit.Milliseconds, DurationUnit.Seconds, DurationUnit.Minutes, DurationUnit.Hours, DurationUnit.Days, DurationUnit.Weeks, DurationUnit.Months, DurationUnit.Quarters, DurationUnit.Years];
@@ -0,0 +1,11 @@
1
+ import { AnyDuration, Duration } from './duration';
2
+ import { DurationUnit } from './duration-unit';
3
+ /** Round every unit in a given duration to the given number of decimal points. */
4
+ export declare function roundDuration<InputDuration extends AnyDuration>(duration: Readonly<InputDuration>,
5
+ /**
6
+ * How many decimals to round to. Leave undefined to skip rounding. (Though if you skip
7
+ * rounding, why are you using this function?)
8
+ */
9
+ decimalCount: number | undefined): InputDuration;
10
+ /** Convert a duration from any combination of units into a single unit. */
11
+ export declare function convertDuration<RequiredDurationUnit extends DurationUnit>(inputDuration: AnyDuration, requiredUnit: RequiredDurationUnit): Duration<RequiredDurationUnit>;
@@ -0,0 +1,25 @@
1
+ import { mapObjectValues, round } from '@augment-vir/common';
2
+ import { Duration as LuxonDuration } from 'luxon';
3
+ import { isRunTimeType } from 'run-time-assertions';
4
+ /** Round every unit in a given duration to the given number of decimal points. */
5
+ export function roundDuration(duration,
6
+ /**
7
+ * How many decimals to round to. Leave undefined to skip rounding. (Though if you skip
8
+ * rounding, why are you using this function?)
9
+ */
10
+ decimalCount) {
11
+ return mapObjectValues(duration, (key, value) => {
12
+ if (decimalCount == undefined || !isRunTimeType(value, 'number')) {
13
+ return value;
14
+ }
15
+ else {
16
+ return round({ number: value, digits: decimalCount });
17
+ }
18
+ });
19
+ }
20
+ /** Convert a duration from any combination of units into a single unit. */
21
+ export function convertDuration(inputDuration, requiredUnit) {
22
+ const luxonDuration = LuxonDuration.fromObject(inputDuration);
23
+ const newUnitValue = luxonDuration.as(requiredUnit);
24
+ return { [requiredUnit]: newUnitValue };
25
+ }
@@ -0,0 +1,32 @@
1
+ import { RequiredAndNotNull } from '@augment-vir/common';
2
+ import { IsUnknown, RequireAtLeastOne, UnionToIntersection } from 'type-fest';
3
+ import { DurationUnit } from './duration-unit';
4
+ /**
5
+ * All possible options for the more strict Duration type. Matches the DurationObjectUnits type from
6
+ * the luxon package.
7
+ */
8
+ export type AnyDuration = {
9
+ years?: number | undefined;
10
+ quarters?: number | undefined;
11
+ months?: number | undefined;
12
+ weeks?: number | undefined;
13
+ days?: number | undefined;
14
+ hours?: number | undefined;
15
+ minutes?: number | undefined;
16
+ seconds?: number | undefined;
17
+ milliseconds?: number | undefined;
18
+ };
19
+ /** Requires at least one duration unit to be set. */
20
+ export type AtLeastOneDuration = RequireAtLeastOne<RequiredAndNotNull<AnyDuration>>;
21
+ /**
22
+ * All possible duration units at once. Whether these duration units should be added together to
23
+ * form the whole picture or whether each duration is the same value but calculated in different
24
+ * units depends on the context of this type's usage.
25
+ */
26
+ export type AllDurations = RequiredAndNotNull<AnyDuration>;
27
+ /**
28
+ * Not a single date instance, but a description of a date duration. Used to calculate diffs between
29
+ * dates, or add offsets to an existing date, or describe a single time duration. Usually only one
30
+ * property is set on this at any given time.
31
+ */
32
+ export type Duration<DurationKeys extends DurationUnit | unknown> = UnionToIntersection<IsUnknown<DurationKeys> extends true ? AnyDuration : DurationKeys extends DurationUnit ? Pick<AllDurations, `${DurationKeys}`> : never>;
@@ -0,0 +1 @@
1
+ export {};
@@ -8,6 +8,15 @@ export declare const zeroDate: {
8
8
  readonly second: 0;
9
9
  readonly millisecond: 0;
10
10
  };
11
+ export declare const emptyDate: {
12
+ readonly year: 0;
13
+ readonly month: 1;
14
+ readonly day: 1;
15
+ readonly hour: 0;
16
+ readonly minute: 0;
17
+ readonly second: 0;
18
+ readonly millisecond: 0;
19
+ };
11
20
  export declare const zeroTime: Pick<{
12
21
  year: 0;
13
22
  month: 1;
@@ -10,6 +10,7 @@ export const zeroDate = {
10
10
  second: 0,
11
11
  millisecond: 0,
12
12
  };
13
+ export const emptyDate = zeroDate;
13
14
  export const zeroTime = pickObjectKeys(zeroDate, timeFullDateKeys);
14
15
  export function clearTime(inputFullDate) {
15
16
  return clearParts(inputFullDate, timeFullDateKeys);
@@ -1,5 +1,5 @@
1
1
  import { PartialAndUndefined } from '@augment-vir/common';
2
- import { DurationUnit } from '../duration';
2
+ import { DurationUnit } from '../duration/duration-unit';
3
3
  import { FullDate } from '../full-date/full-date-shape';
4
4
  export type RelativeStringOptions = {
5
5
  /**
@@ -1,6 +1,6 @@
1
1
  import { areJsonEqual, mapObjectValues, round } from '@augment-vir/common';
2
- import { diffDatesAllUnits } from '../date-operations/diff-dates';
3
- import { DurationUnit, orderedDurationUnits, singularDurationUnitNames } from '../duration';
2
+ import { DiffType, diffDates } from '../date-operations/diff-dates';
3
+ import { DurationUnit, orderedDurationUnits, singularDurationUnitNames, } from '../duration/duration-unit';
4
4
  const maxDurations = {
5
5
  [DurationUnit.Milliseconds]: 1000,
6
6
  [DurationUnit.Seconds]: 60,
@@ -14,9 +14,11 @@ const maxDurations = {
14
14
  };
15
15
  export function toRelativeString({ fullDate, relativeTo, options = {}, }) {
16
16
  const roundingDigits = options.decimalDigitCount || 0;
17
- const allUnitDiffs = diffDatesAllUnits({
17
+ const allUnitDiffs = diffDates({
18
18
  start: fullDate,
19
19
  end: relativeTo,
20
+ units: orderedDurationUnits,
21
+ diffType: DiffType.EquivalentUnits,
20
22
  });
21
23
  const unitsWithinBounds = mapObjectValues(allUnitDiffs, (durationUnit, duration) => {
22
24
  const roundedDuration = Math.floor(Math.abs(round({ digits: 1, number: duration })));
@@ -1,7 +1,9 @@
1
1
  export * from './date-operations/calculate-relative-date';
2
2
  export * from './date-operations/combine-dates';
3
3
  export * from './date-operations/diff-dates';
4
- export * from './duration';
4
+ export * from './duration/duration';
5
+ export * from './duration/duration-unit';
6
+ export * from './duration/duration-utils';
5
7
  export * from './extra-utils/date-transforms';
6
8
  export * from './extra-utils/has-date-props';
7
9
  export * from './extra-utils/js-date-conversion';
package/dist/esm/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  export * from './date-operations/calculate-relative-date';
2
2
  export * from './date-operations/combine-dates';
3
3
  export * from './date-operations/diff-dates';
4
- export * from './duration';
4
+ export * from './duration/duration';
5
+ export * from './duration/duration-unit';
6
+ export * from './duration/duration-utils';
5
7
  export * from './extra-utils/date-transforms';
6
8
  export * from './extra-utils/has-date-props';
7
9
  export * from './extra-utils/js-date-conversion';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "date-vir",
3
- "version": "4.1.1",
3
+ "version": "5.1.0",
4
4
  "description": "Easy and explicit dates and times",
5
5
  "keywords": [
6
6
  "date",
@@ -41,45 +41,41 @@
41
41
  "test:watch": "web-test-runner --watch --color --config configs/web-test-runner.config.mjs"
42
42
  },
43
43
  "dependencies": {
44
- "@augment-vir/common": "^22.0.0",
45
- "@types/luxon": "^3.3.4",
44
+ "@augment-vir/common": "^22.4.0",
45
+ "@types/luxon": "^3.4.1",
46
46
  "luxon": "^3.4.4",
47
- "object-shape-tester": "^1.0.2",
48
- "type-fest": "^4.8.1"
47
+ "object-shape-tester": "^1.0.3",
48
+ "type-fest": "^4.9.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@augment-vir/browser": "^22.0.0",
52
- "@augment-vir/browser-testing": "^22.0.0",
51
+ "@augment-vir/browser": "^22.4.0",
52
+ "@augment-vir/browser-testing": "^22.4.0",
53
53
  "@open-wc/testing": "^4.0.0",
54
- "@types/chai": "^4.3.10",
55
- "@types/mocha": "^10.0.4",
54
+ "@types/chai": "^4.3.11",
55
+ "@types/mocha": "^10.0.6",
56
56
  "@web/dev-server-esbuild": "^1.0.1",
57
57
  "@web/test-runner": "^0.18.0",
58
58
  "@web/test-runner-commands": "^0.9.0",
59
59
  "@web/test-runner-playwright": "^0.11.0",
60
60
  "@web/test-runner-visual-regression": "^0.9.0",
61
- "cspell": "^8.0.0",
62
- "dependency-cruiser": "^15.3.0",
63
- "element-vir": "^17.0.0",
64
- "esbuild": "^0.19.6",
61
+ "cspell": "^8.3.2",
62
+ "dependency-cruiser": "^16.0.0",
63
+ "element-vir": "^20.0.6",
64
+ "esbuild": "^0.19.11",
65
65
  "istanbul-smart-text-reporter": "^1.1.3",
66
66
  "markdown-code-example-inserter": "^0.3.3",
67
67
  "npm-check-updates": "~16.12.3",
68
- "prettier": "^3.1.0",
68
+ "prettier": "3.2.2",
69
69
  "prettier-plugin-interpolated-html-tags": "^1.0.3",
70
- "prettier-plugin-jsdoc": "^1.1.1",
70
+ "prettier-plugin-jsdoc": "^1.3.0",
71
71
  "prettier-plugin-multiline-arrays": "^3.0.1",
72
72
  "prettier-plugin-organize-imports": "^3.2.4",
73
- "prettier-plugin-packagejson": "^2.4.6",
73
+ "prettier-plugin-packagejson": "^2.4.9",
74
74
  "prettier-plugin-sort-json": "^3.1.0",
75
- "prettier-plugin-toml": "^1.0.0",
76
- "run-time-assertions": "^0.2.1",
77
- "typedoc": "^0.25.3",
78
- "typescript": "^5.2.2",
79
- "virmator": "^11.1.2"
80
- },
81
- "overrides": {
82
- "@open-wc/scoped-elements": "2.2.3",
83
- "@open-wc/testing-helpers": "2.3.0"
75
+ "prettier-plugin-toml": "^2.0.1",
76
+ "run-time-assertions": "^0.3.0",
77
+ "typedoc": "^0.25.7",
78
+ "typescript": "^5.3.3",
79
+ "virmator": "^11.3.1"
84
80
  }
85
81
  }
@@ -1,55 +0,0 @@
1
- import { RequiredAndNotNull } from '@augment-vir/common';
2
- export declare enum DurationUnit {
3
- Years = "years",
4
- Quarters = "quarters",
5
- Months = "months",
6
- Weeks = "weeks",
7
- Days = "days",
8
- Hours = "hours",
9
- Minutes = "minutes",
10
- Seconds = "seconds",
11
- Milliseconds = "milliseconds"
12
- }
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>;
50
- /**
51
- * Not a single date instance, but a description of a date duration. Used to calculate diffs between
52
- * dates, or add offsets to an existing date, or describe a single time duration. Usually only one
53
- * property is set on this at any given time.
54
- */
55
- export type Duration<DurationKeys extends DurationUnit = DurationUnit> = DurationUnit extends DurationKeys ? AnyDuration : Pick<AllDurations, `${DurationKeys}`>;