date-vir 7.4.3 → 8.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.
@@ -89,25 +89,25 @@ export declare function getEndDate<const SpecificTimezone extends Timezone>(date
89
89
  */
90
90
  export declare const datePositionCalculationShape: import("object-shape-tester").Shape<import("object-shape-tester").Shape<import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TObject<{
91
91
  get: import("@sinclair/typebox").TUnsafe<DateUnit.Month>;
92
- in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Quarter>)[]>;
92
+ in: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TUnsafe<DateUnit.Year>[]>;
93
93
  }> | import("@sinclair/typebox").TObject<{
94
94
  get: import("@sinclair/typebox").TUnsafe<DateUnit.Week>;
95
- in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Quarter>)[]>;
95
+ in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year>)[]>;
96
96
  }> | import("@sinclair/typebox").TObject<{
97
97
  get: import("@sinclair/typebox").TUnsafe<DateUnit.Day>;
98
- in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Quarter> | import("@sinclair/typebox").TUnsafe<DateUnit.Week>)[]>;
98
+ in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Week>)[]>;
99
99
  }> | import("@sinclair/typebox").TObject<{
100
100
  get: import("@sinclair/typebox").TUnsafe<DateUnit.Hour>;
101
- in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Quarter> | import("@sinclair/typebox").TUnsafe<DateUnit.Week> | import("@sinclair/typebox").TUnsafe<DateUnit.Day>)[]>;
101
+ in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Week> | import("@sinclair/typebox").TUnsafe<DateUnit.Day>)[]>;
102
102
  }> | import("@sinclair/typebox").TObject<{
103
103
  get: import("@sinclair/typebox").TUnsafe<DateUnit.Minute>;
104
- in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Quarter> | import("@sinclair/typebox").TUnsafe<DateUnit.Week> | import("@sinclair/typebox").TUnsafe<DateUnit.Day> | import("@sinclair/typebox").TUnsafe<DateUnit.Hour>)[]>;
104
+ in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Week> | import("@sinclair/typebox").TUnsafe<DateUnit.Day> | import("@sinclair/typebox").TUnsafe<DateUnit.Hour>)[]>;
105
105
  }> | import("@sinclair/typebox").TObject<{
106
106
  get: import("@sinclair/typebox").TUnsafe<DateUnit.Second>;
107
- in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Quarter> | import("@sinclair/typebox").TUnsafe<DateUnit.Week> | import("@sinclair/typebox").TUnsafe<DateUnit.Day> | import("@sinclair/typebox").TUnsafe<DateUnit.Hour> | import("@sinclair/typebox").TUnsafe<DateUnit.Minute>)[]>;
107
+ in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Week> | import("@sinclair/typebox").TUnsafe<DateUnit.Day> | import("@sinclair/typebox").TUnsafe<DateUnit.Hour> | import("@sinclair/typebox").TUnsafe<DateUnit.Minute>)[]>;
108
108
  }> | import("@sinclair/typebox").TObject<{
109
109
  get: import("@sinclair/typebox").TUnsafe<DateUnit.Millisecond>;
110
- in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Quarter> | import("@sinclair/typebox").TUnsafe<DateUnit.Week> | import("@sinclair/typebox").TUnsafe<DateUnit.Day> | import("@sinclair/typebox").TUnsafe<DateUnit.Hour> | import("@sinclair/typebox").TUnsafe<DateUnit.Minute> | import("@sinclair/typebox").TUnsafe<DateUnit.Second>)[]>;
110
+ in: import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TUnsafe<DateUnit.Month> | import("@sinclair/typebox").TUnsafe<DateUnit.Year> | import("@sinclair/typebox").TUnsafe<DateUnit.Week> | import("@sinclair/typebox").TUnsafe<DateUnit.Day> | import("@sinclair/typebox").TUnsafe<DateUnit.Hour> | import("@sinclair/typebox").TUnsafe<DateUnit.Minute> | import("@sinclair/typebox").TUnsafe<DateUnit.Second>)[]>;
111
111
  }>)[]>>>;
