From e7360e0ac8cc7cff8a100dee0e7db2c1229a0e1e Mon Sep 17 00:00:00 2001 From: Alex Oliveira Date: Thu, 1 May 2025 00:00:30 +0100 Subject: [PATCH 1/2] feat: add getIntervalFromNowFormatted --- .../get-interval-from-now-formatted.spec.ts | 65 +++++++++++++++++++ .../get-interval-from-now-formatted/index.ts | 35 ++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/ts/get-interval-from-now-formatted/get-interval-from-now-formatted.spec.ts create mode 100644 src/ts/get-interval-from-now-formatted/index.ts diff --git a/src/ts/get-interval-from-now-formatted/get-interval-from-now-formatted.spec.ts b/src/ts/get-interval-from-now-formatted/get-interval-from-now-formatted.spec.ts new file mode 100644 index 0000000..fdfb3a0 --- /dev/null +++ b/src/ts/get-interval-from-now-formatted/get-interval-from-now-formatted.spec.ts @@ -0,0 +1,65 @@ +import { expect, it, describe, vi, beforeEach } from 'vitest'; +import getIntevalFromNowFormatted, { INTERVAL_DATE_ERR } from '.'; + +describe('getRemainingTimeFormatted', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + it('should return formatted time remaining in the future', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60 * 24); // 1 day in the future + const locale = 'en-US'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('in 1 day'); + }); + + it('should return formatted time remaining in the past', () => { + const pastDate = new Date(Date.now() - 1000 * 60 * 60 * 24); // 1 day in the past + const locale = 'en-US'; + const result = getIntevalFromNowFormatted(pastDate, locale); + expect(result).toBe('1 day ago'); + }); + + it('should return formatted time for 12 hours in the future', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60 * 12); // 12 hours in the future + const locale = 'en-US'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('in 12 hours'); + }); + + it('should return formatted time for 12 minutes in the future', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 12); // 12 minutes in the future + const locale = 'en-US'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('in 12 minutes'); + }); + + it('should return formatted time in Portuguese', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60 * 24); // 1 day in the future + const locale = 'pt-BR'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('em 1 dia'); + }); + + it('should return formatted time in Europe Portuguese', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60 * 24); // 1 day in the future + const locale = 'pt-PT'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('dentro de 1 dia'); + }); + + it('should return formatted time in Spanish', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60 * 24); // 1 day in the future + const locale = 'es-ES'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('dentro de 1 día'); + }); + + it('should throw for invalid date', () => { + const invalidDate = new Date('invalid-date'); + const locale = 'en-US'; + expect(() => getIntevalFromNowFormatted(invalidDate, locale)).toThrowError( + INTERVAL_DATE_ERR + ); + }); +}); diff --git a/src/ts/get-interval-from-now-formatted/index.ts b/src/ts/get-interval-from-now-formatted/index.ts new file mode 100644 index 0000000..d043045 --- /dev/null +++ b/src/ts/get-interval-from-now-formatted/index.ts @@ -0,0 +1,35 @@ +export const INTERVAL_DATE_ERR = + 'Invalid date provided. Please provide a valid Date object.'; + +export default function getIntervalFromNowFormatted( + date: Date, + locale: Intl.LocalesArgument +): string { + if (!(date instanceof Date) || isNaN(date.getTime())) { + throw new TypeError(INTERVAL_DATE_ERR); + } + + const now = new Date(); + const diffMs = date.getTime() - now.getTime(); + + const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'always' }); + + const seconds = diffMs / 1000; + const minutes = seconds / 60; + const hours = minutes / 60; + const days = hours / 24; + + if (Math.abs(days) >= 1) { + return rtf.format(Math.round(days), 'day'); + } + + if (Math.abs(hours) >= 1) { + return rtf.format(Math.round(hours), 'hour'); + } + + if (Math.abs(minutes) >= 1) { + return rtf.format(Math.round(minutes), 'minute'); + } + + return rtf.format(Math.round(seconds), 'second'); +} From dc3eb3fbfa0ab33369b6136a4ef5a70ac58eb793 Mon Sep 17 00:00:00 2001 From: Alex Oliveira Date: Thu, 1 May 2025 00:02:11 +0100 Subject: [PATCH 2/2] add new snippet --- .../get-interval-from-now-formatted.spec.js | 65 +++++++++++++++++++ .../get-interval-from-now-formatted/index.js | 41 ++++++++++++ .../get-interval-from-now-formatted/index.ts | 4 ++ 3 files changed, 110 insertions(+) create mode 100644 src/js/get-interval-from-now-formatted/get-interval-from-now-formatted.spec.js create mode 100644 src/js/get-interval-from-now-formatted/index.js diff --git a/src/js/get-interval-from-now-formatted/get-interval-from-now-formatted.spec.js b/src/js/get-interval-from-now-formatted/get-interval-from-now-formatted.spec.js new file mode 100644 index 0000000..fdfb3a0 --- /dev/null +++ b/src/js/get-interval-from-now-formatted/get-interval-from-now-formatted.spec.js @@ -0,0 +1,65 @@ +import { expect, it, describe, vi, beforeEach } from 'vitest'; +import getIntevalFromNowFormatted, { INTERVAL_DATE_ERR } from '.'; + +describe('getRemainingTimeFormatted', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + it('should return formatted time remaining in the future', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60 * 24); // 1 day in the future + const locale = 'en-US'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('in 1 day'); + }); + + it('should return formatted time remaining in the past', () => { + const pastDate = new Date(Date.now() - 1000 * 60 * 60 * 24); // 1 day in the past + const locale = 'en-US'; + const result = getIntevalFromNowFormatted(pastDate, locale); + expect(result).toBe('1 day ago'); + }); + + it('should return formatted time for 12 hours in the future', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60 * 12); // 12 hours in the future + const locale = 'en-US'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('in 12 hours'); + }); + + it('should return formatted time for 12 minutes in the future', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 12); // 12 minutes in the future + const locale = 'en-US'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('in 12 minutes'); + }); + + it('should return formatted time in Portuguese', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60 * 24); // 1 day in the future + const locale = 'pt-BR'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('em 1 dia'); + }); + + it('should return formatted time in Europe Portuguese', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60 * 24); // 1 day in the future + const locale = 'pt-PT'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('dentro de 1 dia'); + }); + + it('should return formatted time in Spanish', () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60 * 24); // 1 day in the future + const locale = 'es-ES'; + const result = getIntevalFromNowFormatted(futureDate, locale); + expect(result).toBe('dentro de 1 día'); + }); + + it('should throw for invalid date', () => { + const invalidDate = new Date('invalid-date'); + const locale = 'en-US'; + expect(() => getIntevalFromNowFormatted(invalidDate, locale)).toThrowError( + INTERVAL_DATE_ERR + ); + }); +}); diff --git a/src/js/get-interval-from-now-formatted/index.js b/src/js/get-interval-from-now-formatted/index.js new file mode 100644 index 0000000..6836027 --- /dev/null +++ b/src/js/get-interval-from-now-formatted/index.js @@ -0,0 +1,41 @@ +export const INTERVAL_DATE_ERR = + 'Invalid date provided. Please provide a valid Date object.'; + +/** + * Formats the time interval between the given date and the current date + * into a human-readable relative time string. + * + * @param {Date} date - The target date to calculate the interval from now. + * @param {string} locale - A string with a BCP 47 language tag to specify the locale for formatting. + * @returns {string} A formatted string representing the relative time interval. + * @throws {TypeError} Throws an error if the provided date is not a valid Date object. + */ +export default function getIntervalFromNowFormatted(date, locale) { + if (!(date instanceof Date) || isNaN(date.getTime())) { + throw new TypeError(INTERVAL_DATE_ERR); + } + + const now = new Date(); + const diffMs = date.getTime() - now.getTime(); + + const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'always' }); + + const seconds = diffMs / 1000; + const minutes = seconds / 60; + const hours = minutes / 60; + const days = hours / 24; + + if (Math.abs(days) >= 1) { + return rtf.format(Math.round(days), 'day'); + } + + if (Math.abs(hours) >= 1) { + return rtf.format(Math.round(hours), 'hour'); + } + + if (Math.abs(minutes) >= 1) { + return rtf.format(Math.round(minutes), 'minute'); + } + + return rtf.format(Math.round(seconds), 'second'); +} diff --git a/src/ts/get-interval-from-now-formatted/index.ts b/src/ts/get-interval-from-now-formatted/index.ts index d043045..acfe9c3 100644 --- a/src/ts/get-interval-from-now-formatted/index.ts +++ b/src/ts/get-interval-from-now-formatted/index.ts @@ -1,6 +1,10 @@ export const INTERVAL_DATE_ERR = 'Invalid date provided. Please provide a valid Date object.'; +/** + * Formats the time interval between the given date and the current date + * into a human-readable relative time string. + */ export default function getIntervalFromNowFormatted( date: Date, locale: Intl.LocalesArgument