Files
Dorm-Air-Conditioner-Smart-…/LunarCalendarAndHolidayJudge.h
2025-09-20 23:24:30 +08:00

463 lines
16 KiB
C++
Raw Permalink 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>
struct LunarDate {
int year;
int month;
int day;
bool isLeapMonth;
};
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 uint32_t calcSolarDateInterval(int year1, int month1, int day1, int year2, int month2, int day2);
static bool hasLeapMonth(int solarYear, int& leapMonth, bool& leapIs30Days);
static int getLunarMonthDays(int solarYear, int lunarMonth);
static bool calcLunarFromInterval(uint32_t dateInterval, LunarDate& lunar);
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天)
// 数据来源CSDN Tyrion.Mon经过验证的标准农历数据
const uint32_t LunarCalendar::lunarInfo[] = {
0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, //1900-1909
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, //1910-1919
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, //1920-1929
0x06566, 0x0d4a0, 0x0ea50, 0x16a95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, //1930-1939
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, //1940-1949
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, //1950-1959
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, //1960-1969
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, //1970-1979
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, //1980-1989
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, //1990-1999
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, //2000-2009
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, //2010-2019
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, //2020-2029
0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, //2030-2039
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, //2040-2049
0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06aa0, 0x1a6c4, 0x0aae0, //2050-2059
0x092e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, //2060-2069
0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, //2070-2079
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, //2080-2089
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, //2090-2099
0x0d520 //2100
};
// 农历月份名称
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;
}
}
// 获取公历日期在当年的天数
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;
}
// 计算公历年份是否为闰年
bool LunarCalendar::isLeapYear(int year) {
return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}
// 计算两个公历日期之间的天数间隔
uint32_t LunarCalendar::calcSolarDateInterval(int year1, int month1, int day1, int year2, int month2, int day2) {
uint8_t monthDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
uint32_t totalDays1 = 0;
uint32_t totalDays2 = 0;
uint32_t totalDays = 0;
// 计算第一个日期从年初到该日期的天数
monthDays[1] = (isLeapYear(year1) ? 29 : 28);
for (int i = 1; i < month1; i++) {
totalDays1 += monthDays[i - 1];
}
totalDays1 += day1;
// 计算第二个日期从年初到该日期的天数
monthDays[1] = (isLeapYear(year2) ? 29 : 28);
for (int i = 1; i < month2; i++) {
totalDays2 += monthDays[i - 1];
}
totalDays2 += day2;
// 如果是同一年
if (year1 == year2) {
totalDays = totalDays2 - totalDays1;
} else {
// 计算两年之间的天数
for (int i = year1 + 1; i < year2; i++) {
totalDays += (isLeapYear(i) ? 366 : 365);
}
totalDays += ((isLeapYear(year1) ? 366 : 365) - totalDays1 + totalDays2);
}
return totalDays;
}
// 计算某农历年是否有闰月
bool LunarCalendar::hasLeapMonth(int solarYear, int& leapMonth, bool& leapIs30Days) {
if (solarYear < 1900 || solarYear > 2100) return false;
uint32_t info = lunarInfo[solarYear - 1900];
leapMonth = info & 0x0000000f; // 低4位表示闰月月份
if (leapMonth == 0 || leapMonth > 12) {
return false;
}
leapIs30Days = (info >> 16) & 0x00000001; // bit16表示闰月大小
return true;
}
// 计算某农历月的天数
int LunarCalendar::getLunarMonthDays(int solarYear, int lunarMonth) {
if (solarYear < 1900 || solarYear > 2100) return -1;
if (lunarMonth < 1 || lunarMonth > 12) return -1;
uint32_t info = lunarInfo[solarYear - 1900];
// 农历1月对应bit152月对应bit14...12月对应bit4
if ((info >> ((12 - lunarMonth) + 4)) & 0x00000001) {
return 30;
} else {
return 29;
}
}
// 根据天数间隔计算农历日期
bool LunarCalendar::calcLunarFromInterval(uint32_t dateInterval, LunarDate& lunar) {
int solarYear = 1900;
uint32_t tempInterval = 0;
// 初始化农历日期 (1900年正月初一)
lunar.year = 1900;
lunar.month = 1;
lunar.day = 1;
lunar.isLeapMonth = false;
while (true) {
int leapMonth = 0;
bool leapIs30Days = false;
bool hasLeap = hasLeapMonth(solarYear, leapMonth, leapIs30Days);
// 如果当前月是闰月
if (lunar.isLeapMonth) {
tempInterval += leapIs30Days ? 30 : 29;
} else {
// 当前月是正常月
tempInterval += getLunarMonthDays(solarYear, lunar.month);
}
// 如果总天数已达到目标
if (tempInterval >= dateInterval) {
if (lunar.isLeapMonth) {
lunar.day = (leapIs30Days ? 30 : 29) - (tempInterval - dateInterval);
} else {
lunar.day = getLunarMonthDays(solarYear, lunar.month) - (tempInterval - dateInterval);
}
return true;
}
// 处理月份递增
if (hasLeap && !lunar.isLeapMonth && leapMonth == lunar.month) {
// 进入闰月
lunar.isLeapMonth = true;
} else {
// 进入下一个月
lunar.isLeapMonth = false;
lunar.month++;
// 年份递增
if (lunar.month > 12) {
solarYear++;
if (solarYear > 2100) return false;
lunar.year = solarYear;
lunar.month = 1;
}
}
}
}
// 核心转换算法 - 基于标准农历算法重新实现
void LunarCalendar::calculateLunar(int solarYear, int solarMonth, int solarDay,
int& lunarYear, int& lunarMonth, int& lunarDay, bool& isLeap) {
// 边界检查
if (solarYear < 1900 || solarYear > 2100) {
lunarYear = solarYear;
lunarMonth = 1;
lunarDay = 1;
isLeap = false;
return;
}
// 计算与基准日期(1900年1月31日农历正月初一)的天数差
uint32_t interval = calcSolarDateInterval(1900, 1, 31, solarYear, solarMonth, solarDay) + 1;
// 根据天数间隔计算农历日期
LunarDate result;
if (!calcLunarFromInterval(interval, result)) {
lunarYear = solarYear;
lunarMonth = 1;
lunarDay = 1;
isLeap = false;
return;
}
lunarYear = result.year;
lunarMonth = result.month;
lunarDay = result.day;
isLeap = result.isLeapMonth;
}
// 主要转换函数
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