112
112
  /**
113
113
  * All valid date position calculations. Used for {@link calculateDatePosition}.
@@ -1,4 +1,4 @@
1
- import { check } from '@augment-vir/assert';
1
+ import { assertWrap, check } from '@augment-vir/assert';
2
2
  import { stringify } from '@augment-vir/common';
3
3
  import { DateUnit, oneIndexedDateUnits } from '@date-vir/duration';
4
4
  import { checkValidShape, defineShape, exactShape, unionShape } from 'object-shape-tester';
@@ -128,25 +128,25 @@ export function getEndDate(date, unit) {
128
128
  */
129
129
  export const datePositionCalculationShape = defineShape(unionShape({
130
130
  get: exactShape(DateUnit.Month),
131
- in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Quarter)),
131
+ in: unionShape(exactShape(DateUnit.Year)),
132
132
  }, {
133
133
  get: exactShape(DateUnit.Week),
134
- in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Quarter), exactShape(DateUnit.Month)),
134
+ in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Month)),
135
135
  }, {
136
136
  get: exactShape(DateUnit.Day),
137
- in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Quarter), exactShape(DateUnit.Month), exactShape(DateUnit.Week)),
137
+ in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Month), exactShape(DateUnit.Week)),
138
138
  }, {
139
139
  get: exactShape(DateUnit.Hour),
140
- in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Quarter), exactShape(DateUnit.Month), exactShape(DateUnit.Week), exactShape(DateUnit.Day)),
140
+ in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Month), exactShape(DateUnit.Week), exactShape(DateUnit.Day)),
141
141
  }, {
142
142
  get: exactShape(DateUnit.Minute),
143
- in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Quarter), exactShape(DateUnit.Month), exactShape(DateUnit.Week), exactShape(DateUnit.Day), exactShape(DateUnit.Hour)),
143
+ in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Month), exactShape(DateUnit.Week), exactShape(DateUnit.Day), exactShape(DateUnit.Hour)),
144
144
  }, {
145
145
  get: exactShape(DateUnit.Second),
146
- in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Quarter), exactShape(DateUnit.Month), exactShape(DateUnit.Week), exactShape(DateUnit.Day), exactShape(DateUnit.Hour), exactShape(DateUnit.Minute)),
146
+ in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Month), exactShape(DateUnit.Week), exactShape(DateUnit.Day), exactShape(DateUnit.Hour), exactShape(DateUnit.Minute)),
147
147
  }, {
148
148
  get: exactShape(DateUnit.Millisecond),
149
- in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Quarter), exactShape(DateUnit.Month), exactShape(DateUnit.Week), exactShape(DateUnit.Day), exactShape(DateUnit.Hour), exactShape(DateUnit.Minute), exactShape(DateUnit.Second)),
149
+ in: unionShape(exactShape(DateUnit.Year), exactShape(DateUnit.Month), exactShape(DateUnit.Week), exactShape(DateUnit.Day), exactShape(DateUnit.Hour), exactShape(DateUnit.Minute), exactShape(DateUnit.Second)),
150
150
  }));
