From 52d72468eea82c645723d64605749f1a80bc5616 Mon Sep 17 00:00:00 2001 From: Vicente Vendramin Date: Thu, 26 Dec 2024 23:42:27 -0300 Subject: [PATCH] feat: add function that returns holidays of the year --- src/index.test.ts | 1 + src/utilities/holidays/index.test.ts | 61 +++++++++++++++++++ src/utilities/holidays/index.ts | 88 ++++++++++++++++++++++++++++ src/utilities/index.ts | 1 + 4 files changed, 151 insertions(+) create mode 100644 src/utilities/holidays/index.test.ts create mode 100644 src/utilities/holidays/index.ts diff --git a/src/index.test.ts b/src/index.test.ts index f8d4baa0..95805572 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -30,6 +30,7 @@ describe('Public API', () => { 'capitalize', 'formatCurrency', 'parseCurrency', + 'getAllNationalHolidays', ]; Object.keys(API).forEach((method) => { diff --git a/src/utilities/holidays/index.test.ts b/src/utilities/holidays/index.test.ts new file mode 100644 index 00000000..2513256f --- /dev/null +++ b/src/utilities/holidays/index.test.ts @@ -0,0 +1,61 @@ +import { getAllNationalHolidays } from '.'; + +describe('getAllNationalHolidays', () => { + test('should return fixed holidays for the given year', () => { + const year = 2024; + const holidays = getAllNationalHolidays(year); + + const fixedHolidays = [ + { name: 'Ano novo', date: new Date(year, 0, 1) }, + { name: 'Tiradentes', date: new Date(year, 3, 21) }, + { name: 'Dia do trabalhador', date: new Date(year, 4, 1) }, + { name: 'Independência do Brasil', date: new Date(year, 8, 7) }, + { name: 'Nossa Senhora Aparecida', date: new Date(year, 9, 12) }, + { name: 'Finados', date: new Date(year, 10, 2) }, + { name: 'Proclamação da República', date: new Date(year, 10, 15) }, + { name: 'Dia da Consciência Negra', date: new Date(year, 10, 20) }, + { name: 'Natal', date: new Date(year, 11, 25) }, + ]; + + fixedHolidays.forEach(({ name, date }) => { + expect(holidays).toContainEqual({ name, date }); + }); + }); + + test('should calculate Easter-related holidays correctly', () => { + const year = 2024; + const holidays = getAllNationalHolidays(year); + + const easterDate = new Date(2024, 2, 31); // Easter Sunday 2024 + const expectedHolidays = [ + { name: 'Páscoa', date: easterDate }, + { name: 'Carnaval (terça-feira)', date: new Date(2024, 1, 13) }, + { name: 'Sexta-feira Santa', date: new Date(2024, 2, 29) }, + { name: 'Corpus Christi', date: new Date(2024, 5, 9) }, + ]; + + expectedHolidays.forEach(({ name, date }) => { + expect(holidays).toContainEqual({ name, date }); + }); + }); + + test('should return the correct number of holidays', () => { + const year = 2024; + const holidays = getAllNationalHolidays(year); + + expect(holidays.length).toBe(13); // 9 fixed + 4 Easter-related holidays + }); + + test('should work for leap years', () => { + const year = 2020; + const holidays = getAllNationalHolidays(year); + + expect(holidays).toContainEqual({ + name: 'Ano novo', + date: new Date(year, 0, 1), + }); + + const easterDate = new Date(2020, 3, 12); // Easter Sunday 2020 + expect(holidays).toContainEqual({ name: 'Páscoa', date: easterDate }); + }); +}); diff --git a/src/utilities/holidays/index.ts b/src/utilities/holidays/index.ts new file mode 100644 index 00000000..4e34e079 --- /dev/null +++ b/src/utilities/holidays/index.ts @@ -0,0 +1,88 @@ +const FIXED_HOLIDAYS = { + 'Ano novo': { day: 1, month: 1 }, + Tiradentes: { day: 21, month: 4 }, + 'Dia do trabalhador': { day: 1, month: 5 }, + 'Independência do Brasil': { day: 7, month: 9 }, + 'Nossa Senhora Aparecida': { day: 12, month: 10 }, + Finados: { day: 2, month: 11 }, + 'Proclamação da República': { day: 15, month: 11 }, + 'Dia da Consciência Negra': { day: 20, month: 11 }, + Natal: { day: 25, month: 12 }, +}; + +function calculateEaster(year: number): Date { + // Calculation based on the computus algorithm + const a = year % 19; + const b = Math.floor(year / 100); + const c = year % 100; + const d = Math.floor(b / 4); + const e = b % 4; + const f = Math.floor((b + 8) / 25); + const g = Math.floor((b - f + 1) / 3); + const h = (19 * a + b - d - g + 15) % 30; + const i = Math.floor(c / 4); + const k = c % 4; + const l = (32 + 2 * e + 2 * i - h - k) % 7; + const m = Math.floor((a + 11 * h + 22 * l) / 451); + + const month = Math.floor((h + l - 7 * m + 114) / 31) - 1; // 0-based month + const day = ((h + l - 7 * m + 114) % 31) + 1; + + return new Date(year, month, day); +} + +function calculateHolidayFromEaster(year: number, offset: number): Date { + // Calculates the date of Easter + const easterDate = calculateEaster(year); + + // Creates a new Date object for the holiday + const holidayDate = new Date(easterDate); + + // Adds or subtracts the offset to get the holiday date + holidayDate.setDate(easterDate.getDate() + offset); + + return holidayDate; +} + +export function getAllNationalHolidays(year: number): Object[] { + const allHolidays: Object[] = []; + + // Add fixed holidays + for (const [key, { day, month }] of Object.entries(FIXED_HOLIDAYS)) { + const holiday = { + name: key, + date: new Date(year, month - 1, day), + }; + allHolidays.push(holiday); + } + + // Calculate and add Easter + const easterHoliday = { + name: 'Páscoa', + date: calculateEaster(year), + }; + allHolidays.push(easterHoliday); + + // Calculate Gras + const mardiGrasHoliday = { + name: 'Carnaval (terça-feira)', + date: calculateHolidayFromEaster(year, -47), + }; + allHolidays.push(mardiGrasHoliday); + + // Calculate Good Friday + const goodFridayHoliday = { + name: 'Sexta-feira Santa', + date: calculateHolidayFromEaster(year, -2), + }; + allHolidays.push(goodFridayHoliday); + + // Calculate and add Corpus Christi + const corpusChristiHoliday = { + name: 'Corpus Christi', + date: calculateHolidayFromEaster(year, 70), + }; + allHolidays.push(corpusChristiHoliday); + + return allHolidays; +} diff --git a/src/utilities/index.ts b/src/utilities/index.ts index 48fdf9e9..39737314 100644 --- a/src/utilities/index.ts +++ b/src/utilities/index.ts @@ -11,3 +11,4 @@ export { format as formatCNPJ, generate as generateCNPJ, isValid as isValidCNPJ export { capitalize } from './capitalize'; export { getStates } from './states'; export { getCities } from './cities'; +export { getAllNationalHolidays } from './holidays';