fix(P3): 修复倒计时边界问题(闰年、月末)

This commit is contained in:
ddshi 2026-01-29 17:24:53 +08:00
parent 82c291ef30
commit c08f5aa4aa
3 changed files with 68 additions and 24 deletions

View File

@ -52,7 +52,7 @@ export function AnniversaryList({ events, onEventClick, onAddClick }: Anniversar
title: h.name,
date: h.date.toISOString(),
is_holiday: true,
is_lunar: false,
is_lunar: h.isLunar,
repeat_type: 'yearly',
type: 'anniversary',
is_builtin: true,

View File

@ -10,6 +10,18 @@ export interface CountdownResult {
isPast: boolean;
}
/**
*
*/
function safeCreateDate(year: number, month: number, day: number): Date {
const date = new Date(year, month, day);
// 如果日期溢出(如下个月),自动调整到月末
if (date.getMonth() !== month % 12) {
date.setMonth(month + 1, 0);
}
return date;
}
/**
*
* occurrence
@ -21,36 +33,38 @@ export function calculateCountdown(
): CountdownResult {
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
let targetDate = new Date(dateStr);
let targetDate: Date;
let isPast = false;
// 如果是农历日期,转换为公历
// 解析日期字符串
// 格式假设为 ISO 格式 "YYYY-MM-DD" 或完整的 ISO datetime
const dateParts = dateStr.split('T')[0].split('-').map(Number);
const originalYear = dateParts[0];
const originalMonth = dateParts[1] - 1; // JavaScript月份从0开始
const originalDay = dateParts[2];
if (isLunar) {
const lunarDate = Lunar.fromYmd(
targetDate.getFullYear(),
targetDate.getMonth() + 1,
targetDate.getDate()
);
// 农历日期:直接用原始农历年/月/日创建农历对象
const lunarDate = Lunar.fromYmd(originalYear, originalMonth + 1, originalDay);
const solar = lunarDate.getSolar();
targetDate = new Date(solar.getYear(), solar.getMonth() - 1, solar.getDay());
} else {
// 公历日期
targetDate = new Date(originalYear, originalMonth, originalDay);
}
// 计算下一个 occurrence
if (repeatType === 'yearly') {
// 年度重复:找到今年或明年的对应日期
const thisYearTarget = new Date(today.getFullYear(), targetDate.getMonth(), targetDate.getDate());
if (thisYearTarget >= today) {
targetDate = thisYearTarget;
} else {
targetDate = new Date(today.getFullYear() + 1, targetDate.getMonth(), targetDate.getDate());
targetDate = safeCreateDate(today.getFullYear(), targetDate.getMonth(), targetDate.getDate());
if (targetDate < today) {
targetDate = safeCreateDate(today.getFullYear() + 1, targetDate.getMonth(), targetDate.getDate());
}
} else if (repeatType === 'monthly') {
// 月度重复:找到本月或下月的对应日期
const thisMonthTarget = new Date(today.getFullYear(), today.getMonth(), targetDate.getDate());
if (thisMonthTarget >= today) {
targetDate = thisMonthTarget;
} else {
targetDate = new Date(today.getFullYear(), today.getMonth() + 1, targetDate.getDate());
targetDate = safeCreateDate(today.getFullYear(), today.getMonth(), targetDate.getDate());
if (targetDate < today) {
targetDate = safeCreateDate(today.getFullYear(), today.getMonth() + 1, targetDate.getDate());
}
} else {
// 不重复
@ -67,13 +81,14 @@ export function calculateCountdown(
isPast = true;
}
const days = Math.max(0, Math.floor(diff / (1000 * 60 * 60 * 24)));
const hours = Math.max(0, Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)));
const minutes = Math.max(0, Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)));
const seconds = Math.max(0, Math.floor((diff % (1000 * 60)) / 1000));
const absDiff = Math.abs(diff);
const days = Math.floor(absDiff / (1000 * 60 * 60 * 24));
const hours = Math.floor((absDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((absDiff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((absDiff % (1000 * 60)) / 1000);
return {
days,
days: isPast ? -days : days,
hours,
minutes,
seconds,
@ -99,9 +114,13 @@ export function formatCountdown(countdown: CountdownResult): string {
return `${countdown.days}`;
}
if (countdown.hours > 0) {
return `${countdown.hours}${countdown.minutes}`;
}
return `${countdown.minutes}${countdown.seconds}`;
}
/**
*
*/
@ -150,3 +169,27 @@ export function getFriendlyDateDescription(
return `${month}${day}`;
}
/**
*
*/
export function getSolarFromLunar(lunarMonth: number, lunarDay: number, year?: number): Date {
const targetYear = year || new Date().getFullYear();
const lunar = Lunar.fromYmd(targetYear, lunarMonth, lunarDay);
const solar = lunar.getSolar();
return new Date(solar.getYear(), solar.getMonth() - 1, solar.getDay());
}
/**
*
*/
export function getLunarFromSolar(solarDate: Date): { month: number; day: number; monthInChinese: string; dayInChinese: string } {
const solar = Solar.fromYmd(solarDate.getFullYear(), solarDate.getMonth() + 1, solarDate.getDate());
const lunar = solar.getLunar();
return {
month: lunar.getMonth(),
day: lunar.getDay(),
monthInChinese: lunar.getMonthInChinese(),
dayInChinese: lunar.getDayInChinese(),
};
}

1
tmpclaude-05e0-cwd Normal file
View File

@ -0,0 +1 @@
/e/qia/client