mcp-chrono 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +169 -0
  3. package/build/data/festivals-cn.d.ts +17 -0
  4. package/build/data/festivals-cn.js +67 -0
  5. package/build/data/festivals-cn.js.map +1 -0
  6. package/build/data/festivals-hk.d.ts +46 -0
  7. package/build/data/festivals-hk.js +186 -0
  8. package/build/data/festivals-hk.js.map +1 -0
  9. package/build/data/festivals-us.d.ts +25 -0
  10. package/build/data/festivals-us.js +53 -0
  11. package/build/data/festivals-us.js.map +1 -0
  12. package/build/data/holidays.d.ts +8 -0
  13. package/build/data/holidays.js +119 -0
  14. package/build/data/holidays.js.map +1 -0
  15. package/build/data/timezone-meta.d.ts +12 -0
  16. package/build/data/timezone-meta.js +81 -0
  17. package/build/data/timezone-meta.js.map +1 -0
  18. package/build/index.d.ts +2 -0
  19. package/build/index.js +35 -0
  20. package/build/index.js.map +1 -0
  21. package/build/server.d.ts +2 -0
  22. package/build/server.js +36 -0
  23. package/build/server.js.map +1 -0
  24. package/build/tools/almanac.d.ts +2 -0
  25. package/build/tools/almanac.js +74 -0
  26. package/build/tools/almanac.js.map +1 -0
  27. package/build/tools/business-days.d.ts +2 -0
  28. package/build/tools/business-days.js +129 -0
  29. package/build/tools/business-days.js.map +1 -0
  30. package/build/tools/calendar-convert.d.ts +2 -0
  31. package/build/tools/calendar-convert.js +74 -0
  32. package/build/tools/calendar-convert.js.map +1 -0
  33. package/build/tools/countdown.d.ts +2 -0
  34. package/build/tools/countdown.js +38 -0
  35. package/build/tools/countdown.js.map +1 -0
  36. package/build/tools/current-time.d.ts +2 -0
  37. package/build/tools/current-time.js +45 -0
  38. package/build/tools/current-time.js.map +1 -0
  39. package/build/tools/date-calc.d.ts +2 -0
  40. package/build/tools/date-calc.js +149 -0
  41. package/build/tools/date-calc.js.map +1 -0
  42. package/build/tools/date-diff.d.ts +2 -0
  43. package/build/tools/date-diff.js +45 -0
  44. package/build/tools/date-diff.js.map +1 -0
  45. package/build/tools/festivals.d.ts +2 -0
  46. package/build/tools/festivals.js +94 -0
  47. package/build/tools/festivals.js.map +1 -0
  48. package/build/tools/manage-countdown.d.ts +2 -0
  49. package/build/tools/manage-countdown.js +120 -0
  50. package/build/tools/manage-countdown.js.map +1 -0
  51. package/build/tools/month-info.d.ts +2 -0
  52. package/build/tools/month-info.js +30 -0
  53. package/build/tools/month-info.js.map +1 -0
  54. package/build/tools/parse-timestamp.d.ts +2 -0
  55. package/build/tools/parse-timestamp.js +60 -0
  56. package/build/tools/parse-timestamp.js.map +1 -0
  57. package/build/tools/timezone.d.ts +2 -0
  58. package/build/tools/timezone.js +51 -0
  59. package/build/tools/timezone.js.map +1 -0
  60. package/package.json +50 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 mcp-chrono contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # mcp-chrono
