Lightweight, tree-shakeable holiday calculator with algorithmic date computation
npm install soff-date
Lightweight, tree-shakeable holiday calculator with algorithmic date computation.






---
Zero dependencies Β· TypeScript Β· ~3KB per locale
- Table of Contents
- π€ Why?
- π¦ Install
- π Quick Start
- π i18n Support
- Available Locales
- Available Languages
- Shift Rules Explained
- Emiliani (Colombia, Argentina)
- Observed US (USA, UK)
- Advanced: Create Your Own Locale
- Advanced: Use Algorithms Directly
- Bundle Size
- API Reference
- getHolidays(year, options?)
- isHoliday(date, options?)
- getNextHoliday(from?, options?)
- Business Days Calculation
- Types
- Contributing
- License
- Documentation
- Contributors
Most holiday libraries ship giant JSON files with dates until 2050. This library calculates dates algorithmically, supporting:
| Feature | Description | Example |
| ---------------------- | ----------------------------- | ------------------------------- |
| π
Fixed Dates | Static dates every year | December 25 (Christmas) |
| π Nth Weekday | Relative weekday calculations | 3rd Monday of January (MLK Day) |
| β¨ Easter-relative | Based on Easter calculation | Good Friday = Easter - 2 days |
| π Shift Rules | Move holidays to workdays | Colombia's Emiliani Law |
Result: Tiny bundle size (~3KB) with unlimited year support! π
``bashnpm
npm install soff-date
π Quick Start
`typescript
// Only Colombia included in bundle (~3KB)
import { getHolidays, isHoliday, getNextHoliday } from 'soff-date/locales/co';// π Get all holidays for a year
getHolidays(2025);
// β [{ date: '2025-01-01', key: 'newYear', name: 'AΓ±o Nuevo' }, ...]
// β Check if a date is a holiday
isHoliday(new Date('2025-01-01'));
// β { date: '2025-01-01', key: 'newYear', name: 'AΓ±o Nuevo' }
isHoliday(new Date('2025-01-02'));
// β null
// β‘οΈ Get next holiday from a date
getNextHoliday(new Date('2025-01-02'));
// β { date: '2025-01-06', key: 'epiphany', name: 'DΓa de los Reyes Magos' }
`π i18n Support
`typescript
import { getHolidays } from 'soff-date/locales/co';
import { en } from 'soff-date/i18n/en';getHolidays(2025, { lang: en });
// β [{ date: '2025-01-01', key: 'newYear', name: "New Year's Day" }, ...]
// Custom override
getHolidays(2025, { lang: { ...en, newYear: 'Happy New Year!' } });
`Available Locales
| Locale | Import | Holidays | Shift Rule |
| ------------ | ---------------------- | -------- | ---------- |
| π¨π΄ Colombia |
soff-date/locales/co | 18 | Emiliani |
| πΊπΈ USA | soff-date/locales/us | 10 | Observed |
| π²π½ MΓ©xico | soff-date/locales/mx | 8 | NthWeekday |
| π¦π· Argentina | soff-date/locales/ar | 16 | NearestMon |
| π§π· Brasil | soff-date/locales/br | 13 | None |Available Languages
| Language | Import |
| --------- | ------------------- |
| EspaΓ±ol |
soff-date/i18n/es |
| English | soff-date/i18n/en |
| PortuguΓͺs | soff-date/i18n/pt |Shift Rules Explained
$3
Holidays falling on weekends move to Monday.
`typescript
// January 6, 2024 = Saturday β Monday January 8
{ date: '2024-01-08', key: 'epiphany', isShifted: true }
`$3
- Saturday β Friday (before)
- Sunday β Monday (after)
`typescript
// July 4, 2026 = Saturday β Friday July 3
{ date: '2026-07-03', key: 'independenceDayUS', isShifted: true }
`Advanced: Create Your Own Locale
`typescript
import type { HolidayDefinition, Holiday, HolidayNames } from 'soff-date';
import { resolveHolidays, checkIsHoliday, findNextHoliday } from 'soff-date';const definitions: HolidayDefinition[] = [
// Fixed date
{ key: 'newYear', rule: { type: 'fixed', month: 1, day: 1 } },
// Fixed with shift
{ key: 'christmas', rule: { type: 'fixed', month: 12, day: 25 }, shift: 'observedUS' },
// Nth weekday: 3rd Monday of January
{ key: 'mlkDay', rule: { type: 'nthWeekday', month: 1, weekday: 1, n: 3 } },
// Last Monday of May
{ key: 'memorialDay', rule: { type: 'nthWeekday', month: 5, weekday: 1, n: -1 } },
// Easter relative: Good Friday = Easter - 2
{ key: 'goodFriday', rule: { type: 'easterRelative', offset: -2 } },
// Custom calculation
{
key: 'custom',
rule: {
type: 'custom',
calc: (year) => new Date(year, 5, 15), // June 15
},
},
];
const names: HolidayNames = {
newYear: "New Year's Day",
christmas: 'Christmas',
// ...
};
export function getHolidays(year: number): Holiday[] {
return resolveHolidays(definitions, year, names);
}
`Advanced: Use Algorithms Directly
`typescript
import { getEasterSunday } from 'soff-date/core/algorithms/easter';
import { getNthWeekday } from 'soff-date/core/algorithms/nth-weekday';
import { applyShift } from 'soff-date/core/algorithms/shifts';// Easter Sunday 2025
getEasterSunday(2025); // β Date(2025, 3, 20)
// 4th Thursday of November 2025 (Thanksgiving)
getNthWeekday(2025, 11, 4, 4); // β Date(2025, 10, 27)
// Apply observed shift
applyShift(new Date('2026-07-04'), 'observedUS');
// β { date: Date(2026-07-03), shifted: true }
`Bundle Size
| Import | Size (minified) |
| ------------ | --------------- |
|
locales/co | ~5.8KB |
| locales/us | ~4.5KB |
| i18n/es | ~1.9KB |
| i18n/en | ~1.1KB |
| Core only | ~2.7KB |Tree-shaking ensures you only ship what you import.
API Reference
$3
Returns all holidays for a given year.
> Note: Some holidays might fall on the same date (e.g., a fixed holiday coinciding with a movable one). Always use
holiday.key as the unique identifier, not the date.`typescript
interface GetHolidaysOptions {
lang?: HolidayNames; // Custom translations
}interface Holiday {
date: string; // ISO date: '2025-01-01'
key: string; // Identifier: 'newYear'
name: string; // Display name: 'AΓ±o Nuevo'
isShifted?: boolean; // True if moved by shift rule
}
`$3
Returns holiday info if the date is a holiday,
null otherwise.$3
Returns the next holiday from a given date (defaults to today).
Business Days Calculation
In addition to holiday calculation,
soff-date provides utilities to work with business days (skipping weekends and holidays).`typescript
import { isBusinessDay, businessDays, diffBusinessDays } from 'soff-date/locales/co';// Check if a date is a business day
isBusinessDay(new Date('2025-01-01')); // false (Holiday)
isBusinessDay(new Date('2025-01-04')); // false (Saturday)
isBusinessDay(new Date('2025-01-02')); // true
// Add business days
businessDays(new Date('2025-01-03'), 1);
// β Date('2025-01-06') (Friday + 1 business day = Monday)
// Calculate difference in business days
diffBusinessDays(new Date('2025-01-06'), new Date('2025-01-10'));
// β 4
`Types
`typescript
type ShiftRule = 'none' | 'emiliani' | 'observedUS' | 'nextMonday';type HolidayRule =
| { type: 'fixed'; month: number; day: number }
| { type: 'nthWeekday'; month: number; weekday: number; n: number }
| { type: 'easterRelative'; offset: number }
| { type: 'custom'; calc: (year: number) => Date };
interface HolidayDefinition {
key: string;
rule: HolidayRule;
shift?: ShiftRule;
}
``Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.
This project is licensed under the MIT License - see the LICENSE file for details.
- EspaΓ±ol
Thanks goes to these wonderful people (emoji key):
Luis C. Rojas π» π π§ |
This project follows the all-contributors specification. Contributions of any kind welcome!