Commit c5d2cca7 authored by Mark Florian's avatar Mark Florian

Merge branch '350184-add-duration-formatter' into 'master'

Add a "time in words" formatter for durations

See merge request gitlab-org/gitlab!83740
parents 3e5c16ac b3ce14eb
...@@ -70,8 +70,41 @@ const memoizedLocale = () => { ...@@ -70,8 +70,41 @@ const memoizedLocale = () => {
}; };
}; };
/**
* Registers timeago time duration
*/
const memoizedLocaleDuration = () => {
const cache = [];
const durations = [
() => [s__('Duration|%s seconds')],
() => [s__('Duration|%s seconds')],
() => [s__('Duration|1 minute')],
() => [s__('Duration|%s minutes')],
() => [s__('Duration|1 hour')],
() => [s__('Duration|%s hours')],
() => [s__('Duration|1 day')],
() => [s__('Duration|%s days')],
() => [s__('Duration|1 week')],
() => [s__('Duration|%s weeks')],
() => [s__('Duration|1 month')],
() => [s__('Duration|%s months')],
() => [s__('Duration|1 year')],
() => [s__('Duration|%s years')],
];
return (_, index) => {
if (cache[index]) {
return cache[index];
}
cache[index] = durations[index] && durations[index]();
return cache[index];
};
};
timeago.register(timeagoLanguageCode, memoizedLocale()); timeago.register(timeagoLanguageCode, memoizedLocale());
timeago.register(`${timeagoLanguageCode}-remaining`, memoizedLocaleRemaining()); timeago.register(`${timeagoLanguageCode}-remaining`, memoizedLocaleRemaining());
timeago.register(`${timeagoLanguageCode}-duration`, memoizedLocaleDuration());
let memoizedFormatter = null; let memoizedFormatter = null;
...@@ -133,3 +166,16 @@ export const timeFor = (time, expiredLabel) => { ...@@ -133,3 +166,16 @@ export const timeFor = (time, expiredLabel) => {
} }
return timeago.format(time, `${timeagoLanguageCode}-remaining`).trim(); return timeago.format(time, `${timeagoLanguageCode}-remaining`).trim();
}; };
/**
* Returns a duration of time given an amount.
*
* @param {number} milliseconds - Duration in milliseconds.
* @returns {string} A formatted duration, e.g. "10 minutes".
*/
export const duration = (milliseconds) => {
const now = new Date();
return timeago
.format(now.getTime() - Math.abs(milliseconds), `${timeagoLanguageCode}-duration`)
.trim();
};
...@@ -13409,6 +13409,45 @@ msgstr "" ...@@ -13409,6 +13409,45 @@ msgstr ""
msgid "Duration" msgid "Duration"
msgstr "" msgstr ""
msgid "Duration|%s days"
msgstr ""
msgid "Duration|%s hours"
msgstr ""
msgid "Duration|%s minutes"
msgstr ""
msgid "Duration|%s months"
msgstr ""
msgid "Duration|%s seconds"
msgstr ""
msgid "Duration|%s weeks"
msgstr ""
msgid "Duration|%s years"
msgstr ""
msgid "Duration|1 day"
msgstr ""
msgid "Duration|1 hour"
msgstr ""
msgid "Duration|1 minute"
msgstr ""
msgid "Duration|1 month"
msgstr ""
msgid "Duration|1 week"
msgstr ""
msgid "Duration|1 year"
msgstr ""
msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below." msgid "During this process, you’ll be asked for URLs from GitLab’s side. Use the URLs shown below."
msgstr "" msgstr ""
......
import { getTimeago, localTimeAgo, timeFor } from '~/lib/utils/datetime/timeago_utility'; import { getTimeago, localTimeAgo, timeFor, duration } from '~/lib/utils/datetime/timeago_utility';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import '~/commons/bootstrap'; import '~/commons/bootstrap';
...@@ -66,6 +66,54 @@ describe('TimeAgo utils', () => { ...@@ -66,6 +66,54 @@ describe('TimeAgo utils', () => {
}); });
}); });
describe('duration', () => {
const ONE_DAY = 24 * 60 * 60;
it.each`
secs | formatted
${0} | ${'0 seconds'}
${30} | ${'30 seconds'}
${59} | ${'59 seconds'}
${60} | ${'1 minute'}
${-60} | ${'1 minute'}
${2 * 60} | ${'2 minutes'}
${60 * 60} | ${'1 hour'}
${2 * 60 * 60} | ${'2 hours'}
${ONE_DAY} | ${'1 day'}
${2 * ONE_DAY} | ${'2 days'}
${7 * ONE_DAY} | ${'1 week'}
${14 * ONE_DAY} | ${'2 weeks'}
${31 * ONE_DAY} | ${'1 month'}
${61 * ONE_DAY} | ${'2 months'}
${365 * ONE_DAY} | ${'1 year'}
${365 * 2 * ONE_DAY} | ${'2 years'}
`('formats $secs as "$formatted"', ({ secs, formatted }) => {
const ms = secs * 1000;
expect(duration(ms)).toBe(formatted);
});
// `duration` can be used to format Rails month durations.
// Ensure formatting for quantities such as `2.months.to_i`
// based on ActiveSupport::Duration::SECONDS_PER_MONTH.
// See: https://api.rubyonrails.org/classes/ActiveSupport/Duration.html
const SECONDS_PER_MONTH = 2629746; // 1.month.to_i
it.each`
duration | secs | formatted
${'1.month'} | ${SECONDS_PER_MONTH} | ${'1 month'}
${'2.months'} | ${SECONDS_PER_MONTH * 2} | ${'2 months'}
${'3.months'} | ${SECONDS_PER_MONTH * 3} | ${'3 months'}
`(
'formats ActiveSupport::Duration of `$duration` ($secs) as "$formatted"',
({ secs, formatted }) => {
const ms = secs * 1000;
expect(duration(ms)).toBe(formatted);
},
);
});
describe('localTimeAgo', () => { describe('localTimeAgo', () => {
beforeEach(() => { beforeEach(() => {
document.body.innerHTML = document.body.innerHTML =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment