UNPKG

6.21 kBJavaScriptView Raw
1import { defaultLocale } from "./_lib/defaultLocale.js";
2import { getDefaultOptions } from "./_lib/defaultOptions.js";
3import { getRoundingMethod } from "./_lib/getRoundingMethod.js";
4import { getTimezoneOffsetInMilliseconds } from "./_lib/getTimezoneOffsetInMilliseconds.js";
5import { normalizeDates } from "./_lib/normalizeDates.js";
6import { compareAsc } from "./compareAsc.js";
7import {
8 millisecondsInMinute,
9 minutesInDay,
10 minutesInMonth,
11 minutesInYear,
12} from "./constants.js";
13
14/**
15 * The {@link formatDistanceStrict} function options.
16 */
17
18/**
19 * The unit used to format the distance in {@link formatDistanceStrict}.
20 */
21
22/**
23 * @name formatDistanceStrict
24 * @category Common Helpers
25 * @summary Return the distance between the given dates in words.
26 *
27 * @description
28 * Return the distance between the given dates in words, using strict units.
29 * This is like `formatDistance`, but does not use helpers like 'almost', 'over',
30 * 'less than' and the like.
31 *
32 * | Distance between dates | Result |
33 * |------------------------|---------------------|
34 * | 0 ... 59 secs | [0..59] seconds |
35 * | 1 ... 59 mins | [1..59] minutes |
36 * | 1 ... 23 hrs | [1..23] hours |
37 * | 1 ... 29 days | [1..29] days |
38 * | 1 ... 11 months | [1..11] months |
39 * | 1 ... N years | [1..N] years |
40 *
41 * @param laterDate - The date
42 * @param earlierDate - The date to compare with
43 * @param options - An object with options
44 *
45 * @returns The distance in words
46 *
47 * @throws `date` must not be Invalid Date
48 * @throws `baseDate` must not be Invalid Date
49 * @throws `options.unit` must be 'second', 'minute', 'hour', 'day', 'month' or 'year'
50 * @throws `options.locale` must contain `formatDistance` property
51 *
52 * @example
53 * // What is the distance between 2 July 2014 and 1 January 2015?
54 * const result = formatDistanceStrict(new Date(2014, 6, 2), new Date(2015, 0, 2))
55 * //=> '6 months'
56 *
57 * @example
58 * // What is the distance between 1 January 2015 00:00:15
59 * // and 1 January 2015 00:00:00?
60 * const result = formatDistanceStrict(
61 * new Date(2015, 0, 1, 0, 0, 15),
62 * new Date(2015, 0, 1, 0, 0, 0)
63 * )
64 * //=> '15 seconds'
65 *
66 * @example
67 * // What is the distance from 1 January 2016
68 * // to 1 January 2015, with a suffix?
69 * const result = formatDistanceStrict(new Date(2015, 0, 1), new Date(2016, 0, 1), {
70 * addSuffix: true
71 * })
72 * //=> '1 year ago'
73 *
74 * @example
75 * // What is the distance from 1 January 2016
76 * // to 1 January 2015, in minutes?
77 * const result = formatDistanceStrict(new Date(2016, 0, 1), new Date(2015, 0, 1), {
78 * unit: 'minute'
79 * })
80 * //=> '525600 minutes'
81 *
82 * @example
83 * // What is the distance from 1 January 2015
84 * // to 28 January 2015, in months, rounded up?
85 * const result = formatDistanceStrict(new Date(2015, 0, 28), new Date(2015, 0, 1), {
86 * unit: 'month',
87 * roundingMethod: 'ceil'
88 * })
89 * //=> '1 month'
90 *
91 * @example
92 * // What is the distance between 1 August 2016 and 1 January 2015 in Esperanto?
93 * import { eoLocale } from 'date-fns/locale/eo'
94 * const result = formatDistanceStrict(new Date(2016, 7, 1), new Date(2015, 0, 1), {
95 * locale: eoLocale
96 * })
97 * //=> '1 jaro'
98 */
99
100export function formatDistanceStrict(laterDate, earlierDate, options) {
101 const defaultOptions = getDefaultOptions();
102 const locale = options?.locale ?? defaultOptions.locale ?? defaultLocale;
103
104 const comparison = compareAsc(laterDate, earlierDate);
105
106 if (isNaN(comparison)) {
107 throw new RangeError("Invalid time value");
108 }
109
110 const localizeOptions = Object.assign({}, options, {
111 addSuffix: options?.addSuffix,
112 comparison: comparison,
113 });
114
115 const [laterDate_, earlierDate_] = normalizeDates(
116 options?.in,
117 ...(comparison > 0 ? [earlierDate, laterDate] : [laterDate, earlierDate]),
118 );
119
120 const roundingMethod = getRoundingMethod(options?.roundingMethod ?? "round");
121
122 const milliseconds = earlierDate_.getTime() - laterDate_.getTime();
123 const minutes = milliseconds / millisecondsInMinute;
124
125 const timezoneOffset =
126 getTimezoneOffsetInMilliseconds(earlierDate_) -
127 getTimezoneOffsetInMilliseconds(laterDate_);
128
129 // Use DST-normalized difference in minutes for years, months and days;
130 // use regular difference in minutes for hours, minutes and seconds.
131 const dstNormalizedMinutes =
132 (milliseconds - timezoneOffset) / millisecondsInMinute;
133
134 const defaultUnit = options?.unit;
135 let unit;
136 if (!defaultUnit) {
137 if (minutes < 1) {
138 unit = "second";
139 } else if (minutes < 60) {
140 unit = "minute";
141 } else if (minutes < minutesInDay) {
142 unit = "hour";
143 } else if (dstNormalizedMinutes < minutesInMonth) {
144 unit = "day";
145 } else if (dstNormalizedMinutes < minutesInYear) {
146 unit = "month";
147 } else {
148 unit = "year";
149 }
150 } else {
151 unit = defaultUnit;
152 }
153
154 // 0 up to 60 seconds
155 if (unit === "second") {
156 const seconds = roundingMethod(milliseconds / 1000);
157 return locale.formatDistance("xSeconds", seconds, localizeOptions);
158
159 // 1 up to 60 mins
160 } else if (unit === "minute") {
161 const roundedMinutes = roundingMethod(minutes);
162 return locale.formatDistance("xMinutes", roundedMinutes, localizeOptions);
163
164 // 1 up to 24 hours
165 } else if (unit === "hour") {
166 const hours = roundingMethod(minutes / 60);
167 return locale.formatDistance("xHours", hours, localizeOptions);
168
169 // 1 up to 30 days
170 } else if (unit === "day") {
171 const days = roundingMethod(dstNormalizedMinutes / minutesInDay);
172 return locale.formatDistance("xDays", days, localizeOptions);
173
174 // 1 up to 12 months
175 } else if (unit === "month") {
176 const months = roundingMethod(dstNormalizedMinutes / minutesInMonth);
177 return months === 12 && defaultUnit !== "month"
178 ? locale.formatDistance("xYears", 1, localizeOptions)
179 : locale.formatDistance("xMonths", months, localizeOptions);
180
181 // 1 year up to max Date
182 } else {
183 const years = roundingMethod(dstNormalizedMinutes / minutesInYear);
184 return locale.formatDistance("xYears", years, localizeOptions);
185 }
186}
187
188// Fallback for modularized imports:
189export default formatDistanceStrict;