UNPKG

i18n-js

Version:

A small library to provide I18n on JavaScript.

154 lines (119 loc) 3.99 kB
import range from "lodash/range"; import { I18n } from "../I18n"; import { DateTime, TimeAgoInWordsOptions } from "../typing"; import { parseDate } from "./parseDate"; const within = (start: number, end: number, actual: number): boolean => actual >= start && actual <= end; /** * Reports the approximate distance in time between two dates. * * @private * * @param {I18n} i18n The `I18n` instance. * * @param {DateTime} fromTime The initial date. * * @param {DateTime} toTime The end date. * @param {TimeAgoInWordsOptions} options Options. * @return {string} The approximate distance between the dates. */ export function timeAgoInWords( i18n: I18n, fromTime: DateTime, toTime: DateTime, options: TimeAgoInWordsOptions = {}, ): string { const scope = options.scope || "datetime.distance_in_words"; const t = (name: string, count = 0): string => i18n.t(name, { count, scope }); fromTime = parseDate(fromTime); toTime = parseDate(toTime); let fromInSeconds = fromTime.getTime() / 1000; let toInSeconds = toTime.getTime() / 1000; if (fromInSeconds > toInSeconds) { [fromTime, toTime, fromInSeconds, toInSeconds] = [ toTime, fromTime, toInSeconds, fromInSeconds, ]; } const distanceInSeconds = Math.round(toInSeconds - fromInSeconds); const distanceInMinutes = Math.round((toInSeconds - fromInSeconds) / 60); const distanceInHours = distanceInMinutes / 60; const distanceInDays = distanceInHours / 24; const distanceInHoursRounded = Math.round(distanceInMinutes / 60); const distanceInDaysRounded = Math.round(distanceInDays); const distanceInMonthsRounded = Math.round(distanceInDaysRounded / 30); if (within(0, 1, distanceInMinutes)) { if (!options.includeSeconds) { return distanceInMinutes === 0 ? t("less_than_x_minutes", 1) : t("x_minutes", distanceInMinutes); } if (within(0, 4, distanceInSeconds)) { return t("less_than_x_seconds", 5); } if (within(5, 9, distanceInSeconds)) { return t("less_than_x_seconds", 10); } if (within(10, 19, distanceInSeconds)) { return t("less_than_x_seconds", 20); } if (within(20, 39, distanceInSeconds)) { return t("half_a_minute"); } if (within(40, 59, distanceInSeconds)) { return t("less_than_x_minutes", 1); } return t("x_minutes", 1); } if (within(2, 44, distanceInMinutes)) { return t("x_minutes", distanceInMinutes); } if (within(45, 89, distanceInMinutes)) { return t("about_x_hours", 1); } if (within(90, 1439, distanceInMinutes)) { return t("about_x_hours", distanceInHoursRounded); } if (within(1440, 2519, distanceInMinutes)) { return t("x_days", 1); } if (within(2520, 43_199, distanceInMinutes)) { return t("x_days", distanceInDaysRounded); } if (within(43_200, 86_399, distanceInMinutes)) { return t("about_x_months", Math.round(distanceInMinutes / 43200)); } if (within(86_400, 525_599, distanceInMinutes)) { return t("x_months", distanceInMonthsRounded); } let fromYear = fromTime.getFullYear(); if (fromTime.getMonth() + 1 >= 3) { fromYear += 1; } let toYear = toTime.getFullYear(); if (toTime.getMonth() + 1 < 3) { toYear -= 1; } const leapYears = fromYear > toYear ? 0 : range(fromYear, toYear).filter( (year) => new Date(year, 1, 29).getMonth() == 1, ).length; const minutesInYear = 525_600; const minuteOffsetForLeapYear = leapYears * 1440; const minutesWithOffset = distanceInMinutes - minuteOffsetForLeapYear; const distanceInYears = Math.trunc(minutesWithOffset / minutesInYear); const diff = parseFloat( (minutesWithOffset / minutesInYear - distanceInYears).toPrecision(3), ); if (diff < 0.25) { return t("about_x_years", distanceInYears); } if (diff < 0.75) { return t("over_x_years", distanceInYears); } return t("almost_x_years", distanceInYears + 1); }