摘要:市面上的农历订阅大都包含了所有的国家节假日。如果只是想订阅特定的日子(如初一十五),我们可以利用 GitHub 生态打造一套零成本、无人值守的 ICS 日历自动生成方案。
最近我遇到了一个小痛点:为了提醒自己农历的初一和十五吃素,我需要在手机日历里添加提醒。然而,找遍了全网,绝大多数开源的农历日历订阅都会把法定节假日、调休安排一股脑全塞进去。如果你也有类似只关注「特定重复日期」的需求,何不自己动手写一个定制版的订阅源呢?
今天,我将带大家完整复盘一下,我是如何利用 Node.js 和白嫖 GitHub Actions,零成本打造一个每月自动滚动更新未来 13 个月农历数据的 .ics 日历订阅服务的。
一、为什么是 .ics 格式?
无论你是苹果的 iCloud 日历、Google 日历还是微软的 Outlook,它们都支持一种由 RFC 5545 规范定义的全局通用日历格式,那就是 .ics (iCalendar)。
在这个标准里,一个基础的事件(Event)长这样:
BEGIN:VEVENT
UID:[email protected]
DTSTAMP:20260714T120000Z
DTSTART;VALUE=DATE:20260812
SUMMARY:初一 (农历七月初一)
DESCRIPTION:今天吃素
END:VEVENT
我们的核心目标,就是写一个脚本,把想要提醒的农历日期算出来,然后拼装成上述的文本格式,最后提供一个静态 URL 供日历 App 订阅。
二、用 Node.js 计算农历并生成日历文件
要计算农历,我们可以使用开源社区非常成熟的 lunar-javascript 库。它可以轻松地把公历和农历互相转换。
核心脚本 generate-calendar.js 的思路非常清晰:
- 1. 确定生成范围(如当前月份往前 1 个月,往后 12 个月,共计 13 个月,避免日历文件无限膨胀)。
- 2. 遍历这 13 个月里的每一天。
- 3. 通过
Lunar.fromDate()获取农历日,判断是否为初一或十五。 - 4. 组装符合规范的 ICS 字符串并输出。
const fs = require('fs');
const { Lunar } = require('lunar-javascript');
const dayjs = require('dayjs');
// 组装 ICS 头
let icsContent = `BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//HenryYannis//Lunar Vegetarian Calendar//CN
CALSCALE:GREGORIAN
X-WR-CALNAME:农历初一十五(吃素)
X-WR-TIMEZONE:Asia/Shanghai\n`;
// 获取当月的一号
const startMonth = dayjs().startOf('month').subtract(1, 'month');
const endMonth = startMonth.add(13, 'month');
// ... 循环每一天,筛选初一十五并追加 VEVENT ...
icsContent += 'END:VCALENDAR';
fs.writeFileSync('chuyi-shiwu.ics', icsContent);
三、核心大杀器:GitHub Actions 自动化
代码写完了,难点在于:日子是一天天过,日历需要持续往未来更新,难道我要买台服务器用 Cron 跑脚本吗?
完全不需要!我们将整个项目托管在 GitHub 上,并利用 GitHub Actions,通过配置定时任务(Schedule),让微软的服务器每个月免费帮我们跑一次生成脚本,然后再用 git-auto-commit-action 把新生成的 .ics 文件提交并推送到当前仓库。
在仓库的 .github/workflows/update.yml 文件中,配置如下:
name: Update Lunar Calendar
on:
schedule:
# UTC 时间 16:00 相当于北京时间 00:00,每月 1 号执行一次
- cron: '0 16 1 * *'
workflow_dispatch: # 支持在网页端手动点击运行
jobs:
build-and-commit:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Dependencies
run: npm install
- name: Generate ICS File
run: node generate-calendar.js
- name: Commit and Push
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore: update rolling calendar [skip ci]"
file_pattern: "*.ics"
💡 避坑提示
记得在 commit_message 中加入 [skip ci],这可以防止 Actions 提交代码后又触发其他的 CI 流程,造成无限循环。另外,必须在仓库的 Settings -> Actions -> General 底部开启 Read and write permissions,否则 Action 没有权限推送到仓库。
四、最终效果与开源导流
这套组合拳打完之后,你只需将 chuyi-shiwu.ics 文件在 GitHub 里的 Raw 链接复制出来(或者使用 jsDelivr/Cloudflare 等 CDN 加速),即可无缝导入任何日历软件。
只要 GitHub 还在,这套服务就永远不会挂,也永远不花你一分钱的服务器费。
如果你是一名开发者,欢迎 Fork 上方的仓库,修改代码(比如把提醒时间改为提前一天晚上)。如果你不是开发者,只是想直接订阅日历,可以使用以下链接:
1. 简体中文订阅 (推荐)
https://raw.githubusercontent.com/HenryYannis/lunar-vegetarian-calendar/main/chuyi-shiwu-simplified.ics
- 国内 CDN 通用链接 (手动复制):
https://cdn.jsdelivr.net/gh/HenryYannis/lunar-vegetarian-calendar@main/chuyi-shiwu-simplified.ics
2. 繁体中文订阅
https://raw.githubusercontent.com/HenryYannis/lunar-vegetarian-calendar/main/chuyi-shiwu-traditional.ics
- 国内 CDN 通用链接 (手动复制):
https://cdn.jsdelivr.net/gh/HenryYannis/lunar-vegetarian-calendar@main/chuyi-shiwu-traditional.ics
以上就是本期系统教学的全部内容。科技的乐趣,大概就是写一点代码,解决自己生活中的小麻烦吧!
加载评论中……