index.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import { secondsInDay, secondsInHour, secondsInMinute, secondsInMonth, secondsInQuarter, secondsInWeek, secondsInYear } from "../constants/index.js";
  2. import differenceInCalendarDays from "../differenceInCalendarDays/index.js";
  3. import differenceInCalendarMonths from "../differenceInCalendarMonths/index.js";
  4. import differenceInCalendarQuarters from "../differenceInCalendarQuarters/index.js";
  5. import differenceInCalendarWeeks from "../differenceInCalendarWeeks/index.js";
  6. import differenceInCalendarYears from "../differenceInCalendarYears/index.js";
  7. import differenceInHours from "../differenceInHours/index.js";
  8. import differenceInMinutes from "../differenceInMinutes/index.js";
  9. import differenceInSeconds from "../differenceInSeconds/index.js";
  10. import toDate from "../toDate/index.js";
  11. import requiredArgs from "../_lib/requiredArgs/index.js";
  12. /**
  13. * @name intlFormatDistance
  14. * @category Common Helpers
  15. * @summary Formats distance between two dates in a human-readable format
  16. * @description
  17. * The function calculates the difference between two dates and formats it as a human-readable string.
  18. *
  19. * The function will pick the most appropriate unit depending on the distance between dates. For example, if the distance is a few hours, it might return `x hours`. If the distance is a few months, it might return `x months`.
  20. *
  21. * You can also specify a unit to force using it regardless of the distance to get a result like `123456 hours`.
  22. *
  23. * See the table below for the unit picking logic:
  24. *
  25. * | Distance between dates | Result (past) | Result (future) |
  26. * | ---------------------- | -------------- | --------------- |
  27. * | 0 seconds | now | now |
  28. * | 1-59 seconds | X seconds ago | in X seconds |
  29. * | 1-59 minutes | X minutes ago | in X minutes |
  30. * | 1-23 hours | X hours ago | in X hours |
  31. * | 1 day | yesterday | tomorrow |
  32. * | 2-6 days | X days ago | in X days |
  33. * | 7 days | last week | next week |
  34. * | 8 days-1 month | X weeks ago | in X weeks |
  35. * | 1 month | last month | next month |
  36. * | 2-3 months | X months ago | in X months |
  37. * | 1 quarter | last quarter | next quarter |
  38. * | 2-3 quarters | X quarters ago | in X quarters |
  39. * | 1 year | last year | next year |
  40. * | 2+ years | X years ago | in X years |
  41. *
  42. * @param {Date|Number} date - the date
  43. * @param {Date|Number} baseDate - the date to compare with.
  44. * @param {Object} [options] - an object with options.
  45. * @param {String} [options.unit] - formats the distance with the given unit ('year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second').
  46. * @param {String|String[]} [options.locale] - the locale to use.
  47. * @param {String} [options.localeMatcher='best fit'] - the locale matching algorithm to use. Other value: 'lookup'.
  48. * See MDN for details [Locale identification and negotiation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locale_identification_and_negotiation)
  49. * @param {String} [options.numeric='auto'] - the output message format. The values are 'auto' (e.g. `yesterday`), 'always'(e.g. `1 day ago`).
  50. * @param {String} [options.style='long'] - the length of the result. The values are: 'long' (e.g. `1 month`), 'short' (e.g. 'in 1 mo.'), 'narrow' (e.g. 'in 1 mo.').
  51. * The narrow one could be similar to the short one for some locales.
  52. * @returns {String} the distance in words according to language-sensitive relative time formatting.
  53. * @throws {TypeError} 2 arguments required
  54. * @throws {RangeError} `date` must not be Invalid Date
  55. * @throws {RangeError} `baseDate` must not be Invalid Date
  56. * @throws {RangeError} `options.unit` must not be invalid Unit
  57. * @throws {RangeError} `options.locale` must not be invalid locale
  58. * @throws {RangeError} `options.localeMatcher` must not be invalid localeMatcher
  59. * @throws {RangeError} `options.numeric` must not be invalid numeric
  60. * @throws {RangeError} `options.style` must not be invalid style
  61. *
  62. * @example
  63. * // What is the distance between the dates when the fist date is after the second?
  64. * intlFormatDistance(
  65. * new Date(1986, 3, 4, 11, 30, 0),
  66. * new Date(1986, 3, 4, 10, 30, 0)
  67. * )
  68. * //=> 'in 1 hour'
  69. *
  70. * // What is the distance between the dates when the fist date is before the second?
  71. * intlFormatDistance(
  72. * new Date(1986, 3, 4, 10, 30, 0),
  73. * new Date(1986, 3, 4, 11, 30, 0)
  74. * )
  75. * //=> '1 hour ago'
  76. *
  77. * @example
  78. * // Use the unit option to force the function to output the result in quarters. Without setting it, the example would return "next year"
  79. * intlFormatDistance(
  80. * new Date(1987, 6, 4, 10, 30, 0),
  81. * new Date(1986, 3, 4, 10, 30, 0),
  82. * { unit: 'quarter' }
  83. * )
  84. * //=> 'in 5 quarters'
  85. *
  86. * @example
  87. * // Use the locale option to get the result in Spanish. Without setting it, the example would return "in 1 hour".
  88. * intlFormatDistance(
  89. * new Date(1986, 3, 4, 11, 30, 0),
  90. * new Date(1986, 3, 4, 10, 30, 0),
  91. * { locale: 'es' }
  92. * )
  93. * //=> 'dentro de 1 hora'
  94. *
  95. * @example
  96. * // Use the numeric option to force the function to use numeric values. Without setting it, the example would return "tomorrow".
  97. * intlFormatDistance(
  98. * new Date(1986, 3, 5, 11, 30, 0),
  99. * new Date(1986, 3, 4, 11, 30, 0),
  100. * { numeric: 'always' }
  101. * )
  102. * //=> 'in 1 day'
  103. *
  104. * @example
  105. * // Use the style option to force the function to use short values. Without setting it, the example would return "in 2 years".
  106. * intlFormatDistance(
  107. * new Date(1988, 3, 4, 11, 30, 0),
  108. * new Date(1986, 3, 4, 11, 30, 0),
  109. * { style: 'short' }
  110. * )
  111. * //=> 'in 2 yr'
  112. */
  113. export default function intlFormatDistance(date, baseDate, options) {
  114. requiredArgs(2, arguments);
  115. var value = 0;
  116. var unit;
  117. var dateLeft = toDate(date);
  118. var dateRight = toDate(baseDate);
  119. if (!(options !== null && options !== void 0 && options.unit)) {
  120. // Get the unit based on diffInSeconds calculations if no unit is specified
  121. var diffInSeconds = differenceInSeconds(dateLeft, dateRight); // The smallest unit
  122. if (Math.abs(diffInSeconds) < secondsInMinute) {
  123. value = differenceInSeconds(dateLeft, dateRight);
  124. unit = 'second';
  125. } else if (Math.abs(diffInSeconds) < secondsInHour) {
  126. value = differenceInMinutes(dateLeft, dateRight);
  127. unit = 'minute';
  128. } else if (Math.abs(diffInSeconds) < secondsInDay && Math.abs(differenceInCalendarDays(dateLeft, dateRight)) < 1) {
  129. value = differenceInHours(dateLeft, dateRight);
  130. unit = 'hour';
  131. } else if (Math.abs(diffInSeconds) < secondsInWeek && (value = differenceInCalendarDays(dateLeft, dateRight)) && Math.abs(value) < 7) {
  132. unit = 'day';
  133. } else if (Math.abs(diffInSeconds) < secondsInMonth) {
  134. value = differenceInCalendarWeeks(dateLeft, dateRight);
  135. unit = 'week';
  136. } else if (Math.abs(diffInSeconds) < secondsInQuarter) {
  137. value = differenceInCalendarMonths(dateLeft, dateRight);
  138. unit = 'month';
  139. } else if (Math.abs(diffInSeconds) < secondsInYear) {
  140. if (differenceInCalendarQuarters(dateLeft, dateRight) < 4) {
  141. // To filter out cases that are less than a year but match 4 quarters
  142. value = differenceInCalendarQuarters(dateLeft, dateRight);
  143. unit = 'quarter';
  144. } else {
  145. value = differenceInCalendarYears(dateLeft, dateRight);
  146. unit = 'year';
  147. }
  148. } else {
  149. value = differenceInCalendarYears(dateLeft, dateRight);
  150. unit = 'year';
  151. }
  152. } else {
  153. // Get the value if unit is specified
  154. unit = options === null || options === void 0 ? void 0 : options.unit;
  155. if (unit === 'second') {
  156. value = differenceInSeconds(dateLeft, dateRight);
  157. } else if (unit === 'minute') {
  158. value = differenceInMinutes(dateLeft, dateRight);
  159. } else if (unit === 'hour') {
  160. value = differenceInHours(dateLeft, dateRight);
  161. } else if (unit === 'day') {
  162. value = differenceInCalendarDays(dateLeft, dateRight);
  163. } else if (unit === 'week') {
  164. value = differenceInCalendarWeeks(dateLeft, dateRight);
  165. } else if (unit === 'month') {
  166. value = differenceInCalendarMonths(dateLeft, dateRight);
  167. } else if (unit === 'quarter') {
  168. value = differenceInCalendarQuarters(dateLeft, dateRight);
  169. } else if (unit === 'year') {
  170. value = differenceInCalendarYears(dateLeft, dateRight);
  171. }
  172. }
  173. var rtf = new Intl.RelativeTimeFormat(options === null || options === void 0 ? void 0 : options.locale, {
  174. localeMatcher: options === null || options === void 0 ? void 0 : options.localeMatcher,
  175. numeric: (options === null || options === void 0 ? void 0 : options.numeric) || 'auto',
  176. style: options === null || options === void 0 ? void 0 : options.style
  177. });
  178. return rtf.format(value, unit);
  179. }