diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/add.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/add.test.ts new file mode 100644 index 00000000..7e59cf93 --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/add.test.ts @@ -0,0 +1,63 @@ +import add from '../add'; + +describe('dateTime - add', () => { + // Basic Functionality Tests + test('adds days to a date', () => { + const startDate = new Date(2024, 0, 1); // Jan 1, 2024 + expect(add(startDate, {value: 10, unit: 'days'})).toEqual(new Date(2024, 0, 11)); + }); + + test('adds months to a date', () => { + const startDate = new Date(2024, 0, 1); // Jan 1, 2024 + expect(add(startDate,{value: 2, unit: 'months'})).toEqual(new Date(2024, 2, 1)); + }); + + test('adds years to a date', () => { + const startDate = new Date(2024, 0, 1); // Jan 1, 2024 + expect(add(startDate, {value: 1, unit: 'years'})).toEqual(new Date(2025, 0, 1)); + }); + + test('handles negative values', () => { + const startDate = new Date(2024, 0, 10); + expect(add(startDate, {value: -5, unit: 'days'})).toEqual(new Date(2024, 0, 5)); + }); + + test('handles adding zero', () => { + const startDate = new Date(2024, 1, 13); + expect(add(startDate, {value: 0, unit: 'months'})).toEqual(startDate); + }); + + test('handles leap years', () => { + const startDate = new Date(2024, 1, 29); // Feb 29, 2024 + expect(add(startDate, {value: 1, unit: 'years'})).toEqual(new Date(2025, 1, 28)); // Feb 28, 2025 + }); + + test('handles month-end dates', () => { + const startDate = new Date(2024, 0, 31); // Jan 31, 2024 + expect(add(startDate, {value: 1, unit: 'months'})).toEqual(new Date(2024, 1, 29)); // Feb 29, 2024 + }); + + // Invalid Inputs + test('throws error for invalid date string', () => { + expect(() => add('invalid-date', {value: 1, unit: 'days'})).toThrow( + 'Error: Date format not recognized', + ); + }); + + test('throws error for invalid value', () => { + const startDate = new Date(2024, 0, 1); + expect(() => add(startDate, {value: NaN, unit: 'days'})).toThrow( + 'Error: Invalid value passed!', + ); + expect(() => add(startDate, {value: Infinity, unit: 'days'})).toThrow( + 'Error: Invalid value passed!', + ); + }); + + // Type Checking + test('handles Date object and date string inputs', () => { + const startDate = new Date(2024, 0, 1); + const startDateString = '2024-01-01'; + expect(add(startDate, {value: 1, unit: 'days'})).toEqual(add(startDateString, {value: 1, unit: 'days'})); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/formatDate.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/formatDate.test.ts new file mode 100644 index 00000000..69a66629 --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/formatDate.test.ts @@ -0,0 +1,58 @@ +import formatDate from '../formatDate'; +import { DateFormatOptions } from '../types'; + +describe('dateTime - formatDate', () => { + // Basic Functionality Tests + test.each([ + ['2024-01-01', 'en-US', undefined, '1/1/2024'], // US format + ['2024-01-01', 'en-GB', undefined, '01/01/2024'], // UK format + [ + '2024-02-29', + 'en-US', + { + day: '2-digit', + month: '2-digit', + year: 'numeric', + } as DateFormatOptions, + '02/29/2024', + ], // Leap year with specific format + ])( + 'formats date "%s" with locale "%s" and options %o to "%s"', + (date, locale, options, expected) => { + expect(formatDate(date, {locale: locale, intlOptions: options})).toBe(expected); + }, + ); + + test('formats end of year date', () => { + expect(formatDate('2024-12-31', {locale: 'en-US'})).toBe('12/31/2024'); + }); + + test('handles invalid date strings', () => { + expect(() => formatDate('invalid-date', {locale: 'en-US'})).toThrow(); + }); + + // Locale and Option Variations + test('formats date with different locales', () => { + const date = '2024-03-01'; + expect(formatDate(date, {locale: 'fr-FR'})).not.toBe(formatDate(date, {locale: 'de-DE'})); + }); + + test('formats date with different options', () => { + const date = '2024-03-01'; + const options1 = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + } as DateFormatOptions; + const options2 = { + year: '2-digit', + month: 'numeric', + day: 'numeric', + } as DateFormatOptions; + + expect(formatDate(date, {locale: 'en-US', intlOptions: options1})).not.toBe( + formatDate(date, {locale: 'en-US', intlOptions: options2}), + ); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/formatDateTime.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/formatDateTime.test.ts new file mode 100644 index 00000000..a565410e --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/formatDateTime.test.ts @@ -0,0 +1,53 @@ +import formatDateTime from '../formatDateTime'; +import { DateTimeFormatOptions } from '../types'; + +describe('dateTime - formatDateTime', () => { + // Basic Functionality Tests + test.each([ + ['2024-01-01T12:00:00', 'en-US', undefined, '1/1/2024, 12:00:00 PM'], // US format with time + ['2024-01-01T00:00:00', 'en-GB', { hour12: false }, '01/01/2024, 00:00:00'], // UK format with midnight time + ['2024-02-29T15:30:00', 'en-US', { hour12: false }, '2/29/2024, 15:30:00'], // Leap year with 24-hour format + ])( + 'formats date "%s" with locale "%s" and options %o to "%s"', + (date, locale, options, expected) => { + expect(formatDateTime(date, {locale: locale, intlOptions :options})).toBe(expected); + }, + ); + + test('formats end of year date with time', () => { + expect(formatDateTime('2024-12-31T23:59:59', {locale: 'en-US'})).toBe( + '12/31/2024, 11:59:59 PM', + ); + }); + + test('handles invalid date strings', () => { + expect(() => formatDateTime('invalid-date', {locale: 'en-US'})).toThrow(); + }); + + // Locale and Option Variations + test('formats date and time with different locales', () => { + const date = '2024-03-01T20:00:00'; + expect(formatDateTime(date, {locale: 'fr-FR'})).not.toBe( + formatDateTime(date, {locale: 'de-DE'}), + ); + }); + + test('formats date and time with different options', () => { + const date = '2024-03-01T20:00:00'; + const options1 = { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: true, + } as DateTimeFormatOptions; + const options2 = { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false, + } as DateTimeFormatOptions; + expect(formatDateTime(date, {locale: 'en-US', intlOptions :options1})).not.toBe( + formatDateTime(date, {locale: 'en-US', intlOptions: options2}), + ); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/formatTime.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/formatTime.test.ts new file mode 100644 index 00000000..bbc4ebae --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/formatTime.test.ts @@ -0,0 +1,43 @@ +import formatTime from '../formatTime'; +import { DateTimeFormatOptions } from '../types'; + +describe('formatTime function', () => { + // Basic Functionality Tests + test.each([ + ['2024-01-01T12:00:00', 'en-US', undefined, '12:00:00 PM'], // US format 12-hour clock + ['2024-01-01T00:00:00', 'en-GB', { hour12: false }, '00:00:00'], // UK format 24-hour clock + ['2024-01-01T15:30:00', 'en-US', { hour12: false }, '15:30:00'], // US format 24-hour clock + ])( + 'formats time "%s" with locale "%s" and options %o to "%s"', + (date, locale, options, expected) => { + expect(formatTime(date, {locale, intlOptions :options})).toBe(expected); + }, + ); + + test('formats midnight time', () => { + expect(formatTime('2024-01-01T00:00:00', {locale: 'en-US'})).toBe('12:00:00 AM'); + }); + + test('formats end of day time', () => { + expect(formatTime('2024-01-01T23:59:59', {locale: 'en-US'})).toBe('11:59:59 PM'); + }); + + test('formats time with different options', () => { + const date = '2024-03-01T20:00:00'; + const options1 = { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: true, + } as Omit; + const options2 = { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false, + } as Omit; + expect(formatTime(date, {locale: 'en-US', intlOptions :options1})).not.toBe( + formatTime(date, {locale: 'en-US', intlOptions: options2}), + ); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/getQuarter.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/getQuarter.test.ts new file mode 100644 index 00000000..99b473a2 --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/getQuarter.test.ts @@ -0,0 +1,38 @@ +import getQuarter from '../getQuarter'; + +describe('dateTime - getQuarter', () => { + test('returns 1 for dates in the first quarter', () => { + expect(getQuarter('2024-01-01')).toBe(1); // Beginning of Q1 + expect(getQuarter('2024-02-15')).toBe(1); // Middle of Q1 + expect(getQuarter('2024-03-31')).toBe(1); // End of Q1 + }); + + test('returns 2 for dates in the second quarter', () => { + expect(getQuarter('2024-04-01')).toBe(2); // Beginning of Q2 + expect(getQuarter('2024-05-15')).toBe(2); // Middle of Q2 + expect(getQuarter('2024-06-30')).toBe(2); // End of Q2 + }); + + test('returns 3 for dates in the third quarter', () => { + expect(getQuarter('2024-07-01')).toBe(3); // Beginning of Q3 + expect(getQuarter('2024-08-15')).toBe(3); // Middle of Q3 + expect(getQuarter('2024-09-30')).toBe(3); // End of Q3 + }); + + test('returns 4 for dates in the fourth quarter', () => { + expect(getQuarter('2024-10-01')).toBe(4); // Beginning of Q4 + expect(getQuarter('2024-11-15')).toBe(4); // Middle of Q4 + expect(getQuarter('2024-12-31')).toBe(4); // End of Q4 + }); + + test('handles string and Date inputs', () => { + expect(getQuarter('2024-04-15')).toBe(2); // String input + expect(getQuarter(new Date('2024-04-15'))).toBe(2); // Date object input + }); + + test('throws an error for invalid date inputs', () => { + expect(() => getQuarter('invalid-date')).toThrow( + 'Date format not recognized', + ); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/getRelativeTime.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/getRelativeTime.test.ts new file mode 100644 index 00000000..11963a8f --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/getRelativeTime.test.ts @@ -0,0 +1,45 @@ +import getRelativeTime from '../getRelativeTime'; + +describe('dateTime - getRelativeTime', () => { + const now = new Date(); + + test('returns correct relative time for seconds', () => { + const thirtySecondsAgo = new Date(now.getTime() - 30 * 1000); + expect(getRelativeTime(thirtySecondsAgo, now)).toBe('30 seconds ago'); + const inThirtySeconds = new Date(now.getTime() + 30 * 1000); + expect(getRelativeTime(inThirtySeconds, now)).toBe('in 30 seconds'); + }); + + test('returns correct relative time for minutes', () => { + const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000); + expect(getRelativeTime(fiveMinutesAgo, now)).toBe('5 minutes ago'); + const inFiveMinutes = new Date(now.getTime() + 5 * 60 * 1000); + expect(getRelativeTime(inFiveMinutes, now)).toBe('in 5 minutes'); + }); + + test('returns correct relative time for hours', () => { + const twoHoursAgo = new Date(now.getTime() - 2 * 60 * 60 * 1000); + expect(getRelativeTime(twoHoursAgo, now)).toBe('2 hours ago'); + const inTwoHours = new Date(now.getTime() + 2 * 60 * 60 * 1000); + expect(getRelativeTime(inTwoHours, now)).toBe('in 2 hours'); + }); + + test('returns correct relative time for days', () => { + const threeDaysAgo = new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000); + expect(getRelativeTime(threeDaysAgo, now)).toBe('3 days ago'); + const inThreeDays = new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000); + expect(getRelativeTime(inThreeDays, now)).toBe('in 3 days'); + }); + + test('handles different locales', () => { + const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000); + expect(getRelativeTime(oneDayAgo, now, {locale: 'en-US'})).toBe('1 day ago'); + expect(getRelativeTime(oneDayAgo, now, {locale: 'fr-FR'})).toBe('il y a 1 jour'); + }); + + test('throws an error for invalid date inputs', () => { + expect(() => getRelativeTime('invalid-date', now)).toThrow( + 'Date format not recognized', + ); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/getWeek.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/getWeek.test.ts new file mode 100644 index 00000000..930e116d --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/getWeek.test.ts @@ -0,0 +1,29 @@ +import getWeek from '../getWeek'; + +describe('dateTime - getWeek', () => { + test('returns correct week number at the beginning of the year', () => { + expect(getWeek('2024-01-01')).toBe(1); // First day of the year + expect(getWeek('2024-01-07')).toBe(2); // Seventh day of the year + }); + + test('returns correct week number at the end of the year', () => { + expect(getWeek('2024-12-31')).toBe(53); // Last day of a leap year + }); + + test('returns correct week number for a leap year', () => { + expect(getWeek('2024-02-29')).toBe(9); // Leap day + }); + + test('returns correct week number for a date in the middle of the year', () => { + expect(getWeek('2024-06-15')).toBe(24); // A date in mid-June + }); + + test('handles string and Date inputs', () => { + expect(getWeek('2024-04-15')).toBe(16); // String input + expect(getWeek(new Date('2024-04-15'))).toBe(16); // Date object input + }); + + test('throws an error for invalid date inputs', () => { + expect(() => getWeek('invalid-date')).toThrow('Date format not recognized'); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/getWeekdays.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/getWeekdays.test.ts new file mode 100644 index 00000000..c546ac16 --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/getWeekdays.test.ts @@ -0,0 +1,63 @@ +import getWeekdays from '../getWeekdays'; +import { DateTimeFormatOptions } from '../types'; + +describe('dateTime - getWeekdays', () => { + const testCases = [ + { + locale: 'en-US', + expected: [ + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + ], + options: {}, + }, + { + locale: 'de-DE', + expected: [ + 'Sonntag', + 'Montag', + 'Dienstag', + 'Mittwoch', + 'Donnerstag', + 'Freitag', + 'Samstag', + ], + options: {}, + }, + { + locale: 'fr-FR', + expected: [ + 'dimanche', + 'lundi', + 'mardi', + 'mercredi', + 'jeudi', + 'vendredi', + 'samedi', + ], + options: {}, + }, + { + locale: 'en-US', + expected: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + options: { weekday: 'short' }, + }, + ]; + + testCases.forEach(({ locale, expected, options }) => { + test(`returns correct weekdays for ${locale} locale`, () => { + const weekdays = getWeekdays({ + locale, + intlOptions :options as DateTimeFormatOptions, + } + ); + expect(weekdays).toHaveLength(7); + expect(weekdays).toEqual(expected); + }); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/isAfter.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/isAfter.test.ts new file mode 100644 index 00000000..a4dd26c5 --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/isAfter.test.ts @@ -0,0 +1,33 @@ +import isAfter from '../isAfter'; + +describe('dateTime - isAfter', () => { + test('returns true when the first date is after the second date', () => { + expect(isAfter('2024-01-02', '2024-01-01')).toBe(true); + expect(isAfter(new Date(2024, 0, 2), new Date(2024, 0, 1))).toBe(true); + }); + + test('returns false when the first date is before the second date', () => { + expect(isAfter('2024-01-01', '2024-01-02')).toBe(false); + expect(isAfter(new Date(2024, 0, 1), new Date(2024, 0, 2))).toBe(false); + }); + + test('returns false when the dates are the same', () => { + expect(isAfter('2024-01-01', '2024-01-01')).toBe(false); + expect(isAfter(new Date(2024, 0, 1), new Date(2024, 0, 1))).toBe(false); + }); + + test('handles different time units correctly', () => { + expect(isAfter('2024-01-01T01:00:00', '2024-01-01T00:59:59')).toBe(true); + expect(isAfter('2024-01-01T00:00:01', '2024-01-01T00:00:00')).toBe(true); + }); + + test('handles different date formats', () => { + expect(isAfter('01/02/2024', '01/01/2024')).toBe(true); // MM/DD/YYYY format + expect(isAfter('2024.01.02', '2024.01.01')).toBe(true); // YYYY.MM.DD format + }); + + test('throws an error for invalid date formats', () => { + expect(() => isAfter('invalid-date', '2024-01-01')).toThrow(Error); + expect(() => isAfter('2024-01-01', 'invalid-date')).toThrow(Error); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/isBefore.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/isBefore.test.ts new file mode 100644 index 00000000..68522cfd --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/isBefore.test.ts @@ -0,0 +1,33 @@ +import isBefore from '../isBefore'; + +describe('dateTime - isBefore', () => { + test('returns true when the first date is before the second date', () => { + expect(isBefore('2024-01-01', '2024-01-02')).toBe(true); + expect(isBefore(new Date(2024, 0, 1), new Date(2024, 0, 2))).toBe(true); + }); + + test('returns false when the first date is after the second date', () => { + expect(isBefore('2024-01-02', '2024-01-01')).toBe(false); + expect(isBefore(new Date(2024, 0, 2), new Date(2024, 0, 1))).toBe(false); + }); + + test('returns false when the dates are the same', () => { + expect(isBefore('2024-01-01', '2024-01-01')).toBe(false); + expect(isBefore(new Date(2024, 0, 1), new Date(2024, 0, 1))).toBe(false); + }); + + test('handles different time units correctly', () => { + expect(isBefore('2024-01-01T00:00:00', '2024-01-01T00:00:01')).toBe(true); + expect(isBefore('2024-01-01T23:59:59', '2024-01-02T00:00:00')).toBe(true); + }); + + test('handles different date formats', () => { + expect(isBefore('01/01/2024', '01/02/2024')).toBe(true); // MM/DD/YYYY format + expect(isBefore('2024.01.01', '2024.01.02')).toBe(true); // YYYY.MM.DD format + }); + + test('throws an error for invalid date formats', () => { + expect(() => isBefore('invalid-date', '2024-01-01')).toThrow(Error); + expect(() => isBefore('2024-01-01', 'invalid-date')).toThrow(Error); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/isLeapYear.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/isLeapYear.test.ts new file mode 100644 index 00000000..d8b4e50a --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/isLeapYear.test.ts @@ -0,0 +1,24 @@ +import isLeapYear from '../isLeapYear'; + +describe('dateTime - isLeapYear', () => { + test('returns true for leap years', () => { + expect(isLeapYear(2024)).toBe(true); // Divisible by 4 + expect(isLeapYear(2000)).toBe(true); // Divisible by 100 and 400 + }); + + test('returns false for non-leap years', () => { + expect(isLeapYear(2023)).toBe(false); // Not divisible by 4 + expect(isLeapYear(2100)).toBe(false); // Divisible by 100 but not by 400 + }); + + test('works correctly for century years', () => { + expect(isLeapYear(1900)).toBe(false); // Century year, divisible by 100 but not 400 + expect(isLeapYear(2000)).toBe(true); // Century year, divisible by 100 and 400 + }); + + test('handles cases like Year 0 is considered a leap year, Negative leap year, Negative non-leap century year', () => { + expect(isLeapYear(0)).toBe(true); // Year 0 is considered a leap year + expect(isLeapYear(-4)).toBe(true); // Negative leap year + expect(isLeapYear(-100)).toBe(false); // Negative non-leap century year + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/isSameDay.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/isSameDay.test.ts new file mode 100644 index 00000000..8a013510 --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/isSameDay.test.ts @@ -0,0 +1,30 @@ +import isSameDay from '../isSameDay'; + +describe('isSameDay function', () => { + test('returns true for the same dates', () => { + expect(isSameDay('2024-01-01', '2024-01-01')).toBe(true); + expect(isSameDay(new Date(2024, 0, 1), new Date(2024, 0, 1))).toBe(true); + }); + + test('returns true for dates with different times but the same day', () => { + expect(isSameDay('2024-01-01T08:30:00', '2024-01-01T17:45:00')).toBe(true); + expect( + isSameDay(new Date(2024, 0, 1, 8, 30), new Date(2024, 0, 1, 17, 45)), + ).toBe(true); + }); + + test('returns false for different dates', () => { + expect(isSameDay('2024-01-01', '2024-01-02')).toBe(false); + expect(isSameDay(new Date(2024, 0, 1), new Date(2024, 0, 2))).toBe(false); + }); + + test('handles cases around month and year transitions', () => { + expect(isSameDay('2024-01-01', '2023-12-31')).toBe(false); // Year transition + expect(isSameDay('2024-01-31', '2024-02-01')).toBe(false); // Month transition + }); + + test('throws an error for invalid date formats', () => { + expect(() => isSameDay('invalid-date', '2024-01-01')).toThrow(Error); + expect(() => isSameDay('2024-01-01', 'invalid-date')).toThrow(Error); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/isValidDate.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/isValidDate.test.ts new file mode 100644 index 00000000..86be071d --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/isValidDate.test.ts @@ -0,0 +1,34 @@ +import isValidDate from '../isValidDate'; + +describe('dateTime - isValidDate', () => { + test.each([ + // Valid dates for different locales + ['31/12/2020', 'GB', true], // Valid date in British format (DD/MM/YYYY) + ['2020-12-31', 'SE', true], // Valid date in Swedish format (YYYY-MM-DD) + ['12-31-2020', 'US', true], // Valid date in US format (MM-DD-YYYY) + + // Invalid dates + ['31/02/2020', 'GB', false], // Invalid date, February doesn't have 31 days + ['2020-13-01', 'SE', false], // Invalid month + ['02/29/2019', 'US', false], // 2019 is not a leap year + + // Leap year dates + ['29/02/2020', 'GB', true], // Leap year date in British format + ['2020-02-29', 'SE', true], // Leap year date in Swedish format + + // Invalid inputs + ['invalid-date', 'GB', false], // Non-date string + ['2020/02/31', 'SE', false], // Incorrectly formatted date + ['12-31-2020', 'GB', false], // Format mismatch for British + ])( + 'validates date "%s" for countryCode "%s"', + (dateString, countryCode, expected) => { + expect(isValidDate(dateString, {countryCode})).toBe(expected); + }, + ); + + test('handles non-string inputs gracefully', () => { + expect(isValidDate(null as unknown as string, {countryCode: 'GB'})).toBe(false); + expect(isValidDate(12345 as unknown as string, {countryCode: 'GB'})).toBe(false); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/parseDateTime.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/parseDateTime.test.ts new file mode 100644 index 00000000..caccdf81 --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/parseDateTime.test.ts @@ -0,0 +1,103 @@ +import parseDateTime from '../parseDateTime'; +import { DateTimeFormatOptions } from '../types'; + +describe('dateTime - parseDateTime', () => { + test('parses standard date input correctly', () => { + const date = '2024-01-01'; + const result = parseDateTime(date, {intlOptions: { + year: 'numeric', + month: 'long', + day: 'numeric', + }}); + expect(result.dateObj).toBeDefined(); + expect(result.formattedDate).toBe('January 1, 2024'); + expect(result.year).toBe('2024'); + expect(result.month).toBe('January'); + expect(result.day).toBe('1'); + }); + + test('formats date according to specified locale', () => { + const date = '2024-01-01'; + const locale = 'de-DE'; // German locale + const result = parseDateTime( + date, + { + intlOptions: { + year: 'numeric', + month: 'long', + day: 'numeric', + }, + locale, + } + ); + expect(result.formattedDate).toBe('1. Januar 2024'); // Format in German + }); + + test('throws error for invalid date input', () => { + const date = 'invalid-date'; + expect(() => parseDateTime(date)).toThrow(Error); + }); + + test('handles leap year correctly', () => { + const date = '2024-02-29'; + const result = parseDateTime(date); + if (result.dateObj) { + expect(result.dateObj.getFullYear()).toBe(2024); + expect(result.dateObj.getMonth()).toBe(1); // Month is 0-indexed + expect(result.dateObj.getDate()).toBe(29); + } + }); + + test('parses time components correctly', () => { + const dateTime = '2024-01-01 23:59:59'; + const result = parseDateTime(dateTime, {intlOptions: { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: false, + }}); + expect(result.hour).toBe('23'); + expect(result.minute).toBe('59'); + expect(result.second).toBe('59'); + }); + + test('verifies each date component', () => { + const date = '2024-01-01'; + const result = parseDateTime(date, {intlOptions: { + year: 'numeric', + month: 'long', + day: 'numeric', + }}); + expect(result.year).toBe('2024'); + expect(result.month).toBe('January'); + expect(result.day).toBe('1'); + }); + + test('respects intlOptions settings', () => { + const date = '2024-01-01'; + const intlOptions = { year: '2-digit', month: '2-digit', day: '2-digit' }; + const result = parseDateTime(date, {intlOptions: intlOptions as DateTimeFormatOptions}); + expect(result.formattedDate).toBe('01/01/24'); + }); + + test('falls back to system locale if none provided', () => { + const date = '2024-01-01'; + const result = parseDateTime(date); + // This test's outcome may vary based on the system's locale + expect(result.month).toBeDefined(); + }); + + test('handles different date formats', () => { + const dates = ['2024-01-01', '01/01/2024', '01.01.2024']; + dates.forEach((date) => { + const result = parseDateTime(date, {intlOptions: { + year: 'numeric', + month: 'long', + day: 'numeric', + }}); + expect(result.year).toBe('2024'); + expect(result.month).toBe('January'); + expect(result.day).toBe('1'); + }); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/subtract.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/subtract.test.ts new file mode 100644 index 00000000..51f24f2c --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/subtract.test.ts @@ -0,0 +1,53 @@ +import subtract from '../subtract'; + +describe('dateTime - subtract', () => { + // Test subtracting days + test('subtracts days correctly', () => { + const date = new Date(2024, 0, 31); // January 31, 2024 + const result = subtract(date, {value: 10, unit: 'days'}); + expect(result).toEqual(new Date(2024, 0, 21)); // January 21, 2024 + }); + + // Test subtracting months + test('subtracts months correctly', () => { + const date = new Date(2024, 5, 15); // June 15, 2024 + const result = subtract(date, {value: 2, unit: 'months'}); + expect(result).toEqual(new Date(2024, 3, 15)); // April 15, 2024 + }); + + // Test subtracting years + test('subtracts years correctly', () => { + const date = new Date(2024, 0, 1); // January 1, 2024 + const result = subtract(date, {value: 2, unit: 'years'}); + expect(result).toEqual(new Date(2022, 0, 1)); // January 1, 2022 + }); + + // Test leap year + test('handles leap year correctly when subtracting years', () => { + const date = new Date(2024, 1, 29); // February 29, 2024 + const result = subtract(date, {value: 1, unit: 'years'}); + expect(result).toEqual(new Date(2023, 1, 28)); // February 28, 2023 + }); + + // Test month end + test('handles month-end correctly when subtracting months', () => { + const date = new Date(2024, 2, 31); // March 31, 2024 + const result = subtract(date, {value: 1, unit: 'months'}); + expect(result).toEqual(new Date(2024, 1, 29)); // February 29, 2024 + }); + + // Test invalid value + test('throws error for invalid value', () => { + const date = new Date(2024, 0, 1); + expect(() => subtract(date, {value: Infinity, unit: 'days'})).toThrow( + 'Invalid value passed!', + ); + }); + + // Test invalid date + test('throws error for invalid date', () => { + expect(() => subtract('invalid-date', {value: 1, unit: 'days'})).toThrow( + 'Date format not recognized', + ); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/__tests__/utils.test.ts b/packages/i18nify-js/src/modules/dateTime/__tests__/utils.test.ts new file mode 100644 index 00000000..018b4d4a --- /dev/null +++ b/packages/i18nify-js/src/modules/dateTime/__tests__/utils.test.ts @@ -0,0 +1,59 @@ +import { stringToDate } from '../utils'; + +describe('dateTime - utils - stringToDate', () => { + // Test valid date formats + const validDates = [ + { input: '2024/02/29', expected: new Date(2024, 1, 29) }, + { input: '29/02/2024', expected: new Date(2024, 1, 29) }, + { input: '2024.02.29', expected: new Date(2024, 1, 29) }, + { input: '29-02-2024', expected: new Date(2024, 1, 29) }, + { input: '02/29/2024', expected: new Date(2024, 1, 29) }, + { input: '2024-02-29', expected: new Date(2024, 1, 29) }, + { input: '2024. 02. 29.', expected: new Date(2024, 1, 29) }, + { input: '02.29.2024', expected: new Date(2024, 1, 29) }, + { input: '2024-02-29T12:00:00', expected: new Date(2024, 1, 29, 12, 0, 0) }, + { input: '13/01/2024', expected: new Date('01/13/2024') }, + ]; + + validDates.forEach(({ input, expected }) => { + test(`correctly converts '${input}' to a Date object`, () => { + expect(stringToDate(input)).toEqual(expected); + }); + }); + + // Test invalid date formats + const invalidDateFormats = [ + '2024/13/01', + '2024.13.01', + '01-32-2024', + '2024-13-01', + '2024. 13. 01.', + ]; + + invalidDateFormats.forEach((input) => { + test(`throws an error for invalid date format string '${input}'`, () => { + expect(() => stringToDate(input)).toThrow('Date format not recognized'); + }); + }); + + // Test invalid dates + const invalidDates = ['32/01/2024', '2024-02-29T25:00:00']; + + invalidDates.forEach((input) => { + test(`throws an error for invalid date string '${input}'`, () => { + expect(() => stringToDate(input)).toThrow('Invalid Date!'); + }); + }); + + // Test valid leap year date + test('correctly identifies leap year dates', () => { + expect(stringToDate('2024/02/29')).toEqual(new Date(2024, 1, 29)); + }); + + // Test for timestamps + test('correctly converts timestamps', () => { + expect(stringToDate('2024-02-29T23:59:59')).toEqual( + new Date(2024, 1, 29, 23, 59, 59), + ); + }); +}); diff --git a/packages/i18nify-js/src/modules/dateTime/formatDate.ts b/packages/i18nify-js/src/modules/dateTime/formatDate.ts index ee8e7cf1..0171c56f 100644 --- a/packages/i18nify-js/src/modules/dateTime/formatDate.ts +++ b/packages/i18nify-js/src/modules/dateTime/formatDate.ts @@ -37,7 +37,7 @@ const formatDate = ( let formattedDate; try { - formattedDate = formatDateTime(date, options.locale, fullOptions).split(',')[0]; + formattedDate = formatDateTime(date, {locale: options.locale, intlOptions: fullOptions}).split(',')[0]; } catch (err) { if (err instanceof Error) { throw new Error(err.message); diff --git a/packages/i18nify-js/src/modules/dateTime/isValidDate.ts b/packages/i18nify-js/src/modules/dateTime/isValidDate.ts index 6cc32bb1..ab059722 100644 --- a/packages/i18nify-js/src/modules/dateTime/isValidDate.ts +++ b/packages/i18nify-js/src/modules/dateTime/isValidDate.ts @@ -6,11 +6,11 @@ import { LOCALE_DATE_FORMATS } from './data/localeDateFormats'; * Checks if a given string is a valid date according to a specific locale's date format. * * @param dateString The date string to validate. - * @param countryCode The countryCode to use for the date format. + * @param options Config object * @returns True if the dateString is a valid date according to the locale's format, false otherwise. */ -const isValidDate = (dateString: string, countryCode: string): boolean => { - const locale = COUNTRY_CODES_TO_LOCALE[countryCode]; +const isValidDate = (dateString: string, options: {countryCode: string}): boolean => { + const locale = COUNTRY_CODES_TO_LOCALE[options.countryCode]; // Type guard to ensure dateString is a string if (typeof dateString !== 'string') { diff --git a/packages/i18nify-js/src/modules/dateTime/parseDateTime.ts b/packages/i18nify-js/src/modules/dateTime/parseDateTime.ts index 0c0c9447..5c5399d4 100644 --- a/packages/i18nify-js/src/modules/dateTime/parseDateTime.ts +++ b/packages/i18nify-js/src/modules/dateTime/parseDateTime.ts @@ -23,7 +23,7 @@ const parseDateTime = ( options: { locale?: Locale, intlOptions?: Intl.DateTimeFormatOptions, - }, + } = {}, ): ParsedDateTime => { // Parse the input date, converting strings to Date objects if necessary const date =