2
+
3
+ MCP server providing time, calendar, timezone, Chinese lunar calendar, almanac, and date utilities for AI agents.
4
+
5
+ ## Quick Start
6
+
7
+ ### Using npx (no installation needed)
8
+
9
+ ```bash
10
+ npx mcp-chrono
11
+ ```
12
+
13
+ ### Global install
14
+
15
+ ```bash
16
+ npm install -g mcp-chrono
17
+ mcp-chrono
18
+ ```
19
+
20
+ ## Usage with MCP Clients
21
+
22
+ ### Claude Desktop
23
+
24
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "chrono": {
30
+ "command": "npx",
31
+ "args": ["-y", "mcp-chrono"]
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ ### Claude Code
38
+
39
+ ```bash
40
+ claude mcp add chrono -- npx -y mcp-chrono
41
+ ```
42
+
43
+ Or with a custom data directory:
44
+
45
+ ```bash
46
+ claude mcp add chrono -- npx -y mcp-chrono --data-dir /path/to/data
47
+ ```
48
+
49
+ ### Cursor
50
+
51
+ Add to `.cursor/mcp.json` in your project root:
52
+
53
+ ```json
54
+ {
55
+ "mcpServers": {
56
+ "chrono": {
57
+ "command": "npx",
58
+ "args": ["-y", "mcp-chrono"]
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ ### Windsurf
65
+
66
+ Add to `~/.codeium/windsurf/mcp_config.json`:
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "chrono": {
72
+ "command": "npx",
73
+ "args": ["-y", "mcp-chrono"]
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ ### Generic MCP client (mcp.json)
80
+
81
+ Any MCP-compatible client can use the standard `mcp.json` format:
82
+
83
+ ```json
84
+ {
85
+ "mcpServers": {
86
+ "chrono": {
87
+ "command": "npx",
88
+ "args": ["-y", "mcp-chrono"],
89
+ "transportType": "stdio"
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ With custom data directory:
96
+
97
+ ```json
98
+ {
99
+ "mcpServers": {
100
+ "chrono": {
101
+ "command": "npx",
102
+ "args": ["-y", "mcp-chrono", "--data-dir", "/path/to/data"],
103
+ "transportType": "stdio"
104
+ }
105
+ }
106
+ }
107
+ ```
108
+
109
+ ## Data Directory
110
+
111
+ Holiday API responses are cached locally to avoid repeated network requests. Default location: `~/.mcp-chrono/`
112
+
113
+ Override with `--data-dir`:
114
+
115
+ ```bash
116
+ npx mcp-chrono --data-dir /tmp/mcp-chrono-data
117
+ ```
118
+
119
+ ## Tools
120
+
121
+ ### Time & Timezone
122
+
123
+ | Tool | Description |
124
+ |------|-------------|
125
+ | `get_current_time` | Get current time in any IANA timezone with detailed components (weekday, week of year, day of year, UTC offset, etc.) |
126
+ | `convert_timezone` | Convert a datetime between two IANA timezones |
127
+ | `list_timezones` | Search IANA timezones with current offsets and Chinese city names |
128
+ | `parse_timestamp` | Parse Unix timestamps (seconds/milliseconds) or ISO 8601 strings into date components |
129
+
130
+ ### Date Calculation
131
+
132
+ | Tool | Description |
133
+ |------|-------------|
134
+ | `calculate_time` | Add/subtract time from a date. Three modes: **gregorian** (standard), **lunar** (Chinese calendar arithmetic), **anchor** (offset from a named festival like 春节 or Thanksgiving) |
135
+ | `date_diff` | Difference between two dates in years, months, days, hours, minutes, and total counts |
136
+ | `countdown` | Countdown from now to a target date with remaining days/hours/minutes/seconds |
137
+ | `calculate_business_days` | Count or add business days, skipping weekends and optionally public holidays (supports CN makeup workdays) |
138
+
139
+ ### Calendar & Festivals
140
+
141
+ | Tool | Description |
142
+ |------|-------------|
143
+ | `convert_calendar` | Convert between Gregorian and Chinese Lunar calendar. Returns Ganzhi (干支), zodiac, solar terms, festivals, constellation |
144
+ | `get_festivals` | List festivals, solar terms, and public holidays within a date range. Filterable by type |
145
+ | `get_month_info` | Month details: day count, first/last weekday, leap year, quarter, week count |
146
+
147
+ ### Chinese Almanac
148
+
149
+ | Tool | Description |
150
+ |------|-------------|
151
+ | `get_almanac` | Chinese almanac (黄历) for a date: 宜 (suitable) / 忌 (avoid) activities, lucky directions (喜神/财神/福神), conflict zodiac, 彭祖百忌, 吉神宜趋, 凶煞宜忌, 天神, 纳音, and more |
152
+
153
+ ### Persistent Countdowns
154
+
155
+ | Tool | Description |
156
+ |------|-------------|
157
+ | `manage_countdown` | CRUD for persistent countdown timers (set / get / list / delete), stored as JSON on disk |
158
+
159
+ ## Supported Regions
160
+
161
+ | Region | Festivals | Public Holiday API |
162
+ |--------|-----------|-------------------|
163
+ | CN | 27 festivals (lunar + solar) | [timor.tech](https://timor.tech) (includes makeup workdays) |
164
+ | US | 10 federal holidays | [date.nager.at](https://date.nager.at) |
165
+ | HK | 17 public holidays (lunar, Easter, Ching Ming, fixed) | [date.nager.at](https://date.nager.at) |
166
+
167
+ ## License
168
+
169
+ MIT
@@ -0,0 +1,17 @@
1
+ import { Solar } from "lunar-typescript";
2
+ export interface ChineseFestival {
3
+ name: string;
4
+ nameEn: string;
5
+ lunarMonth?: number;
6
+ lunarDay?: number;
7
+ solarMonth?: number;
8
+ solarDay?: number;
9
+ type: "lunar" | "solar";
10
+ }
11
+ export declare const CHINESE_FESTIVALS: ChineseFestival[];
12
+ /**
13
+ * Resolve a Chinese festival name to a Solar date for a given year.
14
+ * For lunar festivals, `year` is the lunar year.
15
+ * Returns null if festival not found.
16
+ */
17
+ export declare function resolveFestivalDate(name: string, year: number): Solar | null;
@@ -0,0 +1,67 @@
1
+ import { Lunar, Solar } from "lunar-typescript";
2
+ export const CHINESE_FESTIVALS = [
3
+ { name: "\u6625\u8282", nameEn: "Spring Festival", lunarMonth: 1, lunarDay: 1, type: "lunar" },
4
+ { name: "\u5143\u5bb5\u8282", nameEn: "Lantern Festival", lunarMonth: 1, lunarDay: 15, type: "lunar" },
5
+ { name: "\u9f99\u62ac\u5934", nameEn: "Dragon Head Raising", lunarMonth: 2, lunarDay: 2, type: "lunar" },
6
+ { name: "\u4e0a\u5df3\u8282", nameEn: "Shangsi Festival", lunarMonth: 3, lunarDay: 3, type: "lunar" },
7
+ { name: "\u6e05\u660e\u8282", nameEn: "Qingming Festival", solarMonth: 4, solarDay: 5, type: "solar" },
8
+ { name: "\u7aef\u5348\u8282", nameEn: "Dragon Boat Festival", lunarMonth: 5, lunarDay: 5, type: "lunar" },
9
+ { name: "\u4e03\u5915\u8282", nameEn: "Qixi Festival", lunarMonth: 7, lunarDay: 7, type: "lunar" },
10
+ { name: "\u4e2d\u5143\u8282", nameEn: "Ghost Festival", lunarMonth: 7, lunarDay: 15, type: "lunar" },
11
+ { name: "\u4e2d\u79cb\u8282", nameEn: "Mid-Autumn Festival", lunarMonth: 8, lunarDay: 15, type: "lunar" },
12
+ { name: "\u91cd\u9633\u8282", nameEn: "Double Ninth Festival", lunarMonth: 9, lunarDay: 9, type: "lunar" },
13
+ { name: "\u5bd2\u8863\u8282", nameEn: "Hanyi Festival", lunarMonth: 10, lunarDay: 1, type: "lunar" },
14
+ { name: "\u4e0b\u5143\u8282", nameEn: "Xiayuan Festival", lunarMonth: 10, lunarDay: 15, type: "lunar" },
15
+ { name: "\u51ac\u81f3", nameEn: "Winter Solstice", solarMonth: 12, solarDay: 22, type: "solar" },
16
+ { name: "\u814a\u516b\u8282", nameEn: "Laba Festival", lunarMonth: 12, lunarDay: 8, type: "lunar" },
17
+ { name: "\u5c0f\u5e74", nameEn: "Little New Year", lunarMonth: 12, lunarDay: 23, type: "lunar" },
18
+ { name: "\u9664\u5915", nameEn: "New Year's Eve", lunarMonth: 12, lunarDay: 30, type: "lunar" },
19
+ // Solar holidays
20
+ { name: "\u5143\u65e6", nameEn: "New Year's Day", solarMonth: 1, solarDay: 1, type: "solar" },
21
+ { name: "\u60c5\u4eba\u8282", nameEn: "Valentine's Day", solarMonth: 2, solarDay: 14, type: "solar" },
22
+ { name: "\u5987\u5973\u8282", nameEn: "Women's Day", solarMonth: 3, solarDay: 8, type: "solar" },
23
+ { name: "\u690d\u6811\u8282", nameEn: "Arbor Day", solarMonth: 3, solarDay: 12, type: "solar" },
24
+ { name: "\u52b3\u52a8\u8282", nameEn: "Labour Day", solarMonth: 5, solarDay: 1, type: "solar" },
25
+ { name: "\u9752\u5e74\u8282", nameEn: "Youth Day", solarMonth: 5, solarDay: 4, type: "solar" },
26
+ { name: "\u513f\u7ae5\u8282", nameEn: "Children's Day", solarMonth: 6, solarDay: 1, type: "solar" },
27
+ { name: "\u5efa\u515a\u8282", nameEn: "CPC Founding Day", solarMonth: 7, solarDay: 1, type: "solar" },
28
+ { name: "\u5efa\u519b\u8282", nameEn: "Army Day", solarMonth: 8, solarDay: 1, type: "solar" },
29
+ { name: "\u6559\u5e08\u8282", nameEn: "Teachers' Day", solarMonth: 9, solarDay: 10, type: "solar" },
30
+ { name: "\u56fd\u5e86\u8282", nameEn: "National Day", solarMonth: 10, solarDay: 1, type: "solar" },
31
+ ];
32
+ /**
33
+ * Resolve a Chinese festival name to a Solar date for a given year.
34
+ * For lunar festivals, `year` is the lunar year.
35
+ * Returns null if festival not found.
36
+ */
37
+ export function resolveFestivalDate(name, year) {
38
+ const festival = CHINESE_FESTIVALS.find(f => f.name === name || f.nameEn.toLowerCase() === name.toLowerCase());
39
+ if (!festival)
40
+ return null;
41
+ if (festival.type === "solar" && festival.solarMonth && festival.solarDay) {
42
+ return Solar.fromYmd(year, festival.solarMonth, festival.solarDay);
43
+ }
44
+ if (festival.type === "lunar" && festival.lunarMonth && festival.lunarDay) {
45
+ try {
46
+ // Handle \u9664\u5915 specially - it's the last day of the lunar year
47
+ // lunar-typescript may not have day 30 for some months
48
+ const lunar = Lunar.fromYmd(year, festival.lunarMonth, festival.lunarDay);
49
+ return lunar.getSolar();
50
+ }
51
+ catch {
52
+ // If the lunar date doesn't exist (e.g., no 30th day), try 29th for \u9664\u5915
53
+ if (festival.name === "\u9664\u5915") {
54
+ try {
55
+ const lunar = Lunar.fromYmd(year, 12, 29);
56
+ return lunar.getSolar();
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+ //# sourceMappingURL=festivals-cn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"festivals-cn.js","sourceRoot":"","sources":["../../src/data/festivals-cn.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ export interface HKFestival {
2
+ name: string;
3
+ nameZh: string;
4
+ rule: HKFixedRule | HKLunarRule | HKSpecialRule;
5
+ }
6
+ interface HKFixedRule {
7
+ type: "fixed";
8
+ month: number;
9
+ day: number;
10
+ }
11
+ interface HKLunarRule {
12
+ type: "lunar";
13
+ lunarMonth: number;
14
+ lunarDay: number;
15
+ }
16
+ interface HKSpecialRule {
17
+ type: "special";
18
+ resolve: (year: number) => {
19
+ month: number;
20
+ day: number;
21
+ } | null;
22
+ }
23
+ /**
24
+ * Hong Kong public holidays (法定公众假期).
25
+ * Includes both fixed solar dates and lunar-based holidays.
26
+ */
27
+ export declare const HK_FESTIVALS: HKFestival[];
28
+ /**
29
+ * Resolve an HK festival name to a { month, day } in the Gregorian calendar for a given year.
30
+ * Returns null if not found.
31
+ */
32
+ export declare function resolveHKFestivalDate(name: string, year: number): {
33
+ month: number;
34
+ day: number;
35
+ } | null;
36
+ /**
37
+ * Get all HK public holidays for a given year with their resolved Gregorian dates.
38
+ */
39
+ export declare function getAllHKFestivals(year: number): Array<{
40
+ name: string;
41
+ nameZh: string;
42
+ month: number;
43
+ day: number;
44
+ date: string;
45
+ }>;
46
+ export {};
@@ -0,0 +1,186 @@
1
+ import { Lunar, Solar } from "lunar-typescript";
2
+ /**
3
+ * Hong Kong public holidays (法定公众假期).
4
+ * Includes both fixed solar dates and lunar-based holidays.
5
+ */
6
+ export const HK_FESTIVALS = [
7
+ // Fixed solar holidays
8
+ { name: "New Year's Day", nameZh: "元旦", rule: { type: "fixed", month: 1, day: 1 } },
9
+ { name: "Labour Day", nameZh: "劳动节", rule: { type: "fixed", month: 5, day: 1 } },
10
+ { name: "HKSAR Establishment Day", nameZh: "香港特别行政区成立纪念日", rule: { type: "fixed", month: 7, day: 1 } },
11
+ { name: "National Day", nameZh: "国庆日", rule: { type: "fixed", month: 10, day: 1 } },
12
+ { name: "Christmas Day", nameZh: "圣诞节", rule: { type: "fixed", month: 12, day: 25 } },
13
+ { name: "Boxing Day", nameZh: "圣诞节翌日", rule: { type: "fixed", month: 12, day: 26 } },
14
+ // Lunar-based holidays
15
+ { name: "Lunar New Year's Day", nameZh: "农历年初一", rule: { type: "lunar", lunarMonth: 1, lunarDay: 1 } },
16
+ { name: "Lunar New Year's Day 2", nameZh: "农历年初二", rule: { type: "lunar", lunarMonth: 1, lunarDay: 2 } },
17
+ { name: "Lunar New Year's Day 3", nameZh: "农历年初三", rule: { type: "lunar", lunarMonth: 1, lunarDay: 3 } },
18
+ { name: "Buddha's Birthday", nameZh: "佛诞", rule: { type: "lunar", lunarMonth: 4, lunarDay: 8 } },
19
+ { name: "Tuen Ng Festival", nameZh: "端午节", rule: { type: "lunar", lunarMonth: 5, lunarDay: 5 } },
20
+ { name: "Mid-Autumn Festival", nameZh: "中秋节翌日", rule: { type: "lunar", lunarMonth: 8, lunarDay: 16 } },
21
+ { name: "Chung Yeung Festival", nameZh: "重阳节", rule: { type: "lunar", lunarMonth: 9, lunarDay: 9 } },
22
+ // Special calculation holidays
23
+ {
24
+ name: "Ching Ming Festival",
25
+ nameZh: "清明节",
26
+ rule: {
27
+ type: "special",
28
+ resolve: (year) => {
29
+ // Ching Ming is a solar term, typically April 4 or 5
30
+ // Use lunar-typescript to find the exact date
31
+ const solar4 = Solar.fromYmd(year, 4, 4);
32
+ const lunar4 = solar4.getLunar();
33
+ if (lunar4.getJieQi() === "清明") {
34
+ return { month: 4, day: 4 };
35
+ }
36
+ const solar5 = Solar.fromYmd(year, 4, 5);
37
+ const lunar5 = solar5.getLunar();
38
+ if (lunar5.getJieQi() === "清明") {
39
+ return { month: 4, day: 5 };
40
+ }
41
+ // Fallback: scan April 3-6
42
+ for (let d = 3; d <= 6; d++) {
43
+ const s = Solar.fromYmd(year, 4, d);
44
+ if (s.getLunar().getJieQi() === "清明") {
45
+ return { month: 4, day: d };
46
+ }
47
+ }
48
+ return { month: 4, day: 5 }; // default
49
+ },
50
+ },
51
+ },
52
+ {
53
+ name: "Easter Monday",
54
+ nameZh: "复活节星期一",
55
+ rule: {
56
+ type: "special",
57
+ resolve: (year) => {
58
+ // Computus algorithm for Easter Sunday
59
+ const a = year % 19;
60
+ const b = Math.floor(year / 100);
61
+ const c = year % 100;
62
+ const d = Math.floor(b / 4);
63
+ const e = b % 4;
64
+ const f = Math.floor((b + 8) / 25);
65
+ const g = Math.floor((b - f + 1) / 3);
66
+ const h = (19 * a + b - d - g + 15) % 30;
67
+ const i = Math.floor(c / 4);
68
+ const k = c % 4;
69
+ const l = (32 + 2 * e + 2 * i - h - k) % 7;
70
+ const m = Math.floor((a + 11 * h + 22 * l) / 451);
71
+ const month = Math.floor((h + l - 7 * m + 114) / 31);
72
+ const day = ((h + l - 7 * m + 114) % 31) + 1;
73
+ // Easter Monday = Easter Sunday + 1
74
+ const easterSunday = new Date(year, month - 1, day);
75
+ const easterMonday = new Date(easterSunday);
76
+ easterMonday.setDate(easterMonday.getDate() + 1);
77
+ return { month: easterMonday.getMonth() + 1, day: easterMonday.getDate() };
78
+ },
79
+ },
80
+ },
81
+ {
82
+ name: "Good Friday",
83
+ nameZh: "耶稣受难节",
84
+ rule: {
85
+ type: "special",
86
+ resolve: (year) => {
87
+ const a = year % 19;
88
+ const b = Math.floor(year / 100);
89
+ const c = year % 100;
90
+ const d = Math.floor(b / 4);
91
+ const e = b % 4;
92
+ const f = Math.floor((b + 8) / 25);
93
+ const g = Math.floor((b - f + 1) / 3);
94
+ const h = (19 * a + b - d - g + 15) % 30;
95
+ const i = Math.floor(c / 4);
96
+ const k = c % 4;
97
+ const l = (32 + 2 * e + 2 * i - h - k) % 7;
98
+ const m = Math.floor((a + 11 * h + 22 * l) / 451);
99
+ const month = Math.floor((h + l - 7 * m + 114) / 31);
100
+ const day = ((h + l - 7 * m + 114) % 31) + 1;
101
+ // Good Friday = Easter Sunday - 2
102
+ const easterSunday = new Date(year, month - 1, day);
103
+ const goodFriday = new Date(easterSunday);
104
+ goodFriday.setDate(goodFriday.getDate() - 2);
105
+ return { month: goodFriday.getMonth() + 1, day: goodFriday.getDate() };
106
+ },
107
+ },
108
+ },
109
+ {
110
+ name: "Day after Good Friday",
111
+ nameZh: "耶稣受难节翌日",
112
+ rule: {
113
+ type: "special",
114
+ resolve: (year) => {
115
+ const a = year % 19;
116
+ const b = Math.floor(year / 100);
117
+ const c = year % 100;
118
+ const d = Math.floor(b / 4);
119
+ const e = b % 4;
120
+ const f = Math.floor((b + 8) / 25);
121
+ const g = Math.floor((b - f + 1) / 3);
122
+ const h = (19 * a + b - d - g + 15) % 30;
123
+ const i = Math.floor(c / 4);
124
+ const k = c % 4;
125
+ const l = (32 + 2 * e + 2 * i - h - k) % 7;
126
+ const m = Math.floor((a + 11 * h + 22 * l) / 451);
127
+ const month = Math.floor((h + l - 7 * m + 114) / 31);
128
+ const day = ((h + l - 7 * m + 114) % 31) + 1;
129
+ const easterSunday = new Date(year, month - 1, day);
130
+ const sat = new Date(easterSunday);
131
+ sat.setDate(sat.getDate() - 1);
132
+ return { month: sat.getMonth() + 1, day: sat.getDate() };
133
+ },
134
+ },
135
+ },
136
+ ];
137
+ /**
138
+ * Resolve an HK festival name to a { month, day } in the Gregorian calendar for a given year.
139
+ * Returns null if not found.
140
+ */
141
+ export function resolveHKFestivalDate(name, year) {
142
+ const festival = HK_FESTIVALS.find(f => f.name.toLowerCase() === name.toLowerCase() || f.nameZh === name);
143
+ if (!festival)
144
+ return null;
145
+ const rule = festival.rule;
146
+ if (rule.type === "fixed") {
147
+ return { month: rule.month, day: rule.day };
148
+ }
149
+ if (rule.type === "lunar") {
150
+ try {
151
+ const lunar = Lunar.fromYmd(year, rule.lunarMonth, rule.lunarDay);
152
+ const solar = lunar.getSolar();
153
+ return { month: solar.getMonth(), day: solar.getDay() };
154
+ }
155
+ catch {
156
+ return null;
157
+ }
158
+ }
159
+ if (rule.type === "special") {
160
+ return rule.resolve(year);
161
+ }
162
+ return null;
163
+ }
164
+ /**
165
+ * Get all HK public holidays for a given year with their resolved Gregorian dates.
166
+ */
167
+ export function getAllHKFestivals(year) {
168
+ const results = [];
169
+ for (const festival of HK_FESTIVALS) {
170
+ const resolved = resolveHKFestivalDate(festival.name, year);
171
+ if (resolved) {
172
+ const m = String(resolved.month).padStart(2, "0");
173
+ const d = String(resolved.day).padStart(2, "0");
174
+ results.push({
175
+ name: festival.name,
176
+ nameZh: festival.nameZh,
177
+ month: resolved.month,
178
+ day: resolved.day,
179
+ date: `${year}-${m}-${d}`,
180
+ });
181
+ }
182
+ }
183
+ results.sort((a, b) => a.date.localeCompare(b.date));
184
+ return results;
185
+ }
186
+ //# sourceMappingURL=festivals-hk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"festivals-hk.js","sourceRoot":"","sources":["../../src/data/festivals-hk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export interface USFestival {
2
+ name: string;
3
+ rule: FixedDateRule | NthWeekdayRule;
4
+ }
5
+ interface FixedDateRule {
6
+ type: "fixed";
7
+ month: number;
8
+ day: number;
9
+ }
10
+ interface NthWeekdayRule {
11
+ type: "nth_weekday";
12
+ month: number;
13
+ weekday: number;
14
+ n: number;
15
+ }
16
+ export declare const US_FESTIVALS: USFestival[];
17
+ /**
18
+ * Resolve a US festival name to a { month, day } for a given year.
19
+ * Returns null if not found.
20
+ */
21
+ export declare function resolveUSFestivalDate(name: string, year: number): {
22
+ month: number;
23
+ day: number;
24
+ } | null;
25
+ export {};
@@ -0,0 +1,53 @@
1
+ export const US_FESTIVALS = [
2
+ { name: "New Year's Day", rule: { type: "fixed", month: 1, day: 1 } },
3
+ { name: "Martin Luther King Jr. Day", rule: { type: "nth_weekday", month: 1, weekday: 1, n: 3 } },
4
+ { name: "Presidents' Day", rule: { type: "nth_weekday", month: 2, weekday: 1, n: 3 } },
5
+ { name: "Memorial Day", rule: { type: "nth_weekday", month: 5, weekday: 1, n: -1 } },
6
+ { name: "Independence Day", rule: { type: "fixed", month: 7, day: 4 } },
7
+ { name: "Labor Day", rule: { type: "nth_weekday", month: 9, weekday: 1, n: 1 } },
8
+ { name: "Columbus Day", rule: { type: "nth_weekday", month: 10, weekday: 1, n: 2 } },
9
+ { name: "Veterans Day", rule: { type: "fixed", month: 11, day: 11 } },
10
+ { name: "Thanksgiving", rule: { type: "nth_weekday", month: 11, weekday: 4, n: 4 } },
11
+ { name: "Christmas", rule: { type: "fixed", month: 12, day: 25 } },
12
+ ];
13
+ /**
14
+ * Resolve a US festival name to a { month, day } for a given year.
15
+ * Returns null if not found.
16
+ */
17
+ export function resolveUSFestivalDate(name, year) {
18
+ const festival = US_FESTIVALS.find(f => f.name.toLowerCase() === name.toLowerCase());
19
+ if (!festival)
20
+ return null;
21
+ const rule = festival.rule;
22
+ if (rule.type === "fixed") {
23
+ return { month: rule.month, day: rule.day };
24
+ }
25
+ // nth_weekday
26
+ if (rule.n === -1) {
27
+ // Last occurrence of weekday in month
28
+ // Start from last day of month and go backward
29
+ const lastDay = new Date(year, rule.month, 0).getDate();
30
+ for (let d = lastDay; d >= 1; d--) {
31
+ const dt = new Date(year, rule.month - 1, d);
32
+ if (dt.getDay() === rule.weekday) {
33
+ return { month: rule.month, day: d };
34
+ }
35
+ }
36
+ }
37
+ else {
38
+ // Nth occurrence
39
+ let count = 0;
40
+ const daysInMonth = new Date(year, rule.month, 0).getDate();
41
+ for (let d = 1; d <= daysInMonth; d++) {
42
+ const dt = new Date(year, rule.month - 1, d);
43
+ if (dt.getDay() === rule.weekday) {
44
+ count++;
45
+ if (count === rule.n) {
46
+ return { month: rule.month, day: d };
47
+ }
48
+ }
49
+ }
50
+ }
51
+ return null;
52
+ }
53
+ //# sourceMappingURL=festivals-us.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"festivals-us.js","sourceRoot":"","sources":["../../src/data/festivals-us.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface HolidayInfo {
2
+ date: string;
3
+ name: string;
4
+ isOffDay: boolean;
5
+ type: "public_holiday" | "makeup_workday" | "regular";
6
+ }
7
+ export declare function getHolidays(dataDir: string, country: string, year: number): Promise<Map<string, HolidayInfo>>;
8
+ export declare function getHolidayInfoSync(dataDir: string, country: string, year: number, date: string): HolidayInfo | undefined;