build-ics.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import fs from "fs";
  2. import ical, {
  3. ICalEventClass,
  4. ICalEventStatus,
  5. ICalEventTransparency,
  6. } from "ical-generator";
  7. import generate from "../src/holidays/generate";
  8. import dayjs, { Dayjs } from "../src/utils/dayjs";
  9. import { Holiday } from "../src/holidays/arrangement";
  10. const { holidays, workdays, inLieuDays } = generate();
  11. const endYear = Number(Object.keys(holidays)[0].slice(0, 4))
  12. // 创建一个新的日历
  13. const cal = ical({
  14. name: "中国节假日",
  15. timezone: "Asia/Shanghai",
  16. prodId: { company: "yaavi.me", product: "Chinese Days", language: "CN" },
  17. });
  18. // 设置日历描述
  19. cal.description(`${endYear - 2}~${endYear}年中国节假日日历`);
  20. // 添加时区信息
  21. cal.timezone({
  22. name: "Asia/Shanghai",
  23. generator: (tzid) => `
  24. BEGIN:VTIMEZONE
  25. TZID:${tzid}
  26. X-LIC-LOCATION:${tzid}
  27. BEGIN:STANDARD
  28. TZOFFSETFROM:+0800
  29. TZOFFSETTO:+0800
  30. TZNAME:CST
  31. DTSTART:19700101T000000
  32. END:STANDARD
  33. END:VTIMEZONE`,
  34. });
  35. const buildHolidays = (
  36. year: number,
  37. days: Record<string, Holiday>,
  38. mark: "(休)" | "(班)" | string
  39. ) => {
  40. // 合并相同节日的日期
  41. const mergedHolidays: Record<
  42. string,
  43. {
  44. chineseName: string;
  45. dates: string[];
  46. }
  47. > = {};
  48. for (const [date, info] of Object.entries(days)) {
  49. if (date.startsWith(String(year))) {
  50. const [name, chineseName] = info.split(",");
  51. if (!mergedHolidays[name]) {
  52. mergedHolidays[name] = {
  53. chineseName,
  54. dates: [],
  55. };
  56. }
  57. mergedHolidays[name].dates.push(date);
  58. }
  59. }
  60. // 检查日期是否连续的函数
  61. const areDatesContinuous = (date1: Dayjs, date2: Dayjs) => {
  62. return dayjs(date2).diff(dayjs(date1), "day") === 1;
  63. };
  64. for (const [name, details] of Object.entries(mergedHolidays)) {
  65. const { chineseName, dates } = details;
  66. dates.sort(); // 确保日期按顺序排列
  67. let startDate = dayjs(dates[0]);
  68. let endDate = startDate;
  69. for (let i = 1; i < dates.length; i++) {
  70. const currentDate = dayjs(dates[i]);
  71. if (areDatesContinuous(endDate, currentDate)) {
  72. endDate = currentDate;
  73. } else {
  74. // 添加当前事件
  75. cal.createEvent({
  76. start: startDate.toDate(),
  77. end: endDate.add(1, "day").toDate(),
  78. description: chineseName,
  79. status: ICalEventStatus.CONFIRMED,
  80. summary: `${chineseName}${mark}`,
  81. transparency: ICalEventTransparency.TRANSPARENT,
  82. allDay: true,
  83. class: ICalEventClass.PUBLIC,
  84. });
  85. // 重置开始和结束日期
  86. startDate = currentDate;
  87. endDate = currentDate;
  88. }
  89. }
  90. // 添加最后一个事件
  91. cal.createEvent({
  92. start: startDate.toDate(),
  93. end: endDate.add(1, "day").toDate(),
  94. description: chineseName,
  95. status: ICalEventStatus.CONFIRMED,
  96. summary: `${chineseName}${mark}`,
  97. transparency: ICalEventTransparency.TRANSPARENT,
  98. allDay: true,
  99. class: ICalEventClass.PUBLIC,
  100. });
  101. }
  102. };
  103. for (let i = endYear; i > endYear - 3; i--) {
  104. buildHolidays(i, holidays, "(休)");
  105. buildHolidays(i, workdays, "(班)");
  106. }
  107. // 将日历保存到 ./dist/holidays.ics 文件
  108. fs.writeFile("./dist/holidays.ics", cal.toString(), "utf8", (err) => {
  109. if (err) throw err;
  110. console.log("The ICS file has been saved!");
  111. });