feat(sri-lankan-holidays): add comprehensive holiday management for Sri Lanka
- 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.
This commit is contained in:
221
worklenz-backend/src/services/sri-lankan-holiday-service.ts
Normal file
221
worklenz-backend/src/services/sri-lankan-holiday-service.ts
Normal file
@@ -0,0 +1,221 @@
|
||||
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;`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user