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/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..acfe9c3 --- /dev/null +++ b/src/ts/get-interval-from-now-formatted/index.ts @@ -0,0 +1,39 @@ +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 +): 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'); +}