370 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef LUNAR_CALENDAR_H
#define LUNAR_CALENDAR_H
#include <Arduino.h>
#include <WiFi.h>
class LunarCalendar {
private:
// 农历数据表 (1900-2100年)
static const uint32_t lunarInfo[];
static const char* lunarMonthNames[];
static const char* lunarDayNames[];
static const char* tianGan[];
static const char* diZhi[];
static const char* animals[];
// 辅助函数
static int getDaysInYear(int year);
static int getLeapMonth(int year);
static int getDaysInMonth(int year, int month, bool isLeap = false);
static bool isLeapYear(int year);
static int getDayOfYear(int year, int month, int day);
static void calculateLunar(int solarYear, int solarMonth, int solarDay,
int& lunarYear, int& lunarMonth, int& lunarDay, bool& isLeap);
public:
// 主要转换函数
static String solarToLunar(String solarDate);
static String getLunarYearName(int year);
static String getLunarMonthName(int month, bool isLeap = false);
static String getLunarDayName(int day);
// 节假日判断函数
static bool isHoliday(int year, int month, int day);
static bool isSolarHoliday(int month, int day);
static bool isLunarHoliday(int lunarYear, int lunarMonth, int lunarDay, bool isLeap);
};
// 农历数据表 (1900-2100年每年用32位表示)
// 前4位表示闰月月份后12位表示每月天数(0=29天1=30天)
const uint32_t LunarCalendar::lunarInfo[] = {
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, 0x0b6a0, 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,
0x0a2e0, 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 char* LunarCalendar::lunarMonthNames[] = {
"正月", "二月", "三月", "四月", "五月", "六月",
"七月", "八月", "九月", "十月", "冬月", "腊月"
};
// 农历日期名称
const char* LunarCalendar::lunarDayNames[] = {
"初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十",
"十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
"廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"
};
// 天干
const char* LunarCalendar::tianGan[] = {
"", "", "", "", "", "", "", "", "", ""
};
// 地支
const char* LunarCalendar::diZhi[] = {
"", "", "", "", "", "", "", "", "", "", "", ""
};
// 生肖
const char* LunarCalendar::animals[] = {
"", "", "", "", "", "", "", "", "", "", "", ""
};
// 获取农历年总天数
int LunarCalendar::getDaysInYear(int year) {
// 边界检查
if (year < 1900 || year > 2100) return 365;
int sum = 348;
for (int i = 0x8000; i > 0x8; i >>= 1) {
if ((lunarInfo[year - 1900] & i) != 0) sum += 1;
}
return sum + getDaysInMonth(year, getLeapMonth(year), true);
}
// 获取农历年闰月月份
int LunarCalendar::getLeapMonth(int year) {
// 边界检查
if (year < 1900 || year > 2100) return 0;
return lunarInfo[year - 1900] & 0xf;
}
// 获取农历月天数
int LunarCalendar::getDaysInMonth(int year, int month, bool isLeap) {
// 边界检查
if (year < 1900 || year > 2100 || month < 1 || month > 12) return 29;
if (isLeap && month != getLeapMonth(year)) return 0;
if (isLeap) {
return (lunarInfo[year - 1900] & 0x10000) ? 30 : 29;
} else {
return (lunarInfo[year - 1900] & (0x10000 >> month)) ? 30 : 29;
}
}
// 判断公历年是否为闰年
bool LunarCalendar::isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
// 获取公历日期在当年的天数
int LunarCalendar::getDayOfYear(int year, int month, int day) {
int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (isLeapYear(year)) daysInMonth[1] = 29;
int dayOfYear = day;
for (int i = 0; i < month - 1; i++) {
dayOfYear += daysInMonth[i];
}
return dayOfYear;
}
// 核心转换算法
void LunarCalendar::calculateLunar(int solarYear, int solarMonth, int solarDay,
int& lunarYear, int& lunarMonth, int& lunarDay, bool& isLeap) {
// 1900年1月31日为农历1900年正月初一
int baseYear = 1900;
int baseMonth = 1;
int baseDay = 31;
// 计算距离基准日期的天数
int offset = 0;
// 计算年份差
for (int i = baseYear; i < solarYear; i++) {
offset += isLeapYear(i) ? 366 : 365;
}
// 加上当年已过天数
offset += getDayOfYear(solarYear, solarMonth, solarDay) - getDayOfYear(baseYear, baseMonth, baseDay);
// 计算农历日期
lunarYear = baseYear;
int daysInLunarYear = getDaysInYear(lunarYear);
while (offset >= daysInLunarYear) {
offset -= daysInLunarYear;
lunarYear++;
daysInLunarYear = getDaysInYear(lunarYear);
}
// 计算月份
lunarMonth = 1;
isLeap = false;
int leapMonth = getLeapMonth(lunarYear);
while (offset > 0 && lunarMonth <= 12) {
int daysInCurrentMonth;
if (lunarMonth == leapMonth && !isLeap && leapMonth > 0) {
// 闰月
daysInCurrentMonth = getDaysInMonth(lunarYear, lunarMonth, true);
if (offset >= daysInCurrentMonth) {
offset -= daysInCurrentMonth;
isLeap = true;
} else {
isLeap = true;
break;
}
}
// 正常月份
daysInCurrentMonth = getDaysInMonth(lunarYear, lunarMonth, false);
if (offset >= daysInCurrentMonth) {
offset -= daysInCurrentMonth;
if (isLeap) {
isLeap = false;
} else {
lunarMonth++;
}
} else {
break;
}
}
// 确保月份在有效范围内
if (lunarMonth > 12) lunarMonth = 12;
if (lunarMonth < 1) lunarMonth = 1;
lunarDay = offset + 1;
}
// 主要转换函数
String LunarCalendar::solarToLunar(String solarDate) {
// 解析输入日期 "2025/5/6"
int year, month, day;
int firstSlash = solarDate.indexOf('/');
int secondSlash = solarDate.indexOf('/', firstSlash + 1);
if (firstSlash == -1 || secondSlash == -1) {
return "日期格式错误";
}
year = solarDate.substring(0, firstSlash).toInt();
month = solarDate.substring(firstSlash + 1, secondSlash).toInt();
day = solarDate.substring(secondSlash + 1).toInt();
// 验证日期范围
if (year < 1900 || year > 2100 || month < 1 || month > 12 || day < 1 || day > 31) {
return "日期超出范围(1900-2100)";
}
// 转换为农历
int lunarYear, lunarMonth, lunarDay;
bool isLeap;
calculateLunar(year, month, day, lunarYear, lunarMonth, lunarDay, isLeap);
// 格式化输出
String result = getLunarYearName(lunarYear) + "";
result += getLunarMonthName(lunarMonth, isLeap);
result += getLunarDayName(lunarDay);
return result;
}
// 获取农历年份名称(天干地支+生肖)
String LunarCalendar::getLunarYearName(int year) {
int tianGanIndex = (year - 4) % 10;
int diZhiIndex = (year - 4) % 12;
// 确保索引为正数
if (tianGanIndex < 0) tianGanIndex += 10;
if (diZhiIndex < 0) diZhiIndex += 12;
String yearName = String(tianGan[tianGanIndex]) + String(diZhi[diZhiIndex]);
yearName += "(" + String(animals[diZhiIndex]) + ")";
return yearName;
}
// 获取农历月份名称
String LunarCalendar::getLunarMonthName(int month, bool isLeap) {
String monthName = "";
if (isLeap) {
monthName = "";
}
// 添加边界检查
if (month >= 1 && month <= 12) {
monthName += lunarMonthNames[month - 1];
} else {
monthName += "未知月";
}
return monthName;
}
// 获取农历日期名称
String LunarCalendar::getLunarDayName(int day) {
// 添加边界检查
if (day >= 1 && day <= 30) {
return String(lunarDayNames[day - 1]);
} else {
return "未知日";
}
}
// 节假日数据结构
struct SolarHoliday {
int month;
int day;
const char* name;
};
struct LunarHoliday {
int month;
int day;
const char* name;
};
// 公历法定节假日列表
static const SolarHoliday solarHolidays[] = {
{1, 1, "元旦"},
{3, 8, "妇女节"},
{5, 1, "劳动节"},
{6, 1, "儿童节"},
{10, 1, "国庆节"},
{10, 2, "国庆节"},
{10, 3, "国庆节"}
};
// 农历法定节假日列表
static const LunarHoliday lunarHolidays[] = {
{1, 1, "春节"},
{1, 2, "春节"},
{1, 3, "春节"},
{1, 15, "元宵节"},
{5, 5, "端午节"},
{8, 15, "中秋节"},
{9, 9, "重阳节"},
{12, 30, "除夕"}, // 大月
{12, 29, "除夕"} // 小月
};
// 判断是否为公历节假日
bool LunarCalendar::isSolarHoliday(int month, int day) {
int numSolarHolidays = sizeof(solarHolidays) / sizeof(solarHolidays[0]);
for (int i = 0; i < numSolarHolidays; i++) {
if (solarHolidays[i].month == month && solarHolidays[i].day == day) {
return true;
}
}
return false;
}
// 判断是否为农历节假日
bool LunarCalendar::isLunarHoliday(int lunarYear, int lunarMonth, int lunarDay, bool isLeap) {
// 闰月不算节假日
if (isLeap) return false;
int numLunarHolidays = sizeof(lunarHolidays) / sizeof(lunarHolidays[0]);
for (int i = 0; i < numLunarHolidays; i++) {
if (lunarHolidays[i].month == lunarMonth && lunarHolidays[i].day == lunarDay) {
// 特殊处理除夕:需要判断是否为该年最后一天
if (lunarMonth == 12 && (lunarDay == 29 || lunarDay == 30)) {
int daysInLastMonth = getDaysInMonth(lunarYear, 12, false);
if (lunarDay == daysInLastMonth) {
return true; // 是除夕
}
} else {
return true;
}
}
}
return false;
}
// 主要节假日判断函数
bool LunarCalendar::isHoliday(int year, int month, int day) {
// 首先检查公历节假日
if (isSolarHoliday(month, day)) {
return true;
}
// 检查农历节假日
int lunarYear, lunarMonth, lunarDay;
bool isLeap;
calculateLunar(year, month, day, lunarYear, lunarMonth, lunarDay, isLeap);
return isLunarHoliday(lunarYear, lunarMonth, lunarDay, isLeap);
}
#endif // LUNAR_CALENDAR_H