- Introduced a new SQL migration file to populate Sri Lankan holidays from verified sources for the years 2020-2050. - Implemented holiday data fetching and population logic in AdminCenterController and HolidayController to automatically sync holidays for Sri Lanka. - Added a holiday data provider service to centralize holiday data retrieval and storage. - Created a script for updating Sri Lankan holidays, including generating SQL for new years and validating holiday data. - Developed documentation outlining the annual update process for Sri Lankan holidays, ensuring accurate data management. - Included a JSON file containing detailed holiday information and metadata for Sri Lankan public holidays.
221 lines
7.8 KiB
TypeScript
221 lines
7.8 KiB
TypeScript
import moment from "moment";
|
|
|
|
interface SriLankanHoliday {
|
|
name: string;
|
|
date: string;
|
|
type: "Public" | "Bank" | "Mercantile" | "Poya";
|
|
description: string;
|
|
is_recurring: boolean;
|
|
is_poya: boolean;
|
|
country_code: string;
|
|
color_code: string;
|
|
}
|
|
|
|
export class SriLankanHolidayService {
|
|
private static readonly COUNTRY_CODE = "LK";
|
|
|
|
// Fixed recurring holidays (same date every year)
|
|
private static readonly FIXED_HOLIDAYS = [
|
|
{
|
|
name: "Independence Day",
|
|
month: 2,
|
|
day: 4,
|
|
type: "Public" as const,
|
|
description: "Commemorates the independence of Sri Lanka from British rule in 1948",
|
|
color_code: "#DC143C"
|
|
},
|
|
{
|
|
name: "Sinhala and Tamil New Year Day",
|
|
month: 4,
|
|
day: 13,
|
|
type: "Public" as const,
|
|
description: "Traditional New Year celebrated by Sinhalese and Tamil communities",
|
|
color_code: "#DC143C"
|
|
},
|
|
{
|
|
name: "Day after Sinhala and Tamil New Year",
|
|
month: 4,
|
|
day: 14,
|
|
type: "Public" as const,
|
|
description: "Second day of traditional New Year celebrations",
|
|
color_code: "#DC143C"
|
|
},
|
|
{
|
|
name: "May Day",
|
|
month: 5,
|
|
day: 1,
|
|
type: "Public" as const,
|
|
description: "International Workers' Day",
|
|
color_code: "#DC143C"
|
|
},
|
|
{
|
|
name: "Christmas Day",
|
|
month: 12,
|
|
day: 25,
|
|
type: "Public" as const,
|
|
description: "Christian celebration of the birth of Jesus Christ",
|
|
color_code: "#DC143C"
|
|
}
|
|
];
|
|
|
|
// Poya days names (in order of Buddhist months)
|
|
private static readonly POYA_NAMES = [
|
|
{ name: "Duruthu", description: "Commemorates the first visit of Buddha to Sri Lanka" },
|
|
{ name: "Navam", description: "Commemorates the appointment of Sariputta and Moggallana as Buddha's chief disciples" },
|
|
{ name: "Medin", description: "Commemorates Buddha's first visit to his father's palace after enlightenment" },
|
|
{ name: "Bak", description: "Commemorates Buddha's second visit to Sri Lanka" },
|
|
{ name: "Vesak", description: "Most sacred day for Buddhists - commemorates birth, enlightenment and passing of Buddha" },
|
|
{ name: "Poson", description: "Commemorates the introduction of Buddhism to Sri Lanka by Arahat Mahinda" },
|
|
{ name: "Esala", description: "Commemorates Buddha's first sermon and the arrival of the Sacred Tooth Relic" },
|
|
{ name: "Nikini", description: "Commemorates the first Buddhist council" },
|
|
{ name: "Binara", description: "Commemorates Buddha's visit to heaven to preach to his mother" },
|
|
{ name: "Vap", description: "Marks the end of Buddhist Lent and Buddha's return from heaven" },
|
|
{ name: "Il", description: "Commemorates Buddha's ordination of sixty disciples" },
|
|
{ name: "Unduvap", description: "Commemorates the arrival of Sanghamitta Theri with the Sacred Bo sapling" }
|
|
];
|
|
|
|
/**
|
|
* Calculate Poya days for a given year
|
|
* Note: This is a simplified calculation. For production use, consider using
|
|
* astronomical calculations or an API that provides accurate lunar calendar dates
|
|
*/
|
|
private static calculatePoyaDays(year: number): SriLankanHoliday[] {
|
|
const poyaDays: SriLankanHoliday[] = [];
|
|
|
|
// This is a simplified approach - in reality, you would need astronomical calculations
|
|
// or use a service that provides accurate Buddhist lunar calendar dates
|
|
// For now, we'll use approximate dates based on lunar month cycles
|
|
|
|
// Starting from a known Vesak date (May full moon)
|
|
// and calculating other Poya days based on lunar month intervals
|
|
const baseVesakDate = this.getVesakDate(year);
|
|
|
|
for (let i = 0; i < 12; i++) {
|
|
const monthsFromVesak = i - 4; // Vesak is the 5th month
|
|
const poyaDate = moment(baseVesakDate).add(monthsFromVesak * 29.53, "days"); // Lunar month average
|
|
|
|
// Adjust to the nearest full moon date (would need proper calculation in production)
|
|
const poyaInfo = this.POYA_NAMES[i];
|
|
|
|
poyaDays.push({
|
|
name: `${poyaInfo.name} Full Moon Poya Day`,
|
|
date: poyaDate.format("YYYY-MM-DD"),
|
|
type: "Poya",
|
|
description: poyaInfo.description,
|
|
is_recurring: false,
|
|
is_poya: true,
|
|
country_code: this.COUNTRY_CODE,
|
|
color_code: "#8B4513"
|
|
});
|
|
}
|
|
|
|
return poyaDays;
|
|
}
|
|
|
|
/**
|
|
* Get approximate Vesak date for a year
|
|
* Vesak typically falls on the full moon in May
|
|
*/
|
|
private static getVesakDate(year: number): Date {
|
|
// This is a simplified calculation
|
|
// In production, use astronomical calculations or a reliable API
|
|
const may1 = new Date(year, 4, 1); // May 1st
|
|
const fullMoonDay = 15; // Approximate - would need proper lunar calculation
|
|
return new Date(year, 4, fullMoonDay);
|
|
}
|
|
|
|
/**
|
|
* Get Easter date for a year (Western/Gregorian calendar)
|
|
* Using Computus algorithm
|
|
*/
|
|
private static getEasterDate(year: number): Date {
|
|
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);
|
|
const day = ((h + l - 7 * m + 114) % 31) + 1;
|
|
|
|
return new Date(year, month - 1, day);
|
|
}
|
|
|
|
/**
|
|
* Get all Sri Lankan holidays for a given year
|
|
*/
|
|
public static getHolidaysForYear(year: number): SriLankanHoliday[] {
|
|
const holidays: SriLankanHoliday[] = [];
|
|
|
|
// Add fixed holidays
|
|
for (const holiday of this.FIXED_HOLIDAYS) {
|
|
holidays.push({
|
|
...holiday,
|
|
date: `${year}-${String(holiday.month).padStart(2, "0")}-${String(holiday.day).padStart(2, "0")}`,
|
|
is_recurring: true,
|
|
is_poya: false,
|
|
country_code: this.COUNTRY_CODE
|
|
});
|
|
}
|
|
|
|
// Add Poya days
|
|
const poyaDays = this.calculatePoyaDays(year);
|
|
holidays.push(...poyaDays);
|
|
|
|
// Add Good Friday (2 days before Easter)
|
|
const easter = this.getEasterDate(year);
|
|
const goodFriday = moment(easter).subtract(2, "days");
|
|
holidays.push({
|
|
name: "Good Friday",
|
|
date: goodFriday.format("YYYY-MM-DD"),
|
|
type: "Public",
|
|
description: "Christian commemoration of the crucifixion of Jesus Christ",
|
|
is_recurring: false,
|
|
is_poya: false,
|
|
country_code: this.COUNTRY_CODE,
|
|
color_code: "#DC143C"
|
|
});
|
|
|
|
// Add day after Vesak
|
|
const vesakDay = poyaDays.find(p => p.name.includes("Vesak"));
|
|
if (vesakDay) {
|
|
const dayAfterVesak = moment(vesakDay.date).add(1, "day");
|
|
holidays.push({
|
|
name: "Day after Vesak Full Moon Poya Day",
|
|
date: dayAfterVesak.format("YYYY-MM-DD"),
|
|
type: "Public",
|
|
description: "Additional day for Vesak celebrations",
|
|
is_recurring: false,
|
|
is_poya: false,
|
|
country_code: this.COUNTRY_CODE,
|
|
color_code: "#DC143C"
|
|
});
|
|
}
|
|
|
|
// Note: Eid and Deepavali dates would need to be calculated based on
|
|
// Islamic and Hindu calendars respectively, or fetched from an external source
|
|
|
|
return holidays.sort((a, b) => a.date.localeCompare(b.date));
|
|
}
|
|
|
|
/**
|
|
* Generate SQL insert statements for holidays
|
|
*/
|
|
public static generateSQL(year: number, tableName = "country_holidays"): string {
|
|
const holidays = this.getHolidaysForYear(year);
|
|
const values = holidays.map(holiday => {
|
|
return `('${this.COUNTRY_CODE}', '${holiday.name.replace(/'/g, "''")}', '${holiday.description.replace(/'/g, "''")}', '${holiday.date}', ${holiday.is_recurring})`;
|
|
}).join(",\n ");
|
|
|
|
return `INSERT INTO ${tableName} (country_code, name, description, date, is_recurring)
|
|
VALUES
|
|
${values}
|
|
ON CONFLICT (country_code, name, date) DO NOTHING;`;
|
|
}
|
|
} |