151
151
  /**
152
152
  * Calculate the position of the given date's "get" unit within the given "in" unit.
@@ -211,8 +211,10 @@ export function calculateDatePosition(date, calculation) {
211
211
  })
212
212
  : start;
213
213
  const diffUnit = `${calculation.get}s`;
214
- const diff = diffDates({ start: startWithOffset, end: date }, { [diffUnit]: true });
215
- const value = diff[diffUnit];
214
+ const diff = diffDates({ start: startWithOffset, end: date }, {
215
+ [diffUnit]: true,
216
+ });
217
+ const value = assertWrap.isDefined(diff[diffUnit]);
216
218
  const isDayOfWeek = calculation.get === DateUnit.Day && calculation.in === DateUnit.Week;
217
219
  if (!isDayOfWeek && check.isIn(calculation.get, oneIndexedDateUnits)) {
218
220
  return value + 1;
@@ -5,8 +5,8 @@ import { type FullDate } from '../full-date/full-date-shape.js';
5
5
  * provided with the `units` input property, an array is returned with the full diff duration
6
6
  * contained in each entry in the requested unit.
7
7
  *
8
- * Note: when years, quarters, or months are used for the unit, "long term" durations are used to
9
- * calculate the diff. See more details here:
8
+ * Note: when years or months are used for the unit, "long term" durations are used to calculate the
9
+ * diff. See more details here:
10
10
  * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy
11
11
  *
12
12
  * @category Calculation
@@ -1,8 +1,7 @@
1
- import { DurationUnit, flattenUnitSelection, roundDuration, } from '@date-vir/duration';
1
+ import { DurationUnit, flattenUnitsSmallestToLargest, roundDuration, } from '@date-vir/duration';
2
2
  import { toLuxonDateTime } from '../full-date/luxon-date-time-conversion.js';
3
3
  const conversionAccuracies = {
4
4
  [DurationUnit.Years]: 'longterm',
5
- [DurationUnit.Quarters]: 'longterm',
6
5
  [DurationUnit.Months]: 'longterm',
7
6
  [DurationUnit.Weeks]: 'casual',
8
7
  [DurationUnit.Days]: 'casual',
@@ -24,8 +23,8 @@ function getHighestPriorityConversionAccuracy(units) {
24
23
  * provided with the `units` input property, an array is returned with the full diff duration
25
24
  * contained in each entry in the requested unit.
26
25
  *
27
- * Note: when years, quarters, or months are used for the unit, "long term" durations are used to
28
- * calculate the diff. See more details here:
26
+ * Note: when years or months are used for the unit, "long term" durations are used to calculate the
27
+ * diff. See more details here:
29
28
  * https://moment.github.io/luxon/#/math?id=casual-vs-longterm-conversion-accuracy
30
29
  *
31
30
  * @category Calculation
@@ -70,7 +69,7 @@ export function diffDates({ start, end, },
70
69
  units, options = {}) {
71
70
  const luxonDateStart = toLuxonDateTime(start);
72
71
  const luxonDateEnd = toLuxonDateTime(end);
73
- const selectedUnits = flattenUnitSelection(units);
72
+ const selectedUnits = flattenUnitsSmallestToLargest(units);
74
73
  const conversionAccuracy = getHighestPriorityConversionAccuracy(selectedUnits);
75
74
  const additiveDiff = luxonDateEnd
76
75
  .diff(luxonDateStart, selectedUnits, {
@@ -1,5 +1,5 @@
1
+ import { userLocale } from '@date-vir/duration';
1
2
  import { toLuxonDateTime } from '../full-date/luxon-date-time-conversion.js';
2
- import { userLocale } from '../locales.js';
3
3
  /**
4
4
  * Chromium has a weird behavior where the "NARROW NO-BREAK SPACE" character (code 8239) is used
5
5
  * instead of a space (code 32) before AM/PM.
@@ -1,5 +1,5 @@
1
- import { type PartialWithUndefined } from '@augment-vir/common';
2
- import { type AnyDuration, type DurationUnitSelection } from '@date-vir/duration';
1
+ import { type PartialWithUndefined, type RequiredAndNotNull } from '@augment-vir/common';
2
+ import { type AnyDuration, type DurationUnitSelection, type LocaleOptions, type RoundOptions } from '@date-vir/duration';
3
3
  import { type FullDate } from '../full-date/full-date-shape.js';
4
4
  /**
5
5
  * Options for {@link toRelativeString}.
@@ -8,10 +8,11 @@ import { type FullDate } from '../full-date/full-date-shape.js';
8
8
  */
