| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- // 农历(阴历)与节日计算:公历转农历采用 1900-2100 查表法,无需外部依赖。
- // 导出 getCalendarInfo(date):返回 { lunarText, festival },供 footer 显示用。
- // 农历数据表:每项表示 1900..2100 年当年的农历信息(闰月月份、大小月、闰月天数)
- const LUNAR_INFO = [
- 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
- 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
- 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
- 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
- 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
- 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0,
- 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,
- 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,
- 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,
- 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
- 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,
- 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
- 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,
- 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
- 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,
- 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0,
- 0x092e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4,
- 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0,
- 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160,
- 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252,
- 0x0d520,
- ];
- // 农历月份名(正月..腊月)
- const LUNAR_MONTHS = ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊'];
- // 农历日名(初一..三十)
- const LUNAR_DAYS = [
- '初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十',
- '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十',
- '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十',
- ];
- // 农历节日:(月, 日) -> 名称
- const LUNAR_FESTIVALS = {
- '1,1': '春节',
- '1,15': '元宵节',
- '2,2': '龙抬头',
- '5,5': '端午节',
- '7,7': '七夕',
- '7,15': '中元节',
- '8,15': '中秋节',
- '9,9': '重阳节',
- '12,8': '腊八节',
- '12,23': '北方小年',
- '12,24': '南方小年',
- };
- // 公历节日:(月, 日) -> 名称
- const SOLAR_FESTIVALS = {
- '1,1': '元旦',
- '2,14': '情人节',
- '3,8': '妇女节',
- '3,12': '植树节',
- '4,1': '愚人节',
- '5,1': '劳动节',
- '5,4': '青年节',
- '6,1': '儿童节',
- '7,1': '建党节',
- '8,1': '建军节',
- '9,10': '教师节',
- '10,1': '国庆节',
- '12,24': '平安夜',
- '12,25': '圣诞节',
- };
- // 农历某年是否闰年
- function leapMonth(y) { return LUNAR_INFO[y - 1900] & 0xf; }
- // 农历某年闰月天数(无闰月返回 0)
- function leapDays(y) { return leapMonth(y) ? ((LUNAR_INFO[y - 1900] & 0x10000) ? 30 : 29) : 0; }
- // 农历某年某月天数(m 为 1..12)
- function monthDays(y, m) { return (LUNAR_INFO[y - 1900] & (0x10000 >> m)) ? 30 : 29; }
- // 农历某年总天数
- function lYearDays(y) {
- let sum = 348;
- for (let i = 0x8000; i > 0x8; i >>= 1) sum += (LUNAR_INFO[y - 1900] & i) ? 1 : 0;
- return sum + leapDays(y);
- }
- // 公历 Date -> 农历 { year, month, day, isLeap }
- function solar2lunar(date) {
- // 用 UTC 构造纯日期,消除时分秒与本地时区对天数差的影响
- const base = Date.UTC(1900, 0, 31); // 1900-01-31 为农历庚子年正月初一
- const target = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());
- let offset = Math.round((target - base) / 86400000);
- let temp = 0;
- let year = 1900;
- for (; year < 2101 && offset > 0; year++) {
- temp = lYearDays(year);
- offset -= temp;
- }
- if (offset < 0) { offset += temp; year--; }
- const leap = leapMonth(year);
- let isLeap = false;
- let month;
- for (month = 1; month < 13 && offset > 0; month++) {
- if (leap > 0 && month === (leap + 1) && !isLeap) {
- --month;
- isLeap = true;
- temp = leapDays(year);
- } else {
- temp = monthDays(year, month);
- }
- if (isLeap && month === (leap + 1)) isLeap = false;
- offset -= temp;
- }
- if (offset === 0 && leap > 0 && month === leap + 1) {
- if (isLeap) { isLeap = false; } else { isLeap = true; --month; }
- }
- if (offset < 0) { offset += temp; --month; }
- return { year, month, day: offset + 1, isLeap };
- }
- // 返回该月最后一天(用于判断除夕)
- function lastDayOfMonth(y, m) { return monthDays(y, m); }
- // 计算某公历日期对应的农历文字与节日
- // date: Date 对象;返回 { lunarText, festival }
- export function getCalendarInfo(date) {
- const y = date.getFullYear();
- const m = date.getMonth() + 1; // 公历 1..12
- const d = date.getDate();
- const lunar = solar2lunar(date);
- // 农历文字:如「九月廿二」,闰月加「闰」前缀
- const lunarText = (lunar.isLeap ? '闰' : '') + LUNAR_MONTHS[lunar.month - 1] + '月' + LUNAR_DAYS[lunar.day - 1];
- // 节日:先查农历节日,其次公历节日,均无则空
- let festival = '';
- const lkey = `${lunar.month},${lunar.day}`;
- if (LUNAR_FESTIVALS[lkey]) {
- festival = LUNAR_FESTIVALS[lkey];
- } else if (lunar.month === 12 && lunar.day === lastDayOfMonth(lunar.year, 12)) {
- festival = '除夕'; // 腊月最后一天
- } else {
- festival = SOLAR_FESTIVALS[`${m},${d}`] || '';
- }
- return { lunarText, festival };
- }
|