|
近日对农历计算感兴趣,在网上找了一些源码看了看,发现一个广为流传的源码,好像是汉王的人所写。但是这个源码不但参数表太大,并且运算速度很慢,不适合于用到嵌入式产品中。后来看到一个不错的版本(可能为台湾人所写),于是对它的参数表进行了优化,得到了一个不错的程序,非常适合于嵌入式系统。 参数表所占存储空间为:30 + 4×支持年数 经在MSP430仿真测试,参数表占有154字节,代码占有622字节,执行一次的典型时间是1382个时钟周期。
// ----------------------------------------------------------------- // 农历计算: // Copy From "西历农历转换程式 黄晓鸣 1995,7,25" // Modify : Blove // Date : 16:02 2005-12-1 // ----------------------------------------------------------------- typedef unsigned char INT8U; typedef unsigned short INT16U; typedef unsigned long INT32U;
typedef struct _SolarDate_Tag_ { INT16U wYear; INT8U chMonth; INT8U chDate; }sSolarDateTag;
typedef struct _LunarDate_Tag_ { INT16U wYear; INT8U chMonth; INT8U chDate; INT8U chKan; INT8U chChi; }sLunarDateTag;
// ------------------------------------------------------------------- // 农历计算参数位定义: // 31-25 7 : ( BaseDays )到公历1月1日到农历正月初一的累积日数 // 24-20 5 : ( Intercalation)闰月月份. 0==此年没有闰月 // 19-13 7 : ( BaseKanChih )此年公历1月1日之干支序号减 1 // 12- 0 13 : ( MonthDays )此农历年每月之大小, 0==小月(29日), 1==大月(30日) // ------------------------------------------------------------------- #define LPARA_MASK_BASE_DAYS 0xFE000000 #define LPARA_MASK_INTERCALATION 0x01F00000 #define LPARA_MASK_BASE_KANCHI 0x000FE000 #define LPARA_MASK_MONTH_DAYS 0x00001FFF
#define LPARA_SHIFT_BASE_DAYS 25 #define LPARA_SHIFT_INTERCALATION 20 #define LPARA_SHIFT_BASE_KANCHI 13 #define LPARA_SHIFT_MONTH_DAYS 0
#define LPara_GetBaseDays(i) ( (m_dwLunarParas[i]&LPARA_MASK_BASE_DAYS)>>LPARA_SHIFT_BASE_DAYS ) #define LPara_GetIntercalation(i) ( (m_dwLunarParas[i]&LPARA_MASK_INTERCALATION)>>LPARA_SHIFT_INTERCALATION ) #define LPara_GetBaseKanChi(i) ( (m_dwLunarParas[i]&LPARA_MASK_BASE_KANCHI)>>LPARA_SHIFT_BASE_KANCHI ) #define LPara_GetMonthDays(wYear,chMonth) \ ( (((m_dwLunarParas[wYear]&LPARA_MASK_MONTH_DAYS)>>LPARA_SHIFT_MONTH_DAYS)>>chMonth)&0x01 )
// ----------------------------------------------------------- // 农历计算参数表 // ----------------------------------------------------------- static const INT32U m_dwLunarParas[] = { 0x2E47752B, 0x5400952B, 0x3E012A5B, 0x2A21D55A, 0x4E02956A, // 2005 0x38733B55, 0x6003DBA4, 0x4A047B49, 0x32553A93, 0x5805DA95, 0x4206752D, 0x2C470AAD, 0x50004AAD, 0x3C90F5AA, 0x620195D2, // 2015 0x4C022DA5, 0x3662FD4A, 0x5C038D4A, 0x46042C95, 0x3044D52E, 0x54059556, 0x3E062AB5, 0x2A26D5B2, 0x500776D2, 0x3860AEA5, // 2025 0x5E015725, 0x4801F64B, 0x32528C97, 0x56035CAB, 0x4203E55A, 0x2C348AD6 // 2031 };
#define FIRSTYEAR 2001 // 参数表中的第一年 #define YEAR_NUMS ( sizeof(m_dwLunarParas) / sizeof(INT32U) ) #define LASTYEAR ( FIRSTYEAR + YEAR_NUMS - 1 ) // 参数表中的最后一年
// ----------------------------------------------------------- // 公历年每月天数标记表 // 由高位到低位为12-1月:1表示31天,0表示30天,二月除外 // 月份: 12 11 10 9 8 7 6 5 4 3 2 1 // 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 28, 31 // 1 0 1 0 1 1 0 1 0 1 0 1 // ----------------------------------------------------------- static const INT16U m_wSolarMonthDaysFlag = 0x0AD5;
// ----------------------------------------------------------- // 公历年每月累积天数, 平年与闰年 // ----------------------------------------------------------- static const INT16U m_wSolarDays[14] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, 396 };
// ----------------------------------------------------------- // 求此公历年是否为闰年, 返回 0 为平年, 1 为闰年 // ----------------------------------------------------------- BOOL Lunar_IsIntercalation( INT16U wYear ) { if ( wYear % 400 == 0 ) return 1; else if ( wYear % 100 == 0 ) return 0; else if ( wYear % 4 == 0 ) return 1; else return 0; }
// ----------------------------------------------------------- // 农历计算 // ----------------------------------------------------------- int Lunar_Cal( sSolarDateTag *psSolarDate, sLunarDateTag *psLunarDate ) { INT16U wSolarMonth, wDaySum, wSolarDaysSum, im, wTmp1, wTmp2, i, wKanChiSum; BOOL bLeapFlag; INT8U chMonthDays, chYearIndex;
// 输入参数的有效性检查 if ( psSolarDate->wYear<=FIRSTYEAR || psSolarDate->wYear>LASTYEAR ) return 1;
wSolarMonth = psSolarDate->chMonth - 1; if ( wSolarMonth > 11 ) return 2;
bLeapFlag = Lunar_IsIntercalation( psSolarDate->wYear ); if ( wSolarMonth == 1) // 二月 { chMonthDays = bLeapFlag + 28; } else // 其他月份 { chMonthDays = 30 + ((m_wSolarMonthDaysFlag>>wSolarMonth)&0x01); }
if ( psSolarDate->chDate<1 || psSolarDate->chDate>chMonthDays ) return 3;
chYearIndex = psSolarDate->wYear - FIRSTYEAR;
wSolarDaysSum = m_wSolarDays[wSolarMonth]; if ( wSolarMonth > 1 ) wSolarDaysSum += bLeapFlag; wDaySum = wSolarDaysSum + psSolarDate->chDate;
wKanChiSum = wDaySum + LPara_GetBaseKanChi(chYearIndex); psLunarDate->chKan = wKanChiSum % 10; psLunarDate->chChi = wKanChiSum % 12;
if ( wDaySum <= LPara_GetBaseDays(chYearIndex) ) { chYearIndex--; psLunarDate->wYear = psSolarDate->wYear - 1; bLeapFlag = Lunar_IsIntercalation( psLunarDate->wYear ); wSolarMonth += 12;
wSolarDaysSum = m_wSolarDays[wSolarMonth]; if ( wSolarMonth > 1 ) wSolarDaysSum += bLeapFlag; wDaySum = wSolarDaysSum + psSolarDate->chDate; } else { psLunarDate->wYear = psSolarDate->wYear; }
wTmp1 = LPara_GetBaseDays(chYearIndex); for ( i=0; i<13; i++ ) { wTmp2 = wTmp1 + LPara_GetMonthDays( chYearIndex, i) + 29; if ( wDaySum <= wTmp2 ) break; wTmp1 = wTmp2; }
psLunarDate->chMonth = i + 1; psLunarDate->chDate = wDaySum - wTmp1;
im = LPara_GetIntercalation(chYearIndex); if ( im != 0 && psLunarDate->chMonth > im) psLunarDate->chMonth--;
if ( psLunarDate->chMonth > 12) psLunarDate->chMonth -= 12;
return 0; }
|