diff --git a/app/webpacker/components/CompetitionsOverview/ListViewSection.js b/app/webpacker/components/CompetitionsOverview/ListViewSection.js
index 2aaf6f47cb..fdd6fdecc7 100644
--- a/app/webpacker/components/CompetitionsOverview/ListViewSection.js
+++ b/app/webpacker/components/CompetitionsOverview/ListViewSection.js
@@ -4,6 +4,7 @@ import {
} from 'semantic-ui-react';
import { BarLoader } from 'react-spinners';
+import { DateTime } from 'luxon';
import I18n from '../../lib/i18n';
import {
computeAnnouncementStatus,
@@ -21,7 +22,7 @@ import {
} from '../../lib/utils/competition-table';
import { countries } from '../../lib/wca-data.js.erb';
import { adminCompetitionUrl, competitionUrl } from '../../lib/requests/routes.js.erb';
-import { dateRange } from '../../lib/utils/dates';
+import { dateRange, toRelativeOptions } from '../../lib/utils/dates';
function ListViewSection({
competitions,
@@ -450,22 +451,42 @@ function RegistrationStatus({ comp, isLoading }) {
return (
}
- content={I18n.t('competitions.index.tooltips.registration.opens_in', { duration: comp.time_until_registration })}
+ content={
+ I18n.t(
+ 'competitions.index.tooltips.registration.opens_in',
+ {
+ relativeDate: DateTime.fromISO(comp.registration_open).toRelative(
+ toRelativeOptions.default,
+ ),
+ },
+ )
+ }
position="top center"
size="tiny"
/>
);
}
+
if (comp.registration_status === 'past') {
return (
}
- content={I18n.t('competitions.index.tooltips.registration.closed', { days: I18n.t('common.days', { count: dayDifferenceFromToday(comp.start_date) }) })}
+ content={
+ I18n.t(
+ 'competitions.index.tooltips.registration.closed',
+ {
+ relativeDate: DateTime.fromISO(comp.start_date).toRelative(
+ toRelativeOptions.roundUpAndAtBestDayPrecision,
+ ),
+ },
+ )
+ }
position="top center"
size="tiny"
/>
);
}
+
if (comp.registration_status === 'full') {
return (
);
}
+
if (comp.registration_status === 'open') {
return (
{
switch (competingStatus) {
@@ -24,10 +25,10 @@ const competingStatusIcon = (competingStatus) => {
const registrationStatusIconText = (competition) => {
if (competition.registration_status === 'not_yet_opened') {
- return I18n.t('competitions.index.tooltips.registration.opens_in', { duration: DateTime.fromISO(competition.registration_open).toRelative({ locale: window.I18n.locale }) });
+ return I18n.t('competitions.index.tooltips.registration.opens_in', { relativeDate: DateTime.fromISO(competition.registration_open).toRelative(toRelativeOptions.default) });
}
if (competition.registration_status === 'past') {
- return I18n.t('competitions.index.tooltips.registration.closed', { days: DateTime.fromISO(competition.start_date).toRelative({ locale: window.I18n.locale }) });
+ return I18n.t('competitions.index.tooltips.registration.closed', { relativeDate: DateTime.fromISO(competition.start_date).toRelative(toRelativeOptions.roundUpAndAtBestDayPrecision) });
}
if (competition.registration_status === 'full') {
return I18n.t('competitions.index.tooltips.registration.full');
diff --git a/app/webpacker/lib/utils/dates.js b/app/webpacker/lib/utils/dates.js
index 5523488962..83420c4871 100644
--- a/app/webpacker/lib/utils/dates.js
+++ b/app/webpacker/lib/utils/dates.js
@@ -1,9 +1,23 @@
import { DateTime, Interval } from 'luxon';
+
// parameter name conventions:
// - `luxonDate` for luxon DateTime objects
// - `date` for date-only ISO strings (no time)
// - `dateTime` for date-and-time ISO strings
+export const toRelativeOptions = {
+ default: {
+ locale: window.I18n.locale,
+ },
+ roundUpAndAtBestDayPrecision: {
+ locale: window.I18n.locale,
+ // don't be more precise than "days" (i.e. no hours/minutes/seconds)
+ unit: ['years', 'months', 'weeks', 'days'],
+ // round up, e.g. in 8 hours -> pads to 1 day 8 hours -> rounds to "in 1 day"
+ padding: 24 * 60 * 60 * 1000,
+ },
+};
+
/// / luxon parameters
export const areOnSameDate = (
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 3e2cc0faaf..bcba370284 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1575,10 +1575,10 @@ en:
recent: "Last %{count} days"
#context: the registration status icon
registration:
- opens_in: "Registration will open in %{duration}"
- closed: "Registration has closed. Competition starts in %{days}"
+ opens_in: "Registration will open %{relativeDate}."
+ closed: "Registration has closed. Competition will start %{relativeDate}."
open: "Registration is open!"
- full: "Competitor limit reached, new registrations will go to the waiting list"
+ full: "Competitor limit reached, new registrations will go to the waiting list."
no_more_comps: "No more competitions."
no_comp_found: "No competitions found."
no_comp_match: "We didn't find any competitions with %{events}! Try searching for fewer events."