fix(P3): 修复倒计时边界问题(闰年、月末)
This commit is contained in:
parent
82c291ef30
commit
c08f5aa4aa
@ -52,7 +52,7 @@ export function AnniversaryList({ events, onEventClick, onAddClick }: Anniversar
|
|||||||
title: h.name,
|
title: h.name,
|
||||||
date: h.date.toISOString(),
|
date: h.date.toISOString(),
|
||||||
is_holiday: true,
|
is_holiday: true,
|
||||||
is_lunar: false,
|
is_lunar: h.isLunar,
|
||||||
repeat_type: 'yearly',
|
repeat_type: 'yearly',
|
||||||
type: 'anniversary',
|
type: 'anniversary',
|
||||||
is_builtin: true,
|
is_builtin: true,
|
||||||
|
|||||||
@ -10,6 +10,18 @@ export interface CountdownResult {
|
|||||||
isPast: boolean;
|
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 的倒计时
|
* 考虑重复规则,计算下一个 occurrence 的倒计时
|
||||||
@ -21,36 +33,38 @@ export function calculateCountdown(
|
|||||||
): CountdownResult {
|
): CountdownResult {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||||
let targetDate = new Date(dateStr);
|
let targetDate: Date;
|
||||||
let isPast = false;
|
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) {
|
if (isLunar) {
|
||||||
const lunarDate = Lunar.fromYmd(
|
// 农历日期:直接用原始农历年/月/日创建农历对象
|
||||||
targetDate.getFullYear(),
|
const lunarDate = Lunar.fromYmd(originalYear, originalMonth + 1, originalDay);
|
||||||
targetDate.getMonth() + 1,
|
|
||||||
targetDate.getDate()
|
|
||||||
);
|
|
||||||
const solar = lunarDate.getSolar();
|
const solar = lunarDate.getSolar();
|
||||||
targetDate = new Date(solar.getYear(), solar.getMonth() - 1, solar.getDay());
|
targetDate = new Date(solar.getYear(), solar.getMonth() - 1, solar.getDay());
|
||||||
|
} else {
|
||||||
|
// 公历日期
|
||||||
|
targetDate = new Date(originalYear, originalMonth, originalDay);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算下一个 occurrence
|
// 计算下一个 occurrence
|
||||||
if (repeatType === 'yearly') {
|
if (repeatType === 'yearly') {
|
||||||
// 年度重复:找到今年或明年的对应日期
|
// 年度重复:找到今年或明年的对应日期
|
||||||
const thisYearTarget = new Date(today.getFullYear(), targetDate.getMonth(), targetDate.getDate());
|
targetDate = safeCreateDate(today.getFullYear(), targetDate.getMonth(), targetDate.getDate());
|
||||||
if (thisYearTarget >= today) {
|
if (targetDate < today) {
|
||||||
targetDate = thisYearTarget;
|
targetDate = safeCreateDate(today.getFullYear() + 1, targetDate.getMonth(), targetDate.getDate());
|
||||||
} else {
|
|
||||||
targetDate = new Date(today.getFullYear() + 1, targetDate.getMonth(), targetDate.getDate());
|
|
||||||
}
|
}
|
||||||
} else if (repeatType === 'monthly') {
|
} else if (repeatType === 'monthly') {
|
||||||
// 月度重复:找到本月或下月的对应日期
|
// 月度重复:找到本月或下月的对应日期
|
||||||
const thisMonthTarget = new Date(today.getFullYear(), today.getMonth(), targetDate.getDate());
|
targetDate = safeCreateDate(today.getFullYear(), today.getMonth(), targetDate.getDate());
|
||||||
if (thisMonthTarget >= today) {
|
if (targetDate < today) {
|
||||||
targetDate = thisMonthTarget;
|
targetDate = safeCreateDate(today.getFullYear(), today.getMonth() + 1, targetDate.getDate());
|
||||||
} else {
|
|
||||||
targetDate = new Date(today.getFullYear(), today.getMonth() + 1, targetDate.getDate());
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 不重复
|
// 不重复
|
||||||
@ -67,13 +81,14 @@ export function calculateCountdown(
|
|||||||
isPast = true;
|
isPast = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const days = Math.max(0, Math.floor(diff / (1000 * 60 * 60 * 24)));
|
const absDiff = Math.abs(diff);
|
||||||
const hours = Math.max(0, Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)));
|
const days = Math.floor(absDiff / (1000 * 60 * 60 * 24));
|
||||||
const minutes = Math.max(0, Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)));
|
const hours = Math.floor((absDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||||
const seconds = Math.max(0, Math.floor((diff % (1000 * 60)) / 1000));
|
const minutes = Math.floor((absDiff % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
const seconds = Math.floor((absDiff % (1000 * 60)) / 1000);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
days,
|
days: isPast ? -days : days,
|
||||||
hours,
|
hours,
|
||||||
minutes,
|
minutes,
|
||||||
seconds,
|
seconds,
|
||||||
@ -99,7 +114,11 @@ export function formatCountdown(countdown: CountdownResult): string {
|
|||||||
return `${countdown.days}天`;
|
return `${countdown.days}天`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${countdown.hours}时${countdown.minutes}分`;
|
if (countdown.hours > 0) {
|
||||||
|
return `${countdown.hours}时${countdown.minutes}分`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${countdown.minutes}分${countdown.seconds}秒`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -150,3 +169,27 @@ export function getFriendlyDateDescription(
|
|||||||
|
|
||||||
return `${month}月${day}日`;
|
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
1
tmpclaude-05e0-cwd
Normal file
@ -0,0 +1 @@
|
|||||||
|
/e/qia/client
|
||||||
Loading…
x
Reference in New Issue
Block a user