9
9
  export type RelativeStringOptions = PartialWithUndefined<{
10
10
  /**
11
- * Set this to `true` to prevent the `'just now'` relative string from being used when the two
12
- * dates are very close.
11
+ * Set this to `true` to block a "just now" string to be used when the two dates are very close
12
+ * or the duration is very small (or 0).
13
13
  *
14
14
  * @default false // (`'just now'` is used)
15
+ * @see `RelativeStringOptions.justNowThresholds`
15
16
  */
16
17
  blockJustNow: boolean;
17
18
  /**
@@ -22,27 +23,61 @@ export type RelativeStringOptions = PartialWithUndefined<{
22
23
  */
23
24
  useOnlyLargestUnit: boolean;
24
25
  /**
25
- * The number of decimals to allow for each duration unit's value.
26
+ * Any values below this will trigger "just now".
26
27
  *
27
- * @default 0
28
+ * @default `defaultJustNowThresholds`
29
+ * @see {@link defaultJustNowThresholds} .
28
30
  */
29
- allowedDecimals: number;
31
+ justNowThresholds: AnyDuration;
30
32
  /**
31
- * Any values below this will trigger "just now".
33
+ * The separator between each unit.
34
+ *
35
+ * @default ', '
36
+ */
37
+ sep: string;
38
+ /**
39
+ * By default, when `blockJustNow` is `true` and there is no duration to print (the diff is 0),
40
+ * this will return the smallest selected unit set to 0 and use "0 <unit> ago". Set this
41
+ * property to `true` to instead use "in 0 <unit>".
32
42
  *
33
- * @default {
43
+ * @default false
44
+ */
45
+ useFutureWhenNothing: boolean;
46
+ /**
47
+ * If `true`, unit names are abbreviated.
34
48
  *
35
- * minutes: 1.5,
36
- * seconds: 5,
37
- * milliseconds: 500,
38
- * }
49
+ * @default false
50
+ */
51
+ abbreviate: boolean;
52
+ /**
53
+ * Customize translations. If this is not provided, or any property is missing, the default
54
+ * english phrases will be used.
39
55
  */
40
- justNowThresholds: {
41
- minutes: number;
42
- seconds: number;
43
- milliseconds: number;
44
- };
45
- }>;
56
+ i18n: Partial<{
57
+ /** Creates past relative strings like "5 seconds ago". */
58
+ timeAgo(
59
+ /**
60
+ * This will look like "5 seconds" or "1 year", translated to the configured locale (or
61
+ * the user's current locale.
62
+ */
63
+ unitString: string): string;
64
+ /** Creates future relative strings like "in 5 seconds". */
65
+ timeIn(
66
+ /**
67
+ * This will look like "5 seconds" or "1 year", translated to the configured locale (or
68
+ * the user's current locale.
69
+ */
70
+ unitString: string): string;
71
+ /** The string to be used when "just now" is chosen. */
72
+ justNow: string;
73
+ }>;
74
+ }> & RequiredAndNotNull<RoundOptions> & LocaleOptions;
75
+ /**
76
+ * Default value for `RelativeStringOptions.justNowThresholds`.
77
+ *
78
+ * @category Internal
79
+ */
80
+ export declare const defaultJustNowThresholds: AnyDuration;
46
81
  /**
47
82
  * This function starts with a duration (either by being directly passed a duration or by diffing
48
83
  * two dates into a duration) and converts that duration into a relative string like "1 month ago"
@@ -61,8 +96,12 @@ export type RelativeStringOptions = PartialWithUndefined<{
61
96
  * toRelativeString({days: 1.6}, {days: true, hours: true}); // `'in 1 day, 14 hours'`
62
97
  * toRelativeString({seconds: 1}, selectAllDurationUnits); // `'just now'`
63
98
  * ```
99
+ *
100
+ * @throws If no units are selected
64
101
  */
65
102
  export declare function toRelativeString(datesOrDuration: Readonly<{
66
103
  start: Readonly<FullDate>;
67
104
  end: Readonly<FullDate>;
68
- }> | Readonly<AnyDuration>, units: Readonly<DurationUnitSelection>, options?: Readonly<RelativeStringOptions>): string;
105
+ }> | Readonly<AnyDuration>,
106
+ /** The units to use in the relative string. */
107
+ units: Readonly<DurationUnitSelection>, options: Readonly<RelativeStringOptions>): string;
@@ -1,11 +1,25 @@
1
1
  import { check } from '@augment-vir/assert';
2
- import { arrayToObject, filterMap } from '@augment-vir/common';
3
- import { convertDuration, DurationUnit, flattenUnitSelection, singularDurationUnitNames, } from '@date-vir/duration';
2
+ import { filterMap, } from '@augment-vir/common';
3
+ import { convertDuration, flattenUnitsSmallestToLargest, getDateUnitString, } from '@date-vir/duration';
4
4
  import { diffDates } from '../date-operations/diff-dates.js';
