Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
);
});
});
41 changes: 41 additions & 0 deletions src/js/get-interval-from-now-formatted/index.js
Original file line number Diff line number Diff line change
@@ -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');
}
Original file line number Diff line number Diff line change
@@ -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
);
});
});
39 changes: 39 additions & 0 deletions src/ts/get-interval-from-now-formatted/index.ts
Original file line number Diff line number Diff line change
@@ -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');
}