├── .nvmrc ├── examples ├── currentDate.ts ├── nextYear.ts └── monthHolidays.ts ├── src ├── utils │ ├── helpers.ts │ ├── isHoliday.ts │ ├── isHoliday.test.ts │ ├── getHoliday.ts │ ├── getHoliday.test.ts │ ├── getHolidaysByYear.ts │ ├── holidaysWithinInterval.ts │ └── holidaysWithinInterval.test.ts ├── types.ts ├── index.ts ├── helpers.ts ├── holidays.ts └── index.test.ts ├── tsconfig.json ├── biome.json ├── LICENSE ├── package.json ├── .gitignore └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /examples/currentDate.ts: -------------------------------------------------------------------------------- 1 | import colombianHolidays from "../src/index"; 2 | 3 | console.log(colombianHolidays({ year: new Date().getFullYear() })); 4 | -------------------------------------------------------------------------------- /examples/nextYear.ts: -------------------------------------------------------------------------------- 1 | import colombianHolidays from "../src/index"; 2 | 3 | console.log(colombianHolidays({ year: new Date().getFullYear() + 1 })); 4 | -------------------------------------------------------------------------------- /src/utils/helpers.ts: -------------------------------------------------------------------------------- 1 | export function isSameDate(date1: Date, date2: Date): boolean { 2 | return ( 3 | date1.getUTCDate() === date2.getUTCDate() && 4 | date1.getUTCMonth() === date2.getUTCMonth() && 5 | date1.getUTCFullYear() === date2.getUTCFullYear() 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /examples/monthHolidays.ts: -------------------------------------------------------------------------------- 1 | import colombianHolidays from "../src/index"; 2 | 3 | const year = new Date().getFullYear(); 4 | const holidays = colombianHolidays({ 5 | year, 6 | month: 1 /* January */, 7 | }); 8 | 9 | console.log(`Getting holidays for January of ${year}`, holidays); 10 | -------------------------------------------------------------------------------- /src/utils/isHoliday.ts: -------------------------------------------------------------------------------- 1 | import { getHolidaysForYear } from "./getHolidaysByYear"; 2 | import { isSameDate } from "./helpers"; 3 | 4 | export function isHoliday(date: Date): boolean { 5 | const holidays = getHolidaysForYear(date.getUTCFullYear(), { 6 | valueAsDate: true, 7 | }); 8 | return holidays.some(({ celebrationDate }) => 9 | isSameDate(celebrationDate, date), 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "module": "CommonJS", 5 | "declaration": true, 6 | "sourceMap": true, 7 | "outDir": "./dist", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true 12 | }, 13 | "include": ["src/**/*.ts"], 14 | "exclude": ["src/**/*.test.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/isHoliday.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { isHoliday } from "./isHoliday"; 3 | 4 | describe("test isHoliday", () => { 5 | it("should return true for a holiday date object", () => { 6 | expect(isHoliday(new Date("2018-01-01"))).toBe(true); 7 | }); 8 | 9 | it("should return false for a non holiday date object", () => { 10 | expect(isHoliday(new Date("2018-01-02"))).toBe(false); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export interface BasicHoliday { 2 | name: { 3 | en: string; 4 | es: string; 5 | }; 6 | nextMonday: boolean; 7 | } 8 | 9 | export interface DateHoliday extends BasicHoliday { 10 | month: number; 11 | day: number; 12 | } 13 | 14 | export interface EasterHoliday extends BasicHoliday { 15 | offset: number; 16 | } 17 | 18 | export interface ColombianHoliday extends BasicHoliday { 19 | date: string; 20 | celebrationDate: string; 21 | } 22 | 23 | export interface ColombianHolidayWithNativeDate extends BasicHoliday { 24 | date: Date; 25 | celebrationDate: Date; 26 | } 27 | 28 | export type Holiday = DateHoliday | EasterHoliday; 29 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/2.1.3/schema.json", 3 | "vcs": { 4 | "enabled": false, 5 | "clientKind": "git", 6 | "useIgnoreFile": false 7 | }, 8 | "files": { 9 | "includes": ["src/**/*.ts", "examples/**/*.ts"], 10 | "ignoreUnknown": false 11 | }, 12 | "formatter": { 13 | "enabled": true, 14 | "indentStyle": "tab" 15 | }, 16 | "linter": { 17 | "enabled": true, 18 | "rules": { 19 | "recommended": true 20 | } 21 | }, 22 | "javascript": { 23 | "formatter": { 24 | "quoteStyle": "double" 25 | } 26 | }, 27 | "assist": { 28 | "enabled": true, 29 | "actions": { 30 | "source": { 31 | "organizeImports": "on" 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mauricio Robayo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/utils/getHoliday.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ColombianHoliday, 3 | ColombianHolidayWithNativeDate, 4 | } from "../types"; 5 | import { getHolidaysForYear } from "./getHolidaysByYear"; 6 | import { isSameDate } from "./helpers"; 7 | 8 | export function getHoliday( 9 | date: Date, 10 | options: { valueAsDate: true }, 11 | ): ColombianHolidayWithNativeDate | null; 12 | export function getHoliday( 13 | date: Date, 14 | options?: undefined | { valueAsDate: false }, 15 | ): ColombianHoliday | null; 16 | export function getHoliday( 17 | date: Date, 18 | options: { valueAsDate: boolean } = { valueAsDate: false }, 19 | ): ColombianHoliday | ColombianHolidayWithNativeDate | null { 20 | const { valueAsDate } = options; 21 | const holiday = getHolidaysForYear(date.getUTCFullYear(), { 22 | valueAsDate: true, 23 | }).find(({ celebrationDate }) => isSameDate(celebrationDate, date)); 24 | if (!holiday) { 25 | return null; 26 | } 27 | 28 | if (valueAsDate) { 29 | return holiday; 30 | } 31 | return { 32 | ...holiday, 33 | date: holiday.date.toISOString().slice(0, 10), 34 | celebrationDate: holiday.celebrationDate.toISOString().slice(0, 10), 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /src/utils/getHoliday.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { getHoliday } from "./getHoliday"; 3 | 4 | describe("test getHoliday", () => { 5 | it("should return holiday for a holiday date object", () => { 6 | const expectedHoliday = { 7 | celebrationDate: "2018-01-01", 8 | date: "2018-01-01", 9 | name: { en: "New Year's Day", es: "Año Nuevo" }, 10 | nextMonday: false, 11 | }; 12 | const holiday = getHoliday(new Date("2018-01-01")); 13 | expect(holiday).toEqual(expectedHoliday); 14 | }); 15 | 16 | it("should return holiday with values as dates for a holiday date object", () => { 17 | const expectedHoliday = { 18 | celebrationDate: new Date("2018-01-01T00:00:00.000Z"), 19 | date: new Date("2018-01-01T00:00:00.000Z"), 20 | name: { en: "New Year's Day", es: "Año Nuevo" }, 21 | nextMonday: false, 22 | }; 23 | const holiday = getHoliday(new Date("2018-01-01"), { 24 | valueAsDate: true, 25 | }); 26 | expect(holiday).toEqual(expectedHoliday); 27 | }); 28 | 29 | it("should return false for a non holiday date object", () => { 30 | expect(getHoliday(new Date("2018-01-02T05:00:00.000Z"))).toBe(null); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "colombian-holidays", 3 | "version": "5.0.9", 4 | "description": "Colombian holidays", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "format": "biome format --write", 9 | "lint": "biome lint --write", 10 | "test": "vitest run", 11 | "build": "tsup src/index.ts --dts --format cjs,esm --out-dir dist", 12 | "prepublishOnly": "biome check && npm test && npm run build" 13 | }, 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/MauricioRobayo/colombian-holidays/issues" 17 | }, 18 | "homepage": "https://github.com/MauricioRobayo/colombian-holidays", 19 | "author": "Mauricio Robayo (https://www.mauriciorobayo.com)", 20 | "keywords": [ 21 | "Colombian holidays", 22 | "Festivos en Colombia", 23 | "Colombia", 24 | "Festivos", 25 | "Holidays" 26 | ], 27 | "devDependencies": { 28 | "@biomejs/biome": "2.1.3", 29 | "tsup": "^8.5.0", 30 | "typescript": "^5.9.2", 31 | "vitest": "^3.2.4" 32 | }, 33 | "dependencies": { 34 | "pascua": "^3.0.8" 35 | }, 36 | "files": [ 37 | "dist" 38 | ], 39 | "repository": { 40 | "type": "git", 41 | "url": "https://github.com/MauricioRobayo/colombian-holidays.git" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/utils/getHolidaysByYear.ts: -------------------------------------------------------------------------------- 1 | import colombianHolidays from ".."; 2 | import type { 3 | ColombianHoliday, 4 | ColombianHolidayWithNativeDate, 5 | } from "../types"; 6 | 7 | const holidaysWithNativeDateCache = new Map< 8 | number, 9 | ColombianHolidayWithNativeDate[] 10 | >(); 11 | const holidaysCache = new Map(); 12 | 13 | export function getHolidaysForYear( 14 | year: number, 15 | options?: { valueAsDate: false | undefined }, 16 | ): ColombianHoliday[]; 17 | export function getHolidaysForYear( 18 | year: number, 19 | options?: { valueAsDate: true }, 20 | ): ColombianHolidayWithNativeDate[]; 21 | export function getHolidaysForYear( 22 | year: number, 23 | { valueAsDate = false }: { valueAsDate?: boolean } = {}, 24 | ) { 25 | if (valueAsDate) { 26 | const cachedHolidays = holidaysWithNativeDateCache.get(year); 27 | if (cachedHolidays) { 28 | return cachedHolidays; 29 | } 30 | 31 | const holidays = colombianHolidays({ year, valueAsDate }); 32 | holidaysWithNativeDateCache.set(year, holidays); 33 | return holidays; 34 | } 35 | 36 | const cachedHolidays = holidaysCache.get(year); 37 | if (cachedHolidays) { 38 | return cachedHolidays; 39 | } 40 | 41 | const holidays = colombianHolidays({ year, valueAsDate }); 42 | holidaysCache.set(year, holidays); 43 | return holidays; 44 | } 45 | -------------------------------------------------------------------------------- /src/utils/holidaysWithinInterval.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ColombianHoliday, 3 | ColombianHolidayWithNativeDate, 4 | } from "../types"; 5 | import { getHolidaysForYear } from "./getHolidaysByYear"; 6 | 7 | export function holidaysWithinInterval(options: { 8 | start: Date; 9 | end: Date; 10 | valueAsDate: false | undefined; 11 | }): ColombianHoliday[]; 12 | export function holidaysWithinInterval(options: { 13 | start: Date; 14 | end: Date; 15 | valueAsDate: true; 16 | }): ColombianHolidayWithNativeDate[]; 17 | export function holidaysWithinInterval(options: { 18 | start: Date; 19 | end: Date; 20 | valueAsDate?: boolean; 21 | }): ColombianHoliday[] | ColombianHolidayWithNativeDate[]; 22 | export function holidaysWithinInterval({ 23 | start, 24 | end, 25 | valueAsDate = false, 26 | }: { 27 | start: Date; 28 | end: Date; 29 | valueAsDate?: boolean; 30 | }): unknown { 31 | if (start >= end) { 32 | throw new Error("end date should be greater than start date"); 33 | } 34 | const yearEnd = end.getUTCFullYear(); 35 | const yearStart = start.getUTCFullYear(); 36 | 37 | const holidays = Array.from({ length: yearEnd - yearStart + 1 }, (_, i) => 38 | getHolidaysForYear(i + yearStart, { valueAsDate: true }), 39 | ).flat(); 40 | 41 | const holidaysWithin = holidays.filter( 42 | ({ celebrationDate }) => celebrationDate >= start && celebrationDate <= end, 43 | ); 44 | 45 | if (valueAsDate) { 46 | return holidaysWithin; 47 | } 48 | 49 | return holidaysWithin.map((holiday) => ({ 50 | ...holiday, 51 | date: holiday.date.toISOString().slice(0, 10), 52 | celebrationDate: holiday.celebrationDate.toISOString().slice(0, 10), 53 | })); 54 | } 55 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import getHoliday from "./helpers"; 2 | import holidays from "./holidays"; 3 | import type { ColombianHoliday, ColombianHolidayWithNativeDate } from "./types"; 4 | 5 | // pascua package year limits 6 | export const FIRST_HOLIDAY_YEAR = 1583; 7 | export const LAST_HOLIDAY_YEAR = 4099; 8 | 9 | type MonthNumbers = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; 10 | export type Month = MonthNumbers | Omit; 11 | 12 | export function colombianHolidays( 13 | options?: 14 | | undefined 15 | | { year?: number; month?: Month; valueAsDate: false | undefined }, 16 | ): ColombianHoliday[]; 17 | export function colombianHolidays(options?: { 18 | year?: number; 19 | month?: Month; 20 | valueAsDate: true; 21 | }): ColombianHolidayWithNativeDate[]; 22 | export function colombianHolidays(options?: { 23 | year?: number; 24 | month?: Month; 25 | valueAsDate?: boolean; 26 | }): ColombianHoliday[] | ColombianHolidayWithNativeDate[]; 27 | export function colombianHolidays({ 28 | year = new Date().getUTCFullYear(), 29 | month, 30 | valueAsDate = false, 31 | }: { 32 | year?: number; 33 | month?: Month; 34 | valueAsDate?: boolean; 35 | } = {}): unknown { 36 | if (year < FIRST_HOLIDAY_YEAR || year > LAST_HOLIDAY_YEAR) { 37 | throw new Error( 38 | `The year should be between ${FIRST_HOLIDAY_YEAR} and ${LAST_HOLIDAY_YEAR}`, 39 | ); 40 | } 41 | 42 | return holidays 43 | .map((holiday) => getHoliday(holiday, { year, valueAsDate })) 44 | .filter((holiday) => { 45 | if (month === undefined) { 46 | return true; 47 | } 48 | 49 | if (typeof holiday.celebrationDate === "string") { 50 | return Number(holiday.celebrationDate.slice(5, 7)) === month; 51 | } 52 | 53 | return holiday.celebrationDate.getUTCMonth() + 1 === month; 54 | }) 55 | .sort((a, b) => { 56 | if ( 57 | a.celebrationDate instanceof Date && 58 | b.celebrationDate instanceof Date 59 | ) { 60 | return a.celebrationDate.getTime() - b.celebrationDate.getTime(); 61 | } 62 | 63 | if ( 64 | typeof a.celebrationDate === "string" && 65 | typeof b.celebrationDate === "string" 66 | ) { 67 | return a.celebrationDate.localeCompare(b.celebrationDate); 68 | } 69 | 70 | throw new Error("Invariant violation: this state is not possible."); 71 | }); 72 | } 73 | 74 | export default colombianHolidays; 75 | 76 | export { getHolidaysForYear } from "./utils/getHolidaysByYear"; 77 | export { holidaysWithinInterval } from "./utils/holidaysWithinInterval"; 78 | export { isHoliday } from "./utils/isHoliday"; 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | .cache 3 | 4 | .DS_Store 5 | 6 | # Created by https://www.toptal.com/developers/gitignore/api/node 7 | # Edit at https://www.toptal.com/developers/gitignore?templates=node 8 | 9 | ### Node ### 10 | # Logs 11 | logs 12 | *.log 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | lerna-debug.log* 17 | 18 | # Diagnostic reports (https://nodejs.org/api/report.html) 19 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 20 | 21 | # Runtime data 22 | pids 23 | *.pid 24 | *.seed 25 | *.pid.lock 26 | 27 | # Directory for instrumented libs generated by jscoverage/JSCover 28 | lib-cov 29 | 30 | # Coverage directory used by tools like istanbul 31 | coverage 32 | *.lcov 33 | 34 | # nyc test coverage 35 | .nyc_output 36 | 37 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 38 | .grunt 39 | 40 | # Bower dependency directory (https://bower.io/) 41 | bower_components 42 | 43 | # node-waf configuration 44 | .lock-wscript 45 | 46 | # Compiled binary addons (https://nodejs.org/api/addons.html) 47 | build/Release 48 | 49 | # Dependency directories 50 | node_modules/ 51 | jspm_packages/ 52 | 53 | # TypeScript v1 declaration files 54 | typings/ 55 | 56 | # TypeScript cache 57 | *.tsbuildinfo 58 | 59 | # Optional npm cache directory 60 | .npm 61 | 62 | # Optional eslint cache 63 | .eslintcache 64 | 65 | # Microbundle cache 66 | .rpt2_cache/ 67 | .rts2_cache_cjs/ 68 | .rts2_cache_es/ 69 | .rts2_cache_umd/ 70 | 71 | # Optional REPL history 72 | .node_repl_history 73 | 74 | # Output of 'npm pack' 75 | *.tgz 76 | 77 | # Yarn Integrity file 78 | .yarn-integrity 79 | 80 | # dotenv environment variables file 81 | .env 82 | .env.test 83 | 84 | # parcel-bundler cache (https://parceljs.org/) 85 | .cache 86 | 87 | # Next.js build output 88 | .next 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # Serverless directories 104 | .serverless/ 105 | 106 | # FuseBox cache 107 | .fusebox/ 108 | 109 | # DynamoDB Local files 110 | .dynamodb/ 111 | 112 | # TernJS port file 113 | .tern-port 114 | 115 | # Stores VSCode versions used for testing VSCode extensions 116 | .vscode-test 117 | 118 | # End of https://www.toptal.com/developers/gitignore/api/node 119 | -------------------------------------------------------------------------------- /src/helpers.ts: -------------------------------------------------------------------------------- 1 | import pascua from "pascua"; 2 | import type { 3 | ColombianHoliday, 4 | ColombianHolidayWithNativeDate, 5 | EasterHoliday, 6 | Holiday, 7 | } from "./types"; 8 | 9 | // 1984 is the year when the current holidays scheme is enforced 10 | // http://www.alcaldiabogota.gov.co/sisjur/normas/Norma1.jsp?i=4954 11 | export const NEW_HOLIDAY_SCHEMA_START_YEAR = 1984; 12 | 13 | function getNextDayOfWeek(date: Date, dayOfWeek: number): Date { 14 | const resultDate = new Date(date); 15 | resultDate.setUTCDate( 16 | date.getUTCDate() + ((7 + dayOfWeek - date.getUTCDay()) % 7), 17 | ); 18 | return resultDate; 19 | } 20 | 21 | function getNextMonday(date: Date): Date { 22 | const MONDAY = 1; 23 | return getNextDayOfWeek(date, MONDAY); 24 | } 25 | 26 | function isEasterHoliday(holiday: Holiday): holiday is EasterHoliday { 27 | return "offset" in holiday; 28 | } 29 | 30 | function getHolidayDate(holiday: Holiday, year: number): Date { 31 | if (isEasterHoliday(holiday)) { 32 | const { month, day } = pascua(year); 33 | const date = new Date(generateUtcStringFromDateParts(year, month, 1)); 34 | date.setUTCDate(day + holiday.offset); 35 | return date; 36 | } 37 | 38 | return new Date( 39 | generateUtcStringFromDateParts(year, holiday.month, holiday.day), 40 | ); 41 | } 42 | 43 | function generateUtcStringFromDateParts( 44 | year: number, 45 | month: number, 46 | day: number, 47 | ) { 48 | return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart( 49 | 2, 50 | "0", 51 | )}`; 52 | } 53 | 54 | function getHoliday( 55 | holiday: Holiday, 56 | options?: { year?: number; valueAsDate: false | undefined }, 57 | ): ColombianHoliday; 58 | function getHoliday( 59 | holiday: Holiday, 60 | options: { year?: number; valueAsDate: true }, 61 | ): ColombianHolidayWithNativeDate; 62 | function getHoliday( 63 | holiday: Holiday, 64 | options?: { year?: number; valueAsDate?: boolean }, 65 | ): ColombianHoliday | ColombianHolidayWithNativeDate; 66 | function getHoliday( 67 | holiday: Holiday, 68 | { 69 | year = new Date().getUTCFullYear(), 70 | valueAsDate = false, 71 | }: { year?: number; valueAsDate?: boolean } = {}, 72 | ): unknown { 73 | const holidayDate = getHolidayDate(holiday, year); 74 | const celebrationDate = 75 | year >= NEW_HOLIDAY_SCHEMA_START_YEAR && holiday.nextMonday 76 | ? getNextMonday(holidayDate) 77 | : holidayDate; 78 | 79 | return { 80 | date: valueAsDate ? holidayDate : holidayDate.toISOString().slice(0, 10), 81 | celebrationDate: valueAsDate 82 | ? celebrationDate 83 | : celebrationDate.toISOString().slice(0, 10), 84 | name: holiday.name, 85 | nextMonday: year >= NEW_HOLIDAY_SCHEMA_START_YEAR && holiday.nextMonday, 86 | }; 87 | } 88 | 89 | export default getHoliday; 90 | -------------------------------------------------------------------------------- /src/holidays.ts: -------------------------------------------------------------------------------- 1 | import type { DateHoliday, EasterHoliday } from "./types"; 2 | 3 | const dateHolidays: DateHoliday[] = [ 4 | { 5 | month: 1, 6 | day: 1, 7 | name: { 8 | es: "Año Nuevo", 9 | en: "New Year's Day", 10 | }, 11 | nextMonday: false, 12 | }, 13 | { 14 | month: 1, 15 | day: 6, 16 | name: { 17 | es: "Reyes Magos", 18 | en: "Epiphany", 19 | }, 20 | nextMonday: true, 21 | }, 22 | { 23 | month: 3, 24 | day: 19, 25 | name: { 26 | es: "San José", 27 | en: "Saint Joseph's Day", 28 | }, 29 | nextMonday: true, 30 | }, 31 | { 32 | month: 5, 33 | day: 1, 34 | name: { 35 | es: "Día del Trabajo", 36 | en: "Labour Day", 37 | }, 38 | nextMonday: false, 39 | }, 40 | { 41 | month: 6, 42 | day: 29, 43 | name: { 44 | es: "San Pedro y San Pablo", 45 | en: "Saint Peter and Saint Paul", 46 | }, 47 | nextMonday: true, 48 | }, 49 | { 50 | month: 7, 51 | day: 20, 52 | name: { 53 | es: "Grito de la Independencia", 54 | en: "Declaration of Independence", 55 | }, 56 | nextMonday: false, 57 | }, 58 | { 59 | month: 8, 60 | day: 7, 61 | name: { 62 | es: "Batalla de Boyacá", 63 | en: "Battle of Boyacá", 64 | }, 65 | nextMonday: false, 66 | }, 67 | { 68 | month: 8, 69 | day: 15, 70 | name: { 71 | es: "Asunción de la Virgen", 72 | en: "Assumption of Mary", 73 | }, 74 | nextMonday: true, 75 | }, 76 | { 77 | month: 10, 78 | day: 12, 79 | name: { 80 | es: "Día de la Raza", 81 | en: "Columbus Day", 82 | }, 83 | nextMonday: true, 84 | }, 85 | { 86 | month: 11, 87 | day: 1, 88 | name: { 89 | es: "Todos los Santos", 90 | en: "All Saints’ Day", 91 | }, 92 | nextMonday: true, 93 | }, 94 | { 95 | month: 11, 96 | day: 11, 97 | name: { es: "Independencia de Cartagena", en: "Independence of Cartagena" }, 98 | nextMonday: true, 99 | }, 100 | { 101 | month: 12, 102 | day: 8, 103 | name: { es: "Inmaculada Concepción", en: "Immaculate Conception" }, 104 | nextMonday: false, 105 | }, 106 | { 107 | month: 12, 108 | day: 25, 109 | name: { es: "Navidad", en: "Christmas" }, 110 | nextMonday: false, 111 | }, 112 | ]; 113 | 114 | // We could simplify the calculation by setting the offset to match Monday. 115 | // For example, the date for the 'Corpus Christi' is 60 days after Easter 116 | // and that's the date it is celebrated in most countries. In Colombia, 117 | // that date is moved to the next monday, hence, we use 60 for the offset 118 | // and then get the next monday instead of directly using 64 as the offset. 119 | // https://www.alcaldiabogota.gov.co/sisjur/normas/Norma1.jsp?i=4954 120 | const easterHolidays: EasterHoliday[] = [ 121 | { 122 | offset: -3, 123 | name: { es: "Jueves Santo", en: "Maundy Thursday" }, 124 | nextMonday: false, 125 | }, 126 | { 127 | offset: -2, 128 | name: { es: "Viernes Santo", en: "Good Friday" }, 129 | nextMonday: false, 130 | }, 131 | { 132 | offset: 39, 133 | name: { es: "Ascensión del Señor", en: "Ascension of Jesus" }, 134 | nextMonday: true, 135 | }, 136 | { 137 | offset: 60, 138 | name: { es: "Corpus Christi", en: "Corpus Christi" }, 139 | nextMonday: true, 140 | }, 141 | { 142 | offset: 68, 143 | name: { es: "Sagrado Corazón de Jesús", en: "Sacred Heart" }, 144 | nextMonday: true, 145 | }, 146 | ]; 147 | 148 | export default [...dateHolidays, ...easterHolidays]; 149 | -------------------------------------------------------------------------------- /src/utils/holidaysWithinInterval.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { holidaysWithinInterval } from "./holidaysWithinInterval"; 3 | 4 | describe("holidaysWithinInterval", () => { 5 | it("should throw an error if end is equal to start", () => { 6 | const start = new Date("2022-01-01"); 7 | const end = new Date("2022-01-01"); 8 | expect(() => holidaysWithinInterval({ start, end })).toThrowError( 9 | "end date should be greater than start date", 10 | ); 11 | }); 12 | 13 | it("should throw an error if start is greater than end", () => { 14 | const start = new Date("2022-01-02"); 15 | const end = new Date("2022-01-01"); 16 | expect(() => holidaysWithinInterval({ start, end })).toThrowError( 17 | "end date should be greater than start date", 18 | ); 19 | }); 20 | 21 | it("should return the correct number of holidays overlapping two years", () => { 22 | const start = new Date("2020-12-01"); 23 | const end = new Date("2021-01-15"); 24 | const result = holidaysWithinInterval({ end, start }); 25 | 26 | expect(result).toEqual([ 27 | { 28 | celebrationDate: "2020-12-08", 29 | date: "2020-12-08", 30 | name: expect.objectContaining({ es: "Inmaculada Concepción" }), 31 | nextMonday: false, 32 | }, 33 | { 34 | celebrationDate: "2020-12-25", 35 | date: "2020-12-25", 36 | name: expect.objectContaining({ es: "Navidad" }), 37 | nextMonday: false, 38 | }, 39 | { 40 | celebrationDate: "2021-01-01", 41 | date: "2021-01-01", 42 | name: expect.objectContaining({ es: "Año Nuevo" }), 43 | nextMonday: false, 44 | }, 45 | { 46 | celebrationDate: "2021-01-11", 47 | date: "2021-01-06", 48 | name: expect.objectContaining({ es: "Reyes Magos" }), 49 | nextMonday: true, 50 | }, 51 | ]); 52 | }); 53 | 54 | it("should return the correct number of holidays inclusive", () => { 55 | const start = new Date("2021-01-01"); 56 | const end = new Date("2021-01-11"); 57 | const result = holidaysWithinInterval({ start, end }); 58 | 59 | expect(result).toEqual([ 60 | { 61 | celebrationDate: "2021-01-01", 62 | date: "2021-01-01", 63 | name: expect.objectContaining({ es: "Año Nuevo" }), 64 | nextMonday: false, 65 | }, 66 | { 67 | celebrationDate: "2021-01-11", 68 | date: "2021-01-06", 69 | name: expect.objectContaining({ es: "Reyes Magos" }), 70 | nextMonday: true, 71 | }, 72 | ]); 73 | }); 74 | 75 | it("should return the correct number of holidays for a given year", () => { 76 | const start = new Date("2021-01-01"); 77 | const end = new Date("2021-12-31"); 78 | const result = holidaysWithinInterval({ start, end }); 79 | 80 | expect(result.length).toBe(18); 81 | expect(result).toEqual( 82 | expect.arrayContaining([ 83 | expect.objectContaining({ 84 | date: expect.any(String), 85 | celebrationDate: expect.any(String), 86 | }), 87 | ]), 88 | ); 89 | }); 90 | 91 | it("should return the correct number of holidays for a given year with native JS dates", () => { 92 | const start = new Date("2021-01-01"); 93 | const end = new Date("2021-12-31"); 94 | const result = holidaysWithinInterval({ 95 | start, 96 | end, 97 | valueAsDate: true, 98 | }); 99 | expect(result.length).toBe(18); 100 | expect(result).toEqual( 101 | expect.arrayContaining([ 102 | expect.objectContaining({ 103 | date: expect.any(Date), 104 | celebrationDate: expect.any(Date), 105 | }), 106 | ]), 107 | ); 108 | }); 109 | 110 | it("should return the correct number of holidays overlapping three years", () => { 111 | const start = new Date("2014-07-20"); 112 | const end = new Date("2016-03-25"); 113 | const result = holidaysWithinInterval({ start, end }); 114 | 115 | expect(result.length).toBe(31); 116 | }); 117 | }); 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Colombian Holidays 🎆 2 | 3 | [![npm version](https://badge.fury.io/js/colombian-holidays.svg)](https://badge.fury.io/js/colombian-holidays) 4 | 5 | TypeScript module to get colombian holidays for any given year. 6 | 7 | ## Installation 8 | 9 | To install as a dependency of your project: 10 | 11 | ```sh 12 | npm install colombian-holidays 13 | ``` 14 | 15 | ## Usage 16 | 17 | The default export is a function to get all the holidays for a given year. 18 | 19 | The `year` should be between 1583 and 4099 (see [Pascua](https://github.com/MauricioRobayo/pascua) package). 20 | 21 | ### CommonJS 22 | 23 | ```js 24 | const colombianHolidays = require("colombian-holidays").default; 25 | ``` 26 | 27 | ### ES6 Modules 28 | 29 | ```js 30 | import colombianHolidays from "colombian-holidays"; 31 | ``` 32 | 33 | You get a function that you can use to get the complete list of holidays for a given year: 34 | 35 | ```js 36 | const colombianHolidays2015 = colombianHolidays({ year: 2015 }); 37 | ``` 38 | 39 | # Examples 40 | 41 | Get current year holidays 42 | 43 | ```sh 44 | npm run ts-node examples/currentDate 45 | ``` 46 | 47 | Get next year holidays 48 | 49 | ```sh 50 | npm run ts-node examples/nextYear 51 | ``` 52 | 53 | The content of the `colombianHolidays2015` variable will be the following array: 54 | 55 | ```js 56 | [ 57 | { 58 | date: "2015-01-01", 59 | celebrationDate: "2015-01-01", 60 | name: { 61 | es: "Año Nuevo", 62 | en: "New Year's day", 63 | }, 64 | nextMonday: false, 65 | }, 66 | { 67 | date: "2015-01-06", 68 | celebrationDate: "2015-01-12", 69 | name: { 70 | es: "Reyes Magos", 71 | en: "Epiphany", 72 | }, 73 | nextMonday: true, 74 | }, 75 | { 76 | date: "2015-03-19", 77 | celebrationDate: "2015-03-23", 78 | name: { 79 | es: "San José", 80 | en: "Saint Joseph's Day", 81 | }, 82 | nextMonday: true, 83 | }, 84 | { 85 | date: "2015-04-02", 86 | celebrationDate: "2015-04-02", 87 | { es: "Jueves Santo", en: "Maundy Thursday" }, 88 | nextMonday: false, 89 | }, 90 | { 91 | date: "2015-04-03", 92 | celebrationDate: "2015-04-03", 93 | { es: "Viernes Santo", en: "Good Friday" }, 94 | nextMonday: false, 95 | }, 96 | { 97 | date: "2015-05-01", 98 | celebrationDate: "2015-05-01", 99 | name: { 100 | es: "Día del Trabajo", 101 | en: "Labour Day", 102 | } 103 | nextMonday: false, 104 | }, 105 | { 106 | date: "2015-05-14", 107 | celebrationDate: "2015-05-18", 108 | name: { es: "Ascensión del Señor", en: "Ascension of Jesus" }, 109 | nextMonday: true, 110 | }, 111 | { 112 | date: "2015-06-04", 113 | celebrationDate: "2015-06-08", 114 | name: { es: "Corpus Christi", en: "Corpus Christi" }, 115 | nextMonday: true, 116 | }, 117 | { 118 | date: "2015-06-12", 119 | celebrationDate: "2015-06-15", 120 | name: { es: "Sagrado Corazón de Jesús", en: "Sacred Heart" }, 121 | nextMonday: true, 122 | }, 123 | { 124 | date: "2015-06-29", 125 | celebrationDate: "2015-06-29", 126 | name: { 127 | es: "San Pedro y San Pablo", 128 | en: "Saint Peter and Saint Paul", 129 | }, 130 | nextMonday: true, 131 | }, 132 | { 133 | date: "2015-07-20", 134 | celebrationDate: "2015-07-20", 135 | name: { 136 | es: "Grito de la Independencia", 137 | en: "Declaration of Independence", 138 | }, 139 | nextMonday: false, 140 | }, 141 | { 142 | date: "2015-08-07", 143 | celebrationDate: "2015-08-07", 144 | name: { 145 | es: "Batalla de Boyacá", 146 | en: "Battle of Boyacá", 147 | }, 148 | nextMonday: false, 149 | }, 150 | { 151 | date: "2015-08-15", 152 | celebrationDate: "2015-08-17", 153 | name: { 154 | es: "Asunción de la Virgen", 155 | en: "Assumption of Mary", 156 | }, 157 | nextMonday: true, 158 | }, 159 | { 160 | date: "2015-10-12", 161 | celebrationDate: "2015-10-12", 162 | name: { 163 | es: "Día de la Raza", 164 | en: "Columbus Day", 165 | }, 166 | nextMonday: true, 167 | }, 168 | { 169 | date: "2015-11-01", 170 | celebrationDate: "2015-11-02", 171 | name: { 172 | es: "Todos los Santos", 173 | en: "All Saints’ Day", 174 | }, 175 | nextMonday: true, 176 | }, 177 | { 178 | date: "2015-11-11", 179 | celebrationDate: "2015-11-16", 180 | name: { es: "Independencia de Cartagena", en: "Independence of Cartagena" }, 181 | nextMonday: true, 182 | }, 183 | { 184 | date: "2015-12-08", 185 | celebrationDate: "2015-12-08", 186 | name: { es: "Inmaculada Concepción", en: "Immaculate Conception" }, 187 | nextMonday: false, 188 | }, 189 | { 190 | date: "2015-12-25", 191 | celebrationDate: "2015-12-25", 192 | name: { es: "Navidad", en: "Christmas" }, 193 | nextMonday: false, 194 | }, 195 | ]; 196 | ``` 197 | 198 | Optionally, you can request the holidays for just a given month: 199 | 200 | ```js 201 | const colombianHolidays2015 = colombianHolidays({ 202 | year: 2015, 203 | month: 1 /* January */, 204 | }); 205 | ``` 206 | 207 | Returns: 208 | 209 | ```js 210 | [ 211 | { 212 | date: "2015-01-01", 213 | celebrationDate: "2015-01-01", 214 | name: { 215 | es: "Año Nuevo", 216 | en: "New Year's day", 217 | }, 218 | nextMonday: false, 219 | }, 220 | { 221 | date: "2015-01-06", 222 | celebrationDate: "2015-01-12", 223 | name: { 224 | es: "Reyes Magos", 225 | en: "Epiphany", 226 | }, 227 | nextMonday: true, 228 | }, 229 | ]; 230 | ``` 231 | 232 | Run the previous example 233 | 234 | ```sh 235 | npm run ts-node examples/monthHolidays 236 | ``` 237 | 238 | You can opt-in to have the function return native JavaScript dates instead of strings for the `date` and `celebrationDate` properties by using the `valueAsDate` option: 239 | 240 | ```js 241 | const colombianHolidays2015 = colombianHolidays({ 242 | year: 2015, 243 | valueAsDate: true, 244 | }); 245 | ``` 246 | 247 | If the year is omitted, by default the function will return the holidays for the current year: 248 | 249 | ```js 250 | const currentYearHolidaysAsStrings = colombianHolidays(); 251 | const currentYearHolidaysAsDates = colombianHolidays({ valueAsDate: true }); 252 | ``` 253 | 254 | ## Utils 255 | 256 | The package provides four helper functions: 257 | 258 | ### getHolidaysByYear 259 | 260 | Returns the list of all Colombian holidays for a given year. 261 | 262 | ```js 263 | import { getHolidaysByYear } from "colombian-holidays"; 264 | 265 | const holidays = getHolidaysByYear(2025); 266 | ``` 267 | 268 | You can also pass the `valueAsDate` option to return native JavaScript `Date` objects instead of ISO strings: 269 | 270 | ```js 271 | const holidaysAsDates = getHolidaysByYear(2025, { valueAsDate: true }); 272 | ``` 273 | 274 | This function is equivalent to calling: 275 | 276 | ```js 277 | colombianHolidays({ year: 2025 }); 278 | ``` 279 | 280 | But uses an in-memory cache. 281 | 282 | > [!TIP] 283 | > Use `getHolidaysByYear` instead of `colombianHolidays` when possible. It includes built-in caching, which improves performance and avoids redundant computations when accessing holidays by year. It is used under the hood by all other helpers, providing a shared in-memory cache. 284 | 285 | ### isHoliday 286 | 287 | Returns `true` if the given date is a colombian holiday, otherwise returns `false`. 288 | 289 | ```js 290 | import { isHoliday } from "colombian-holidays"; 291 | 292 | const date = new Date("2018-01-01"); 293 | 294 | if (isHoliday(date)) { 295 | console.log("it is a colombian holiday"); 296 | } else { 297 | console.log("it is NOT a colombian holiday"); 298 | } 299 | ``` 300 | 301 | ### getHoliday 302 | 303 | Similar to `isHoliday` but will return the corresponding holiday for a given date or `null` if there is no matching holiday. 304 | 305 | ```js 306 | import { getHoliday } from "colombian-holidays"; 307 | 308 | const date = new Date("2018-01-01"); 309 | 310 | const newYear = getHoliday(date); 311 | /* 312 | { 313 | celebrationDate: "2018-01-01", 314 | date: "2018-01-01", 315 | name: { en: "New Year's Day", es: "Año Nuevo" }, 316 | nextMonday: false, 317 | } 318 | */ 319 | ``` 320 | 321 | To get the values as date use the options argument: 322 | 323 | ```js 324 | const newYearAsDate = getHoliday(date, { valueAsDate: true }); 325 | ``` 326 | 327 | ### holidaysWithinInterval 328 | 329 | Returns an array with the colombian holidays within two dates: 330 | 331 | ```js 332 | import { holidaysWithinInterval } from "colombian-holidays"; 333 | 334 | const start = new Date("2021-01-01"); 335 | const end = new Date("2021-01-11"); 336 | const holidays = holidaysWithinInterval({ start, end }); 337 | // const holidays = holidaysWithinInterval({ start, end, valueAsDate: true }); 338 | ``` 339 | 340 | returns: 341 | 342 | ```js 343 | [ 344 | { 345 | celebrationDate: "2021-01-01", 346 | date: "2021-01-01", 347 | name: { 348 | es: "Año Nuevo", 349 | en: "New Year's day", 350 | }, 351 | nextMonday: false, 352 | }, 353 | { 354 | celebrationDate: "2021-01-11", 355 | date: "2021-01-06", 356 | name: { 357 | es: "Reyes Magos", 358 | en: "Epiphany", 359 | }, 360 | nextMonday: true, 361 | }, 362 | ]; 363 | ``` 364 | 365 | ### TypeScript 366 | 367 | The module is written in TypeScript and type definitions files are included. 368 | 369 | ## License 370 | 371 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FMauricioRobayo%2Fcolombian-holidays.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FMauricioRobayo%2Fcolombian-holidays?ref=badge_large) 372 | -------------------------------------------------------------------------------- /src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import colombianHolidays, { FIRST_HOLIDAY_YEAR, LAST_HOLIDAY_YEAR } from "."; 3 | import type { ColombianHoliday, ColombianHolidayWithNativeDate } from "./types"; 4 | 5 | const holidaysYears: Record = { 6 | 1976: [ 7 | { 8 | date: "1976-01-01", 9 | celebrationDate: "1976-01-01", 10 | name: expect.objectContaining({ es: "Año Nuevo" }), 11 | nextMonday: false, 12 | }, 13 | { 14 | date: "1976-01-06", 15 | celebrationDate: "1976-01-06", 16 | name: expect.objectContaining({ es: "Reyes Magos" }), 17 | nextMonday: false, 18 | }, 19 | { 20 | date: "1976-03-19", 21 | celebrationDate: "1976-03-19", 22 | name: expect.objectContaining({ es: "San José" }), 23 | nextMonday: false, 24 | }, 25 | { 26 | date: "1976-04-15", 27 | celebrationDate: "1976-04-15", 28 | name: expect.objectContaining({ es: "Jueves Santo" }), 29 | nextMonday: false, 30 | }, 31 | { 32 | date: "1976-04-16", 33 | celebrationDate: "1976-04-16", 34 | name: expect.objectContaining({ es: "Viernes Santo" }), 35 | nextMonday: false, 36 | }, 37 | { 38 | date: "1976-05-01", 39 | celebrationDate: "1976-05-01", 40 | name: expect.objectContaining({ es: "Día del Trabajo" }), 41 | nextMonday: false, 42 | }, 43 | { 44 | date: "1976-05-27", 45 | celebrationDate: "1976-05-27", 46 | name: expect.objectContaining({ es: "Ascensión del Señor" }), 47 | nextMonday: false, 48 | }, 49 | { 50 | date: "1976-06-17", 51 | celebrationDate: "1976-06-17", 52 | name: expect.objectContaining({ es: "Corpus Christi" }), 53 | nextMonday: false, 54 | }, 55 | { 56 | date: "1976-06-25", 57 | celebrationDate: "1976-06-25", 58 | name: expect.objectContaining({ es: "Sagrado Corazón de Jesús" }), 59 | nextMonday: false, 60 | }, 61 | { 62 | date: "1976-06-29", 63 | celebrationDate: "1976-06-29", 64 | name: expect.objectContaining({ es: "San Pedro y San Pablo" }), 65 | nextMonday: false, 66 | }, 67 | { 68 | date: "1976-07-20", 69 | celebrationDate: "1976-07-20", 70 | name: expect.objectContaining({ es: "Grito de la Independencia" }), 71 | nextMonday: false, 72 | }, 73 | { 74 | date: "1976-08-07", 75 | celebrationDate: "1976-08-07", 76 | name: expect.objectContaining({ es: "Batalla de Boyacá" }), 77 | nextMonday: false, 78 | }, 79 | { 80 | date: "1976-08-15", 81 | celebrationDate: "1976-08-15", 82 | name: expect.objectContaining({ es: "Asunción de la Virgen" }), 83 | nextMonday: false, 84 | }, 85 | { 86 | date: "1976-10-12", 87 | celebrationDate: "1976-10-12", 88 | name: expect.objectContaining({ es: "Día de la Raza" }), 89 | nextMonday: false, 90 | }, 91 | { 92 | date: "1976-11-01", 93 | celebrationDate: "1976-11-01", 94 | name: expect.objectContaining({ es: "Todos los Santos" }), 95 | nextMonday: false, 96 | }, 97 | { 98 | date: "1976-11-11", 99 | celebrationDate: "1976-11-11", 100 | name: expect.objectContaining({ es: "Independencia de Cartagena" }), 101 | nextMonday: false, 102 | }, 103 | { 104 | date: "1976-12-08", 105 | celebrationDate: "1976-12-08", 106 | name: expect.objectContaining({ es: "Inmaculada Concepción" }), 107 | nextMonday: false, 108 | }, 109 | { 110 | date: "1976-12-25", 111 | celebrationDate: "1976-12-25", 112 | name: expect.objectContaining({ es: "Navidad" }), 113 | nextMonday: false, 114 | }, 115 | ], 116 | 1983: [ 117 | { 118 | date: "1983-01-01", 119 | celebrationDate: "1983-01-01", 120 | name: expect.objectContaining({ es: "Año Nuevo" }), 121 | nextMonday: false, 122 | }, 123 | { 124 | date: "1983-01-06", 125 | celebrationDate: "1983-01-06", 126 | name: expect.objectContaining({ es: "Reyes Magos" }), 127 | nextMonday: false, 128 | }, 129 | { 130 | date: "1983-03-19", 131 | celebrationDate: "1983-03-19", 132 | name: expect.objectContaining({ es: "San José" }), 133 | nextMonday: false, 134 | }, 135 | { 136 | date: "1983-03-31", 137 | celebrationDate: "1983-03-31", 138 | name: expect.objectContaining({ es: "Jueves Santo" }), 139 | nextMonday: false, 140 | }, 141 | { 142 | date: "1983-04-01", 143 | celebrationDate: "1983-04-01", 144 | name: expect.objectContaining({ es: "Viernes Santo" }), 145 | nextMonday: false, 146 | }, 147 | { 148 | date: "1983-05-01", 149 | celebrationDate: "1983-05-01", 150 | name: expect.objectContaining({ es: "Día del Trabajo" }), 151 | nextMonday: false, 152 | }, 153 | { 154 | date: "1983-05-12", 155 | celebrationDate: "1983-05-12", 156 | name: expect.objectContaining({ es: "Ascensión del Señor" }), 157 | nextMonday: false, 158 | }, 159 | { 160 | date: "1983-06-02", 161 | celebrationDate: "1983-06-02", 162 | name: expect.objectContaining({ es: "Corpus Christi" }), 163 | nextMonday: false, 164 | }, 165 | { 166 | date: "1983-06-10", 167 | celebrationDate: "1983-06-10", 168 | name: expect.objectContaining({ es: "Sagrado Corazón de Jesús" }), 169 | nextMonday: false, 170 | }, 171 | { 172 | date: "1983-06-29", 173 | celebrationDate: "1983-06-29", 174 | name: expect.objectContaining({ es: "San Pedro y San Pablo" }), 175 | nextMonday: false, 176 | }, 177 | { 178 | date: "1983-07-20", 179 | celebrationDate: "1983-07-20", 180 | name: expect.objectContaining({ es: "Grito de la Independencia" }), 181 | nextMonday: false, 182 | }, 183 | { 184 | date: "1983-08-07", 185 | celebrationDate: "1983-08-07", 186 | name: expect.objectContaining({ es: "Batalla de Boyacá" }), 187 | nextMonday: false, 188 | }, 189 | { 190 | date: "1983-08-15", 191 | celebrationDate: "1983-08-15", 192 | name: expect.objectContaining({ es: "Asunción de la Virgen" }), 193 | nextMonday: false, 194 | }, 195 | { 196 | date: "1983-10-12", 197 | celebrationDate: "1983-10-12", 198 | name: expect.objectContaining({ es: "Día de la Raza" }), 199 | nextMonday: false, 200 | }, 201 | { 202 | date: "1983-11-01", 203 | celebrationDate: "1983-11-01", 204 | name: expect.objectContaining({ es: "Todos los Santos" }), 205 | nextMonday: false, 206 | }, 207 | { 208 | date: "1983-11-11", 209 | celebrationDate: "1983-11-11", 210 | name: expect.objectContaining({ es: "Independencia de Cartagena" }), 211 | nextMonday: false, 212 | }, 213 | { 214 | date: "1983-12-08", 215 | celebrationDate: "1983-12-08", 216 | name: expect.objectContaining({ es: "Inmaculada Concepción" }), 217 | nextMonday: false, 218 | }, 219 | { 220 | date: "1983-12-25", 221 | celebrationDate: "1983-12-25", 222 | name: expect.objectContaining({ es: "Navidad" }), 223 | nextMonday: false, 224 | }, 225 | ], 226 | 2015: [ 227 | { 228 | date: "2015-01-01", 229 | celebrationDate: "2015-01-01", 230 | name: expect.objectContaining({ es: "Año Nuevo" }), 231 | nextMonday: false, 232 | }, 233 | { 234 | date: "2015-01-06", 235 | celebrationDate: "2015-01-12", 236 | name: expect.objectContaining({ es: "Reyes Magos" }), 237 | nextMonday: true, 238 | }, 239 | { 240 | date: "2015-03-19", 241 | celebrationDate: "2015-03-23", 242 | name: expect.objectContaining({ es: "San José" }), 243 | nextMonday: true, 244 | }, 245 | { 246 | date: "2015-04-02", 247 | celebrationDate: "2015-04-02", 248 | name: expect.objectContaining({ es: "Jueves Santo" }), 249 | nextMonday: false, 250 | }, 251 | { 252 | date: "2015-04-03", 253 | celebrationDate: "2015-04-03", 254 | name: expect.objectContaining({ es: "Viernes Santo" }), 255 | nextMonday: false, 256 | }, 257 | { 258 | date: "2015-05-01", 259 | celebrationDate: "2015-05-01", 260 | name: expect.objectContaining({ es: "Día del Trabajo" }), 261 | nextMonday: false, 262 | }, 263 | { 264 | date: "2015-05-14", 265 | celebrationDate: "2015-05-18", 266 | name: expect.objectContaining({ es: "Ascensión del Señor" }), 267 | nextMonday: true, 268 | }, 269 | { 270 | date: "2015-06-04", 271 | celebrationDate: "2015-06-08", 272 | name: expect.objectContaining({ es: "Corpus Christi" }), 273 | nextMonday: true, 274 | }, 275 | { 276 | date: "2015-06-12", 277 | celebrationDate: "2015-06-15", 278 | name: expect.objectContaining({ es: "Sagrado Corazón de Jesús" }), 279 | nextMonday: true, 280 | }, 281 | { 282 | date: "2015-06-29", 283 | celebrationDate: "2015-06-29", 284 | name: expect.objectContaining({ es: "San Pedro y San Pablo" }), 285 | nextMonday: true, 286 | }, 287 | { 288 | date: "2015-07-20", 289 | celebrationDate: "2015-07-20", 290 | name: expect.objectContaining({ es: "Grito de la Independencia" }), 291 | nextMonday: false, 292 | }, 293 | { 294 | date: "2015-08-07", 295 | celebrationDate: "2015-08-07", 296 | name: expect.objectContaining({ es: "Batalla de Boyacá" }), 297 | nextMonday: false, 298 | }, 299 | { 300 | date: "2015-08-15", 301 | celebrationDate: "2015-08-17", 302 | name: expect.objectContaining({ es: "Asunción de la Virgen" }), 303 | nextMonday: true, 304 | }, 305 | { 306 | date: "2015-10-12", 307 | celebrationDate: "2015-10-12", 308 | name: expect.objectContaining({ es: "Día de la Raza" }), 309 | nextMonday: true, 310 | }, 311 | { 312 | date: "2015-11-01", 313 | celebrationDate: "2015-11-02", 314 | name: expect.objectContaining({ es: "Todos los Santos" }), 315 | nextMonday: true, 316 | }, 317 | { 318 | date: "2015-11-11", 319 | celebrationDate: "2015-11-16", 320 | name: expect.objectContaining({ es: "Independencia de Cartagena" }), 321 | nextMonday: true, 322 | }, 323 | { 324 | date: "2015-12-08", 325 | celebrationDate: "2015-12-08", 326 | name: expect.objectContaining({ es: "Inmaculada Concepción" }), 327 | nextMonday: false, 328 | }, 329 | { 330 | date: "2015-12-25", 331 | celebrationDate: "2015-12-25", 332 | name: expect.objectContaining({ es: "Navidad" }), 333 | nextMonday: false, 334 | }, 335 | ], 336 | 2018: [ 337 | { 338 | date: "2018-01-01", 339 | celebrationDate: "2018-01-01", 340 | name: expect.objectContaining({ es: "Año Nuevo" }), 341 | nextMonday: false, 342 | }, 343 | { 344 | date: "2018-01-06", 345 | celebrationDate: "2018-01-08", 346 | name: expect.objectContaining({ es: "Reyes Magos" }), 347 | nextMonday: true, 348 | }, 349 | { 350 | date: "2018-03-19", 351 | celebrationDate: "2018-03-19", 352 | name: expect.objectContaining({ es: "San José" }), 353 | nextMonday: true, 354 | }, 355 | { 356 | date: "2018-03-29", 357 | celebrationDate: "2018-03-29", 358 | name: expect.objectContaining({ es: "Jueves Santo" }), 359 | nextMonday: false, 360 | }, 361 | { 362 | date: "2018-03-30", 363 | celebrationDate: "2018-03-30", 364 | name: expect.objectContaining({ es: "Viernes Santo" }), 365 | nextMonday: false, 366 | }, 367 | { 368 | date: "2018-05-01", 369 | celebrationDate: "2018-05-01", 370 | name: expect.objectContaining({ es: "Día del Trabajo" }), 371 | nextMonday: false, 372 | }, 373 | { 374 | date: "2018-05-10", 375 | celebrationDate: "2018-05-14", 376 | name: expect.objectContaining({ es: "Ascensión del Señor" }), 377 | nextMonday: true, 378 | }, 379 | { 380 | date: "2018-05-31", 381 | celebrationDate: "2018-06-04", 382 | name: expect.objectContaining({ es: "Corpus Christi" }), 383 | nextMonday: true, 384 | }, 385 | { 386 | date: "2018-06-08", 387 | celebrationDate: "2018-06-11", 388 | name: expect.objectContaining({ es: "Sagrado Corazón de Jesús" }), 389 | nextMonday: true, 390 | }, 391 | { 392 | date: "2018-06-29", 393 | celebrationDate: "2018-07-02", 394 | name: expect.objectContaining({ es: "San Pedro y San Pablo" }), 395 | nextMonday: true, 396 | }, 397 | { 398 | date: "2018-07-20", 399 | celebrationDate: "2018-07-20", 400 | name: expect.objectContaining({ es: "Grito de la Independencia" }), 401 | nextMonday: false, 402 | }, 403 | { 404 | date: "2018-08-07", 405 | celebrationDate: "2018-08-07", 406 | name: expect.objectContaining({ es: "Batalla de Boyacá" }), 407 | nextMonday: false, 408 | }, 409 | { 410 | date: "2018-08-15", 411 | celebrationDate: "2018-08-20", 412 | name: expect.objectContaining({ es: "Asunción de la Virgen" }), 413 | nextMonday: true, 414 | }, 415 | { 416 | date: "2018-10-12", 417 | celebrationDate: "2018-10-15", 418 | name: expect.objectContaining({ es: "Día de la Raza" }), 419 | nextMonday: true, 420 | }, 421 | { 422 | date: "2018-11-01", 423 | celebrationDate: "2018-11-05", 424 | name: expect.objectContaining({ es: "Todos los Santos" }), 425 | nextMonday: true, 426 | }, 427 | { 428 | date: "2018-11-11", 429 | celebrationDate: "2018-11-12", 430 | name: expect.objectContaining({ es: "Independencia de Cartagena" }), 431 | nextMonday: true, 432 | }, 433 | { 434 | date: "2018-12-08", 435 | celebrationDate: "2018-12-08", 436 | name: expect.objectContaining({ es: "Inmaculada Concepción" }), 437 | nextMonday: false, 438 | }, 439 | { 440 | date: "2018-12-25", 441 | celebrationDate: "2018-12-25", 442 | name: expect.objectContaining({ es: "Navidad" }), 443 | nextMonday: false, 444 | }, 445 | ], 446 | }; 447 | 448 | const years = Object.keys(holidaysYears).map(Number); 449 | const months = Array.from({ length: 12 }, (_, i) => i + 1); 450 | 451 | describe.each(years)("Gets all holidays for %p", (year) => { 452 | describe.each(months)( 453 | "Get only corresponding holidays for month %p", 454 | (month) => { 455 | it("should return all holidays for %p", () => { 456 | const holidays = colombianHolidays({ year, month }); 457 | const expected = holidaysYears[year].filter( 458 | (holiday) => Number(holiday.celebrationDate.slice(5, 7)) === month, 459 | ); 460 | expect(holidays).toEqual(expected); 461 | }); 462 | 463 | it("should return all holidays for %p when using native dates", () => { 464 | const holidays = colombianHolidays({ 465 | year, 466 | month, 467 | valueAsDate: true, 468 | }); 469 | const expected = holidaysYears[year] 470 | .filter( 471 | (holiday) => Number(holiday.celebrationDate.slice(5, 7)) === month, 472 | ) 473 | .map(transformStringsToDates); 474 | expect(holidays).toEqual(expected); 475 | }); 476 | }, 477 | ); 478 | 479 | it("Should return holidays formatted as string for %p if no options given", () => { 480 | expect(colombianHolidays({ year })).toEqual(holidaysYears[year]); 481 | }); 482 | 483 | it("Should return holidays formatted as string for %p if valueAsDate is set to false", () => { 484 | expect(colombianHolidays({ year, valueAsDate: false })).toEqual( 485 | holidaysYears[year], 486 | ); 487 | }); 488 | 489 | it("Should return holidays with native JS date for %p if valueAsDate is set to true", () => { 490 | expect(colombianHolidays({ year, valueAsDate: true })).toEqual( 491 | holidaysYears[year].map(transformStringsToDates), 492 | ); 493 | }); 494 | }); 495 | 496 | describe("Gets all holidays for the current year", () => { 497 | it("Should return holidays for the current year when no year is given", () => { 498 | const currYear = new Date().getFullYear(); 499 | const currHols = colombianHolidays(); 500 | const holidaysInAYear = 18; 501 | expect(currHols).toEqual(colombianHolidays({ year: currYear })); 502 | expect(Array.isArray(currHols)).toBe(true); 503 | expect(currHols.length).toBe(holidaysInAYear); 504 | }); 505 | 506 | describe("Current year", () => { 507 | it("should return 2 holidays for month 1 for current year when no year is given", () => { 508 | const holidays = colombianHolidays({ month: 1 }); 509 | expect(Array.isArray(holidays)).toBe(true); 510 | expect(holidays.length).toBe(2); 511 | }); 512 | 513 | it("Should return holidays for the current year when no year is given", () => { 514 | const currYear = new Date().getFullYear(); 515 | const currHols = colombianHolidays(); 516 | const holidaysInAYear = 18; 517 | expect(currHols).toEqual(colombianHolidays({ year: currYear })); 518 | expect(Array.isArray(currHols)).toBe(true); 519 | expect(currHols.length).toBe(holidaysInAYear); 520 | }); 521 | }); 522 | }); 523 | 524 | describe("Should throw an error for a non valid year", () => { 525 | it(`should throw an error for a year below ${FIRST_HOLIDAY_YEAR}`, () => { 526 | expect(() => colombianHolidays({ year: FIRST_HOLIDAY_YEAR })).not.toThrow(); 527 | expect(() => colombianHolidays({ year: FIRST_HOLIDAY_YEAR - 1 })).toThrow(); 528 | }); 529 | it(`should throw an error for a year above ${LAST_HOLIDAY_YEAR}`, () => { 530 | expect(() => colombianHolidays({ year: LAST_HOLIDAY_YEAR })).not.toThrow(); 531 | expect(() => colombianHolidays({ year: LAST_HOLIDAY_YEAR + 1 })).toThrow(); 532 | }); 533 | }); 534 | 535 | function transformStringsToDates( 536 | holiday: ColombianHoliday, 537 | ): ColombianHolidayWithNativeDate { 538 | return { 539 | date: new Date(holiday.date), 540 | celebrationDate: new Date(holiday.celebrationDate), 541 | name: holiday.name, 542 | nextMonday: holiday.nextMonday, 543 | }; 544 | } 545 | --------------------------------------------------------------------------------