5
- const defaultJustNowThresholds = {
5
+ const defaultRelativeStringI18n = {
6
+ justNow: 'just now',
7
+ timeAgo(unitString) {
8
+ return `${unitString} ago`;
9
+ },
10
+ timeIn(unitString) {
11
+ return `in ${unitString}`;
12
+ },
13
+ };
14
+ /**
15
+ * Default value for `RelativeStringOptions.justNowThresholds`.
16
+ *
17
+ * @category Internal
18
+ */
19
+ export const defaultJustNowThresholds = {
6
20
  minutes: 1.5,
7
21
  seconds: 5,
8
- milliseconds: 500,
22
+ milliseconds: 200,
9
23
  };
10
24
  /**
11
25
  * This function starts with a duration (either by being directly passed a duration or by diffing
@@ -25,85 +39,136 @@ const defaultJustNowThresholds = {
25
39
  * toRelativeString({days: 1.6}, {days: true, hours: true}); // `'in 1 day, 14 hours'`
26
40
  * toRelativeString({seconds: 1}, selectAllDurationUnits); // `'just now'`
27
41
  * ```
42
+ *
43
+ * @throws If no units are selected
28
44
  */
29
- export function toRelativeString(datesOrDuration, units, options = {}) {
30
- const selectedUnits = flattenUnitSelection(units);
31
- /** If there are no selected units, return nothing. */
32
- if (!check.isLengthAtLeast(selectedUnits, 1)) {
33
- return '';
45
+ export function toRelativeString(datesOrDuration,
46
+ /** The units to use in the relative string. */
47
+ units, options) {
48
+ /**
49
+ * Don't use Luxon's implementation of this (`DateTime.toRelative()`) because it's a hopeless
50
+ * mess.
51
+ */
52
+ const smallestToLargestSelectedUnit = flattenUnitsSmallestToLargest(units);
53
+ const smallestSelectedUnit = smallestToLargestSelectedUnit[0];
54
+ const largestToSmallestSelectedUnit = smallestToLargestSelectedUnit.toReversed();
55
+ if (smallestSelectedUnit == undefined) {
56
+ throw new Error('No units selected for relative string.');
34
57
  }
35
- const diff = convertDuration('start' in datesOrDuration ? diffDates(datesOrDuration, units) : datesOrDuration, units, {
36
- roundToDigits: options.allowedDecimals || 0,
58
+ const diff = getRelativeStringDiff({
59
+ datesOrDuration,
60
+ options,
61
+ units,
62
+ largestToSmallestSelectedUnit,
37
63
  });
64
+ const unitCounts = {};
38
65
  const isDiffPositive = convertDuration(diff, { milliseconds: true }).milliseconds >= 0;
39
- const unitValues = filterMap(selectedUnits, (unit) => {
40
- const quantity = diff[unit] || 0;
41
- if (!quantity) {
42
- return undefined;
66
+ largestToSmallestSelectedUnit.forEach((unit) => {
67
+ if (options.useOnlyLargestUnit && Object.keys(unitCounts).length) {
68
+ return;
69
+ }
70
+ const unitCount = diff[unit] || 0;
71
+ if (unitCount) {
72
+ unitCounts[unit] = unitCount;
43
73
  }
44
- return {
45
- quantity,
46
- unit,
47
- };
48
- }, check.isTruthy).reverse();
49
- const justNowThresholds = options.justNowThresholds || defaultJustNowThresholds;
50
- const shouldUseJustNow = !options.blockJustNow &&
51
- (!check.isLengthAtLeast(unitValues, 1) ||
52
- (unitValues[0].unit === DurationUnit.Minutes &&
53
- /* node:coverage ignore next 1 */
54
- Math.abs(diff.minutes || 0) < justNowThresholds.minutes) ||
55
- (unitValues[0].unit === DurationUnit.Seconds &&
56
- /* node:coverage ignore next 1 */
57
- Math.abs(diff.seconds || 0) < justNowThresholds.seconds) ||
58
- (unitValues[0].unit === DurationUnit.Milliseconds &&
59
- /* node:coverage ignore next 1 */
60
- Math.abs(diff.milliseconds || 0) < justNowThresholds.milliseconds));
74
+ });
75
+ const shouldUseJustNow = determineShouldUseJustNow({ options, smallestSelectedUnit, unitCounts });
76
+ const i18n = {
77
+ ...defaultRelativeStringI18n,
78
+ ...options.i18n,
79
+ };
61
80
  if (shouldUseJustNow) {
62
- return 'just now';
81
+ return i18n.justNow;
63
82
  }
64
- else if (options.useOnlyLargestUnit) {
65
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
66
- if (!unitValues[0]) {
67
- return '';
83
+ else if (Object.keys(unitCounts).length) {
84
+ const unitStrings = filterMap(largestToSmallestSelectedUnit, (unit) => {
85
+ const unitCount = unitCounts[unit];
86
+ if (unitCount) {
87
+ return getDateUnitString({
88
+ count: Math.abs(unitCount),
89
+ unit,
90
+ locale: options.locale,
91
+ abbreviate: options.abbreviate,
92
+ decimalCount: options.decimalCount,
93
+ });
94
+ }
95
+ else {
96
+ return undefined;
97
+ }
98
+ }, check.isTruthy);
99
+ const allUnitStrings = unitStrings.join(options.sep ?? ', ');
100
+ if (isDiffPositive) {
101
+ return i18n.timeIn(allUnitStrings);
102
+ }
103
+ else {
104
+ return i18n.timeAgo(allUnitStrings);
68
105
  }
69
- return toRelativeString(datesOrDuration, { [unitValues[0].unit]: true }, {
70
- ...options,
71
- useOnlyLargestUnit: false,
72
- });
73
106
  }
74
- else if (unitValues.length < selectedUnits.length) {
75
- /**
76
- * If the finalized units are less than the selected units, rerun the whole calculation to
77
- * make sure we're using the right accuracy.
78
- */
79
- return toRelativeString(datesOrDuration, arrayToObject(unitValues, ({ unit }) => {
80
- return {
81
- key: unit,
82
- value: true,
83
- };
84
- }), options);
107
+ else {
108
+ const unitString = getDateUnitString({
109
+ count: 0,
110
+ unit: smallestSelectedUnit,
111
+ locale: options.locale,
112
+ abbreviate: options.abbreviate,
113
+ decimalCount: options.decimalCount,
114
+ });
115
+ if (options.useFutureWhenNothing) {
116
+ return i18n.timeIn(unitString);
117
+ }
118
+ else {
119
+ return i18n.timeAgo(unitString);
120
+ }
85
121
  }
86
- const unitsString = unitValues
87
- .map(({ quantity, unit }) => {
88
- const absoluteQuantity = Math.abs(quantity);
89
- return [
90
- absoluteQuantity,
91
- ' ',
92
- singularDurationUnitNames[unit],
93
- absoluteQuantity > 1 ? 's' : '',
94
- ].join('');
95
- })
96
- .join(', ');
97
- if (isDiffPositive) {
98
- return [
99
- 'in',
100
- unitsString,
101
- ].join(' ');
122
+ }
123
+ function getRelativeStringDiff({ datesOrDuration, units, options, largestToSmallestSelectedUnit, }) {
124
+ if (options.useOnlyLargestUnit) {
125
+ return findLargestDiff({ datesOrDuration, largestToSmallestSelectedUnit, options });
102
126
  }
103
127
  else {
104
- return [
105
- unitsString,
106
- 'ago',
107
- ].join(' ');
128
+ return createDiff(datesOrDuration, { decimalCount: undefined }, units);
129
+ }
130
+ }
131
+ function createDiff(datesOrDuration, options, units) {
132
+ return 'start' in datesOrDuration
133
+ ? diffDates(datesOrDuration, units, options)
134
+ : convertDuration(datesOrDuration, units, options);
135
+ }
136
+ function findLargestDiff({ datesOrDuration, options, largestToSmallestSelectedUnit, }) {
137
+ for (const unit of largestToSmallestSelectedUnit) {
138
+ const innerDiff = createDiff(datesOrDuration, options, {
139
+ [unit]: true,
140
+ });
141
+ if (innerDiff[unit]) {
142
+ return innerDiff;
143
+ }
144
+ }
145
+ return {};
146
+ }
147
+ function determineShouldUseJustNow({ options, smallestSelectedUnit, unitCounts, }) {
148
+ if (options.blockJustNow) {
149
+ return false;
150
+ }
151
+ const thresholds = {
152
+ ...defaultJustNowThresholds,
153
+ ...options.justNowThresholds,
154
+ };
155
+ if (!(smallestSelectedUnit in thresholds) || Object.keys(unitCounts).length > 1) {
156
+ return false;
157
+ }
158
+ if (!Object.keys(unitCounts).length || smallestSelectedUnit in unitCounts) {
159
+ /* node:coverage disable: these fallbacks aren't required at runtime but are required for type safety. */
160
+ /**
161
+ * We've already verify that `smallestSelectedUnit` is in `thresholds` so the `|| 0`
162
+ * fallback is safe.
163
+ */
164
+ const unitThreshold = thresholds[smallestSelectedUnit] || 0;
165
+ /**
166
+ * We've already verify that `smallestSelectedUnit` is in `unitCounts` so the `|| 0`
167
+ * fallback is safe.
168
+ */
169
+ const unitValue = unitCounts[smallestSelectedUnit] || 0;
170
+ /* node:coverage enable */
171
+ return unitValue <= unitThreshold;
108
172
  }
173
+ return false;
109
174
  }
@@ -8,7 +8,7 @@ import { type FullDate } from './full-date-shape.js';
8
8
  *
9
9
  * @category Internal
10
10
  */
11
- export declare function toLuxonDateTime(fullDateInput: Readonly<FullDate>): DateTime;
11
+ export declare function toLuxonDateTime(fullDateInput: Readonly<FullDate>): DateTime<true>;
12
12
  /**
13
13
  * Convert a Luxon DateTime object into a {@link FullDate} instance. Used internally inside of
14
14
  * date-vir, but could be helpful if you need it. Usually you should prefer using the
package/dist/index.d.ts CHANGED
@@ -23,7 +23,6 @@ export * from './full-date/full-date-shape.js';
23
23
  export * from './full-date/is-valid-full-date.js';
24
24
  export * from './full-date/luxon-date-time-conversion.js';
25
25
  export * from './full-date/parsing.js';
26
- export * from './locales.js';
27
26
  export * from './timezone/timezone-checks.js';
28
27
  export * from './timezone/timezone-names.js';
29
28
  export * from './timezone/timezones.js';
package/dist/index.js CHANGED
@@ -23,7 +23,6 @@ export * from './full-date/full-date-shape.js';
23
23
  export * from './full-date/is-valid-full-date.js';
24
24
  export * from './full-date/luxon-date-time-conversion.js';
25
25
  export * from './full-date/parsing.js';
26
- export * from './locales.js';
27
26
  export * from './timezone/timezone-checks.js';
28
27
  export * from './timezone/timezone-names.js';
29
28
  export * from './timezone/timezones.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "date-vir",
3
- "version": "7.4.3",
3
+ "version": "8.1.0",
4
4
  "description": "Easy and explicit dates and times.",
5
5
  "keywords": [
6
6
  "date",
@@ -34,25 +34,25 @@
34
34
  "test:docs": "virmator docs check"
35
35
  },
36
36
  "dependencies": {
37
- "@augment-vir/assert": "^31.40.0",
38
- "@augment-vir/common": "^31.40.0",
39
- "@date-vir/duration": "^7.4.3",
37
+ "@augment-vir/assert": "^31.54.3",
38
+ "@augment-vir/common": "^31.54.3",
39
+ "@date-vir/duration": "^8.1.0",
40
40
  "@types/luxon": "^3.7.1",
41
41
  "luxon": "^3.7.2",
42
- "object-shape-tester": "^6.9.2",
43
- "type-fest": "^5.0.1"
42
+ "object-shape-tester": "^6.10.0",
43
+ "type-fest": "^5.3.1"
44
44
  },
45
45
  "devDependencies": {
46
- "@augment-vir/test": "^31.40.0",
46
+ "@augment-vir/test": "^31.54.3",
47
47
  "@web/dev-server-esbuild": "^1.0.4",
48
48
  "@web/test-runner": "^0.20.2",
49
49
  "@web/test-runner-commands": "^0.9.0",
50
50
  "@web/test-runner-playwright": "^0.11.1",
51
51
  "@web/test-runner-visual-regression": "^0.10.0",
52
- "element-vir": "^26.10.0",
52
+ "element-vir": "^26.12.1",
53
53
  "istanbul-smart-text-reporter": "^1.1.5",
54
54
  "markdown-code-example-inserter": "^3.0.3",
55
- "typedoc": "^0.28.13",
55
+ "typedoc": "^0.28.15",
56
56
  "typescript": "^5.9.3"
57
57
  },
58
58
  "engines": {
package/dist/locales.d.ts DELETED
@@ -1,6 +0,0 @@
1
- /**
2
- * The current user's default locale, useful for formatting purposes.
3
- *
4
- * @category Formatting
5
- */
6
- export declare const userLocale: string;
package/dist/locales.js DELETED
@@ -1,7 +0,0 @@
1
- import { Settings } from 'luxon';
2
- /**
3
- * The current user's default locale, useful for formatting purposes.
4
- *
5
- * @category Formatting
6
- */
7
- export const userLocale = Settings.defaultLocale;