diff --git a/src/app/legacy/psammead/psammead-timestamp-container/src/utilities/index.ts b/src/app/legacy/psammead/psammead-timestamp-container/src/utilities/index.ts index 76e5b73ce6e..50b1b2607d8 100644 --- a/src/app/legacy/psammead/psammead-timestamp-container/src/utilities/index.ts +++ b/src/app/legacy/psammead/psammead-timestamp-container/src/utilities/index.ts @@ -22,6 +22,34 @@ moment.relativeTimeThreshold('h', 24); moment.relativeTimeThreshold('d', 30); moment.relativeTimeThreshold('M', 12); +const sanitiseDuration = (duration: ISODuration) => { + const durationApi = globalThis.Temporal?.Duration; + + if (!durationApi) { + return { total: () => 0 }; + } + + try { + return durationApi.from(duration); + } catch { + return durationApi.from('PT0S'); // fallback to 0 seconds if the duration is invalid + } +}; + +// Extra confirmation +// const normaliseLocale = (locale?: string): string | undefined => { +// if (!locale) return undefined; +// const transformedLocaleToBCP47LangTag = locale.replace(/_/g, '-'); +// try { +// return Intl.getCanonicalLocales(transformedLocaleToBCP47LangTag)[0]; +// } catch { +// return undefined; +// } +// }; + +const transformLocaleToBCP47LangTag = (locale: Locale) => + locale.replace(/_/g, '-'); + export const formatDuration = ({ duration, format, @@ -31,12 +59,45 @@ export const formatDuration = ({ format?: string; locale?: Locale; }): string => { - const defaultDurationFormat = duration?.includes('H') ? 'h:mm:ss' : 'mm:ss'; - const durationInMilliseconds = moment.duration(duration).asMilliseconds(); - return moment - .utc(durationInMilliseconds) - .locale(locale) - .format(format || defaultDurationFormat); + const formattedLocale = transformLocaleToBCP47LangTag(locale) || undefined; + + const totalSeconds = sanitiseDuration(duration).total({ + unit: 'seconds', + }); + const hours = Math.floor(totalSeconds / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const seconds = Math.floor(totalSeconds % 60); + + // note, using Intl.NumberFormat and Intl.Locale does not take into account overrides in psammead-locales/moment + const translateDigits = (timeValueAsNumber: number, minDigits: number) => + // Using this instead of src/app/legacy/psammead/psammead-locales/src/numerals/index.js since this is a safe usecase. + new Intl.NumberFormat(formattedLocale, { + minimumIntegerDigits: minDigits, + useGrouping: false, + }).format(timeValueAsNumber); + + const withArabicComma = (str: string) => { + if (!formattedLocale) return str; + return new Intl.Locale(formattedLocale).maximize().script === 'Arab' + ? str.replace(/,/g, '\u060C') + : str; + }; + + if (format) { + return withArabicComma( + format + .replace('h', translateDigits(hours, 1)) + .replace('mm', translateDigits(minutes, 2)) + .replace('ss', translateDigits(seconds, 2)) + .replace('m', translateDigits(minutes, 1)), + ); + } + + return withArabicComma( + hours > 0 + ? `${translateDigits(hours, 1)}:${translateDigits(minutes, 2)}:${translateDigits(seconds, 2)}` + : `${translateDigits(minutes, 2)}:${translateDigits(seconds, 2)}`, + ); }; // if the date is invalid return false - https://stackoverflow.com/questions/1353684/detecting-an-invalid-date-date-instance-in-javascript#answer-1353711 diff --git a/src/testHelpers/jest-setup.js b/src/testHelpers/jest-setup.js index f50d1d857e4..c655f091437 100644 --- a/src/testHelpers/jest-setup.js +++ b/src/testHelpers/jest-setup.js @@ -1,5 +1,6 @@ /* eslint-disable max-classes-per-file */ import { jest } from '@jest/globals'; +import 'temporal-polyfill/global'; import path from 'path'; import { TextEncoder, TextDecoder } from 'util'; import { ReadableStream } from 'node:stream/web'; diff --git a/ws-nextjs-app/setupTests.ts b/ws-nextjs-app/setupTests.ts index b8860e92d74..db18a7e860f 100644 --- a/ws-nextjs-app/setupTests.ts +++ b/ws-nextjs-app/setupTests.ts @@ -1,4 +1,5 @@ import '@testing-library/jest-dom'; +import 'temporal-polyfill/global'; import { TextEncoder, TextDecoder } from 'node:util'; import { ReadableStream } from 'node:stream/web'; import { MessageChannel, MessagePort } from 'node:worker_threads';