Files
SyncCalendar/entry/src/main/ets/common/LunarUtil.ets
T

167 lines
6.2 KiB
Plaintext
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.
// entry/src/main/ets/common/LunarUtil.ets
// 公历 → 农历(1901~2099),经典压缩表算法
export class LunarDate {
year: number = 0;
month: number = 0; // 1~12
day: number = 0; // 1~30
isLeap: boolean = false;
}
export class LunarUtil {
// 农历压缩数据表:每年一个十六进制值(1900~2100)
private static readonly LUNAR_INFO: number[] = [
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, 0x06e95, 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, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
0x0a2e0, 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
];
private static readonly MONTH_CN: string[] =
['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊'];
private static readonly DIGIT_CN: string[] =
['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
/** 农历闰月是哪个月(0 表示无闰月) */
private static leapMonth(y: number): number {
return LunarUtil.LUNAR_INFO[y - 1900] & 0xf;
}
/** 农历闰月天数 */
private static leapDays(y: number): number {
if (LunarUtil.leapMonth(y) === 0) {
return 0;
}
return (LunarUtil.LUNAR_INFO[y - 1900] & 0x10000) !== 0 ? 30 : 29;
}
/** 农历 y 年 m 月(非闰月)天数 */
private static monthDays(y: number, m: number): number {
return (LunarUtil.LUNAR_INFO[y - 1900] & (0x10000 >> m)) !== 0 ? 30 : 29;
}
/** 农历 y 年总天数 */
private static yearDays(y: number): number {
let sum: number = 348;
for (let i: number = 0x8000; i > 0x8; i >>= 1) {
sum += (LunarUtil.LUNAR_INFO[y - 1900] & i) !== 0 ? 1 : 0;
}
return sum + LunarUtil.leapDays(y);
}
/** 公历毫秒 → 农历日期(超出 1901~2099 返回 null) */
static lunar(ms: number): LunarDate | null {
const d = new Date(ms);
const year: number = d.getFullYear();
const month: number = d.getMonth() + 1;
const day: number = d.getDate();
if (year < 1901 || year > 2099) {
return null;
}
let offset: number =
Math.floor((Date.UTC(year, month - 1, day) - Date.UTC(1900, 0, 31)) / 86400000);
let i: number = 1900;
let temp: number = 0;
for (i = 1900; i < 2101 && offset > 0; i++) {
temp = LunarUtil.yearDays(i);
offset -= temp;
}
if (offset < 0) {
offset += temp;
i--;
}
const leap: number = LunarUtil.leapMonth(i);
let isLeap: boolean = false;
let m: number = 1;
for (m = 1; m < 13 && offset > 0; m++) {
if (leap > 0 && m === leap + 1 && isLeap === false) {
--m;
isLeap = true;
temp = LunarUtil.leapDays(i);
} else {
temp = LunarUtil.monthDays(i, m);
}
if (isLeap === true && m === leap + 1) {
isLeap = false;
}
offset -= temp;
}
if (offset === 0 && leap > 0 && m === leap + 1) {
if (isLeap) {
isLeap = false;
} else {
isLeap = true;
--m;
}
}
if (offset < 0) {
offset += temp;
--m;
}
const result = new LunarDate();
result.year = i;
result.month = m;
result.day = offset + 1;
result.isLeap = isLeap;
return result;
}
/** 农历日名称:初一~三十 */
static dayName(day: number): string {
const dg: string[] = LunarUtil.DIGIT_CN;
if (day === 10) {
return '初十';
}
if (day === 20) {
return '二十';
}
if (day === 30) {
return '三十';
}
if (day < 10) {
return '初' + dg[day - 1];
}
if (day < 20) {
return '十' + dg[day - 11];
}
return '廿' + dg[day - 21];
}
/** 农历月名称:正月/二月/…/冬月/腊月,闰月带"闰" */
static monthName(month: number, isLeap: boolean): string {
const base: string = (month >= 1 && month <= 12) ? LunarUtil.MONTH_CN[month - 1] : '';
return (isLeap ? '闰' : '') + base + '月';
}
/**
* 日历格子上显示的农历文本:
* 初一时显示月名(如"九月"),其余显示日名(如"初五"、"十五")
*/
static lunarDayText(ms: number): string {
const info = LunarUtil.lunar(ms);
if (info === null) {
return '';
}
if (info.day === 1) {
return LunarUtil.monthName(info.month, info.isLeap);
}
return LunarUtil.dayName(info.day);
}
}