@raven-js/cortex
Version:
Zero-dependency machine learning, AI, and data processing library for modern JavaScript
442 lines (402 loc) • 9.99 kB
JavaScript
/**
* @author Anonyfox <max@anonyfox.com>
* @license MIT
* @see {@link https://github.com/Anonyfox/ravenjs}
* @see {@link https://ravenjs.dev}
* @see {@link https://anonyfox.com}
*/
/**
* @file Malaysian holiday definitions with multicultural and regional system
*
* Malaysia has a complex multicultural holiday system:
* - Islamic holidays (official religion)
* - Chinese holidays (Chinese New Year, Wesak Day)
* - Hindu holidays (Deepavali, Thaipusam)
* - Christian holidays (Christmas, Good Friday in some states)
* - National civic holidays
* - Extensive state-level variations
*/
import { HolidayDefinition } from "../holiday-definition.js";
/**
* Calculate Chinese New Year.
* @param {number} year - Year to calculate for
* @returns {Date} Chinese New Year date
*/
function calculateChineseNewYear(year) {
/** @type {Record<number, Date>} */
const chineseNewYearDates = {
2024: new Date(Date.UTC(year, 1, 10)), // Feb 10
2025: new Date(Date.UTC(year, 0, 29)), // Jan 29
2026: new Date(Date.UTC(year, 1, 17)), // Feb 17
2027: new Date(Date.UTC(year, 1, 6)), // Feb 6
2028: new Date(Date.UTC(year, 0, 26)), // Jan 26
2029: new Date(Date.UTC(year, 1, 13)), // Feb 13
2030: new Date(Date.UTC(year, 1, 3)), // Feb 3
};
return chineseNewYearDates[year] || new Date(Date.UTC(year, 1, 10)); // fallback
}
/**
* Calculate Eid al-Fitr (Islamic calendar).
* @param {number} year - Year to calculate for
* @returns {Date} Eid al-Fitr date
*/
function calculateEidAlFitr(year) {
/** @type {Record<number, Date>} */
const eidDates = {
2024: new Date(Date.UTC(year, 3, 10)), // Apr 10
2025: new Date(Date.UTC(year, 2, 30)), // Mar 30
2026: new Date(Date.UTC(year, 2, 20)), // Mar 20
2027: new Date(Date.UTC(year, 2, 9)), // Mar 9
2028: new Date(Date.UTC(year, 1, 26)), // Feb 26
2029: new Date(Date.UTC(year, 1, 15)), // Feb 15
2030: new Date(Date.UTC(year, 1, 5)), // Feb 5
};
return eidDates[year] || new Date(Date.UTC(year, 3, 10)); // fallback
}
/**
* Calculate Eid al-Adha (Islamic calendar).
* @param {number} year - Year to calculate for
* @returns {Date} Eid al-Adha date
*/
function calculateEidAlAdha(year) {
const eidAlFitr = calculateEidAlFitr(year);
return new Date(eidAlFitr.getTime() + 70 * 24 * 60 * 60 * 1000);
}
/**
* Calculate Deepavali (Hindu Festival of Lights).
* @param {number} year - Year to calculate for
* @returns {Date} Deepavali date
*/
function calculateDeepavali(year) {
/** @type {Record<number, Date>} */
const deepavaliDates = {
2024: new Date(Date.UTC(year, 10, 1)), // Nov 1
2025: new Date(Date.UTC(year, 9, 20)), // Oct 20
2026: new Date(Date.UTC(year, 10, 8)), // Nov 8
2027: new Date(Date.UTC(year, 9, 29)), // Oct 29
2028: new Date(Date.UTC(year, 10, 17)), // Nov 17
2029: new Date(Date.UTC(year, 10, 5)), // Nov 5
2030: new Date(Date.UTC(year, 9, 26)), // Oct 26
};
return deepavaliDates[year] || new Date(Date.UTC(year, 10, 1)); // fallback
}
// National holidays observed throughout Malaysia
const NATIONAL_HOLIDAYS = [
// New Year's Day
new HolidayDefinition({
name: "New Year's Day",
type: "fixed",
month: 1,
day: 1,
workFree: true,
}),
// Federal Territory Day
new HolidayDefinition({
name: "Federal Territory Day",
type: "fixed",
month: 2,
day: 1,
workFree: true,
}),
// Chinese New Year Day 1
new HolidayDefinition({
name: "Chinese New Year",
type: "calculated",
workFree: true,
calculator: (/** @type {number} */ year) => calculateChineseNewYear(year),
}),
// Chinese New Year Day 2
new HolidayDefinition({
name: "Chinese New Year (Day 2)",
type: "calculated",
workFree: true,
calculator: (/** @type {number} */ year) => {
const cny = calculateChineseNewYear(year);
return new Date(cny.getTime() + 24 * 60 * 60 * 1000);
},
}),
// Labor Day
new HolidayDefinition({
name: "Labour Day",
type: "fixed",
month: 5,
day: 1,
workFree: true,
}),
// Wesak Day
new HolidayDefinition({
name: "Wesak Day",
type: "calculated",
workFree: true,
calculator: (/** @type {number} */ year) => {
// Full moon in May (Buddhist calendar)
return new Date(Date.UTC(year, 4, 15)); // Simplified
},
}),
// Yang di-Pertuan Agong's Birthday
new HolidayDefinition({
name: "Yang di-Pertuan Agong's Birthday",
type: "calculated", // First Saturday in June
workFree: true,
calculator: (/** @type {number} */ year) => {
const jun1 = new Date(Date.UTC(year, 5, 1));
const dayOfWeek = jun1.getUTCDay();
const firstSaturday = dayOfWeek === 6 ? 1 : ((6 - dayOfWeek) % 7) + 1;
return new Date(Date.UTC(year, 5, firstSaturday));
},
}),
// Eid al-Fitr Day 1
new HolidayDefinition({
name: "Hari Raya Aidilfitri",
type: "calculated",
workFree: true,
calculator: (/** @type {number} */ year) => calculateEidAlFitr(year),
}),
// Eid al-Fitr Day 2
new HolidayDefinition({
name: "Hari Raya Aidilfitri (Day 2)",
type: "calculated",
workFree: true,
calculator: (/** @type {number} */ year) => {
const eid = calculateEidAlFitr(year);
return new Date(eid.getTime() + 24 * 60 * 60 * 1000);
},
}),
// Eid al-Adha
new HolidayDefinition({
name: "Hari Raya Aidiladha",
type: "calculated",
workFree: true,
calculator: (/** @type {number} */ year) => calculateEidAlAdha(year),
}),
// National Day
new HolidayDefinition({
name: "National Day",
type: "fixed",
month: 8,
day: 31,
workFree: true,
}),
// Malaysia Day
new HolidayDefinition({
name: "Malaysia Day",
type: "fixed",
month: 9,
day: 16,
workFree: true,
}),
// Deepavali
new HolidayDefinition({
name: "Deepavali",
type: "calculated",
workFree: true,
calculator: (/** @type {number} */ year) => calculateDeepavali(year),
}),
// Christmas Day
new HolidayDefinition({
name: "Christmas Day",
type: "fixed",
month: 12,
day: 25,
workFree: true,
}),
];
// State-specific holidays
const REGIONAL_HOLIDAYS = {
// Johor
JHR: [
new HolidayDefinition({
name: "Sultan of Johor's Birthday",
type: "fixed",
month: 3,
day: 23,
workFree: true,
}),
new HolidayDefinition({
name: "Thaipusam",
type: "calculated",
workFree: true,
calculator: (/** @type {number} */ year) =>
new Date(Date.UTC(year, 0, 25)), // Simplified
}),
],
// Kedah
KDH: [
new HolidayDefinition({
name: "Sultan of Kedah's Birthday",
type: "fixed",
month: 6,
day: 15,
workFree: true,
}),
],
// Kelantan
KTN: [
new HolidayDefinition({
name: "Sultan of Kelantan's Birthday",
type: "fixed",
month: 9,
day: 29,
workFree: true,
}),
new HolidayDefinition({
name: "Kelantan Day",
type: "fixed",
month: 8,
day: 19,
workFree: true,
}),
],
// Malacca
MLK: [
new HolidayDefinition({
name: "Declaration of Malacca as Historical City",
type: "fixed",
month: 4,
day: 15,
workFree: true,
}),
],
// Negeri Sembilan
NSN: [
new HolidayDefinition({
name: "Yang di-Pertuan Besar of Negeri Sembilan's Birthday",
type: "fixed",
month: 1,
day: 14,
workFree: true,
}),
],
// Pahang
PHG: [
new HolidayDefinition({
name: "Sultan of Pahang's Birthday",
type: "fixed",
month: 10,
day: 24,
workFree: true,
}),
],
// Penang
PNG: [
new HolidayDefinition({
name: "Penang Governor's Birthday",
type: "calculated", // Second Saturday in July
workFree: true,
calculator: (/** @type {number} */ year) => {
const jul1 = new Date(Date.UTC(year, 6, 1));
const dayOfWeek = jul1.getUTCDay();
const firstSaturday = dayOfWeek === 6 ? 1 : ((6 - dayOfWeek) % 7) + 1;
return new Date(Date.UTC(year, 6, firstSaturday + 7));
},
}),
new HolidayDefinition({
name: "Thaipusam",
type: "calculated",
workFree: true,
calculator: (/** @type {number} */ year) =>
new Date(Date.UTC(year, 0, 25)), // Simplified
}),
],
// Perak
PRK: [
new HolidayDefinition({
name: "Sultan of Perak's Birthday",
type: "fixed",
month: 11,
day: 1,
workFree: true,
}),
],
// Perlis
PLS: [
new HolidayDefinition({
name: "Raja of Perlis's Birthday",
type: "fixed",
month: 5,
day: 17,
workFree: true,
}),
],
// Sabah
SBH: [
new HolidayDefinition({
name: "Head of State of Sabah's Birthday",
type: "calculated", // First Saturday in October
workFree: true,
calculator: (/** @type {number} */ year) => {
const oct1 = new Date(Date.UTC(year, 9, 1));
const dayOfWeek = oct1.getUTCDay();
const firstSaturday = dayOfWeek === 6 ? 1 : ((6 - dayOfWeek) % 7) + 1;
return new Date(Date.UTC(year, 9, firstSaturday));
},
}),
new HolidayDefinition({
name: "Good Friday",
type: "easter_relative",
offset: -2,
workFree: true,
}),
],
// Sarawak
SWK: [
new HolidayDefinition({
name: "Sarawak Day",
type: "fixed",
month: 7,
day: 22,
workFree: true,
}),
new HolidayDefinition({
name: "Good Friday",
type: "easter_relative",
offset: -2,
workFree: true,
}),
],
// Selangor
SGR: [
new HolidayDefinition({
name: "Sultan of Selangor's Birthday",
type: "fixed",
month: 12,
day: 11,
workFree: true,
}),
new HolidayDefinition({
name: "Thaipusam",
type: "calculated",
workFree: true,
calculator: (/** @type {number} */ year) =>
new Date(Date.UTC(year, 0, 25)), // Simplified
}),
],
// Terengganu
TRG: [
new HolidayDefinition({
name: "Sultan of Terengganu's Birthday",
type: "fixed",
month: 3,
day: 4,
workFree: true,
}),
new HolidayDefinition({
name: "Terengganu Day",
type: "fixed",
month: 8,
day: 21,
workFree: true,
}),
],
};
// Freeze holiday arrays to prevent modification
Object.freeze(NATIONAL_HOLIDAYS);
for (const holidays of Object.values(REGIONAL_HOLIDAYS)) {
Object.freeze(holidays);
}
Object.freeze(REGIONAL_HOLIDAYS);
export const MY = {
national: NATIONAL_HOLIDAYS,
regional: REGIONAL_HOLIDAYS,
};
Object.freeze(MY);