UNPKG

6.39 kBJavaScriptView Raw
1import { defaultLocale } from "./_lib/defaultLocale.mjs";
2import { getDefaultOptions } from "./_lib/defaultOptions.mjs";
3import { getRoundingMethod } from "./_lib/getRoundingMethod.mjs";
4import { getTimezoneOffsetInMilliseconds } from "./_lib/getTimezoneOffsetInMilliseconds.mjs";
5import { compareAsc } from "./compareAsc.mjs";
6import {
7 millisecondsInMinute,
8 minutesInDay,
9 minutesInMonth,
10 minutesInYear,
11} from "./constants.mjs";
12import { toDate } from "./toDate.mjs";
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 * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
42 *
43 * @param date - The date
44 * @param baseDate - The date to compare with
45 * @param options - An object with options
46 *
47 * @returns The distance in words
48 *
49 * @throws `date` must not be Invalid Date
50 * @throws `baseDate` must not be Invalid Date
51 * @throws `options.unit` must be 'second', 'minute', 'hour', 'day', 'month' or 'year'
52 * @throws `options.locale` must contain `formatDistance` property
53 *
54 * @example
55 * // What is the distance between 2 July 2014 and 1 January 2015?
56 * const result = formatDistanceStrict(new Date(2014, 6, 2), new Date(2015, 0, 2))
57 * //=> '6 months'
58 *
59 * @example
60 * // What is the distance between 1 January 2015 00:00:15
61 * // and 1 January 2015 00:00:00?
62 * const result = formatDistanceStrict(
63 * new Date(2015, 0, 1, 0, 0, 15),
64 * new Date(2015, 0, 1, 0, 0, 0)
65 * )
66 * //=> '15 seconds'
67 *
68 * @example
69 * // What is the distance from 1 January 2016
70 * // to 1 January 2015, with a suffix?
71 * const result = formatDistanceStrict(new Date(2015, 0, 1), new Date(2016, 0, 1), {
72 * addSuffix: true
73 * })
74 * //=> '1 year ago'
75 *
76 * @example
77 * // What is the distance from 1 January 2016
78 * // to 1 January 2015, in minutes?
79 * const result = formatDistanceStrict(new Date(2016, 0, 1), new Date(2015, 0, 1), {
80 * unit: 'minute'
81 * })
82 * //=> '525600 minutes'
83 *
84 * @example
85 * // What is the distance from 1 January 2015
86 * // to 28 January 2015, in months, rounded up?
87 * const result = formatDistanceStrict(new Date(2015, 0, 28), new Date(2015, 0, 1), {
88 * unit: 'month',
89 * roundingMethod: 'ceil'
90 * })
91 * //=> '1 month'
92 *
93 * @example
94 * // What is the distance between 1 August 2016 and 1 January 2015 in Esperanto?
95 * import { eoLocale } from 'date-fns/locale/eo'
96 * const result = formatDistanceStrict(new Date(2016, 7, 1), new Date(2015, 0, 1), {
97 * locale: eoLocale
98 * })
99 * //=> '1 jaro'
100 */
101
102export function formatDistanceStrict(date, baseDate, options) {
103 const defaultOptions = getDefaultOptions();
104 const locale = options?.locale ?? defaultOptions.locale ?? defaultLocale;
105
106 const comparison = compareAsc(date, baseDate);
107
108 if (isNaN(comparison)) {
109 throw new RangeError("Invalid time value");
110 }
111
112 const localizeOptions = Object.assign({}, options, {
113 addSuffix: options?.addSuffix,
114 comparison: comparison,
115 });
116
117 let dateLeft;
118 let dateRight;
119 if (comparison > 0) {
120 dateLeft = toDate(baseDate);
121 dateRight = toDate(date);
122 } else {
123 dateLeft = toDate(date);
124 dateRight = toDate(baseDate);
125 }
126
127 const roundingMethod = getRoundingMethod(options?.roundingMethod ?? "round");
128
129 const milliseconds = dateRight.getTime() - dateLeft.getTime();
130 const minutes = milliseconds / millisecondsInMinute;
131
132 const timezoneOffset =
133 getTimezoneOffsetInMilliseconds(dateRight) -
134 getTimezoneOffsetInMilliseconds(dateLeft);
135
136 // Use DST-normalized difference in minutes for years, months and days;
137 // use regular difference in minutes for hours, minutes and seconds.
138 const dstNormalizedMinutes =
139 (milliseconds - timezoneOffset) / millisecondsInMinute;
140
141 const defaultUnit = options?.unit;
142 let unit;
143 if (!defaultUnit) {
144 if (minutes < 1) {
145 unit = "second";
146 } else if (minutes < 60) {
147 unit = "minute";
148 } else if (minutes < minutesInDay) {
149 unit = "hour";
150 } else if (dstNormalizedMinutes < minutesInMonth) {
151 unit = "day";
152 } else if (dstNormalizedMinutes < minutesInYear) {
153 unit = "month";
154 } else {
155 unit = "year";
156 }
157 } else {
158 unit = defaultUnit;
159 }
160
161 // 0 up to 60 seconds
162 if (unit === "second") {
163 const seconds = roundingMethod(milliseconds / 1000);
164 return locale.formatDistance("xSeconds", seconds, localizeOptions);
165
166 // 1 up to 60 mins
167 } else if (unit === "minute") {
168 const roundedMinutes = roundingMethod(minutes);
169 return locale.formatDistance("xMinutes", roundedMinutes, localizeOptions);
170
171 // 1 up to 24 hours
172 } else if (unit === "hour") {
173 const hours = roundingMethod(minutes / 60);
174 return locale.formatDistance("xHours", hours, localizeOptions);
175
176 // 1 up to 30 days
177 } else if (unit === "day") {
178 const days = roundingMethod(dstNormalizedMinutes / minutesInDay);
179 return locale.formatDistance("xDays", days, localizeOptions);
180
181 // 1 up to 12 months
182 } else if (unit === "month") {
183 const months = roundingMethod(dstNormalizedMinutes / minutesInMonth);
184 return months === 12 && defaultUnit !== "month"
185 ? locale.formatDistance("xYears", 1, localizeOptions)
186 : locale.formatDistance("xMonths", months, localizeOptions);
187
188 // 1 year up to max Date
189 } else {
190 const years = roundingMethod(dstNormalizedMinutes / minutesInYear);
191 return locale.formatDistance("xYears", years, localizeOptions);
192 }
193}
194
195// Fallback for modularized imports:
196export default formatDistanceStrict;