import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; import AsyncStorage from '@/utils/kvStore'; import { FASTING_STORAGE_KEYS } from '@/constants/Fasting'; dayjs.extend(duration); dayjs.extend(isSameOrAfter); dayjs.extend(isSameOrBefore); export type FastingPhase = 'upcoming' | 'fasting' | 'completed'; export const calculateFastingWindow = (start: Date, fastingHours: number) => { const startDate = dayjs(start).second(0).millisecond(0); const endDate = startDate.add(fastingHours, 'hour'); return { start: startDate.toDate(), end: endDate.toDate(), }; }; export const getFastingPhase = (start?: Date | null, end?: Date | null, now: Date = new Date()): FastingPhase => { if (!start || !end) { return 'completed'; } const nowJs = dayjs(now); const startJs = dayjs(start); const endJs = dayjs(end); if (nowJs.isBefore(startJs)) { return 'upcoming'; } if (nowJs.isSameOrAfter(startJs) && nowJs.isBefore(endJs)) { return 'fasting'; } return 'completed'; }; export const getPhaseLabel = (phase: FastingPhase) => { switch (phase) { case 'fasting': return '断食中'; case 'upcoming': return '可饮食'; case 'completed': return '可饮食'; default: return '可饮食'; } }; export const formatDayDescriptor = (date: Date | null | undefined, now: Date = new Date()) => { if (!date) return '--'; const target = dayjs(date); const today = dayjs(now).startOf('day'); if (target.isSame(today, 'day')) { return '今天'; } if (target.isSame(today.subtract(1, 'day'), 'day')) { return '昨天'; } if (target.isSame(today.add(1, 'day'), 'day')) { return '明天'; } return target.format('MM-DD'); }; export const formatTime = (date: Date | null | undefined) => { if (!date) return '--:--'; return dayjs(date).format('HH:mm'); }; export const formatCountdown = (target: Date | null | undefined, now: Date = new Date()) => { if (!target) return '--:--:--'; const diff = dayjs(target).diff(now); if (diff <= 0) { return '00:00:00'; } const dur = dayjs.duration(diff); const hours = String(Math.floor(dur.asHours())).padStart(2, '0'); const minutes = String(dur.minutes()).padStart(2, '0'); const seconds = String(dur.seconds()).padStart(2, '0'); return `${hours}:${minutes}:${seconds}`; }; export const buildDisplayWindow = (start?: Date | null, end?: Date | null) => { return { startDayLabel: formatDayDescriptor(start ?? null), startTimeLabel: formatTime(start ?? null), endDayLabel: formatDayDescriptor(end ?? null), endTimeLabel: formatTime(end ?? null), }; }; export const loadPreferredPlanId = async (): Promise => { try { return await AsyncStorage.getItem(FASTING_STORAGE_KEYS.preferredPlanId); } catch (error) { console.warn('加载断食首选计划ID失败', error); return null; } }; export const savePreferredPlanId = async (planId: string) => { try { await AsyncStorage.setItem(FASTING_STORAGE_KEYS.preferredPlanId, planId); } catch (error) { console.warn('保存断食首选计划ID失败', error); } }; export type FastingNotificationIds = { startId?: string | null; endId?: string | null; }; export const getFastingNotificationsRegistered = async (): Promise => { try { const value = await AsyncStorage.getItem(FASTING_STORAGE_KEYS.notificationsRegistered); return value === 'true'; } catch (error) { console.warn('读取断食通知注册状态失败', error); return false; } }; export const setFastingNotificationsRegistered = async (registered: boolean) => { try { if (registered) { await AsyncStorage.setItem(FASTING_STORAGE_KEYS.notificationsRegistered, 'true'); } else { await AsyncStorage.removeItem(FASTING_STORAGE_KEYS.notificationsRegistered); } } catch (error) { console.warn('更新断食通知注册状态失败', error); } }; export const loadStoredFastingNotificationIds = async (): Promise => { try { const [startId, endId] = await Promise.all([ AsyncStorage.getItem(FASTING_STORAGE_KEYS.startNotificationId), AsyncStorage.getItem(FASTING_STORAGE_KEYS.endNotificationId), ]); return { startId: startId ?? undefined, endId: endId ?? undefined, }; } catch (error) { console.warn('读取断食通知ID失败', error); return {}; } }; export const saveFastingNotificationIds = async (ids: FastingNotificationIds) => { try { if (ids.startId) { await AsyncStorage.setItem(FASTING_STORAGE_KEYS.startNotificationId, ids.startId); } else { await AsyncStorage.removeItem(FASTING_STORAGE_KEYS.startNotificationId); } if (ids.endId) { await AsyncStorage.setItem(FASTING_STORAGE_KEYS.endNotificationId, ids.endId); } else { await AsyncStorage.removeItem(FASTING_STORAGE_KEYS.endNotificationId); } } catch (error) { console.warn('保存断食通知ID失败', error); } }; export const clearFastingNotificationIds = async () => { try { await Promise.all([ AsyncStorage.removeItem(FASTING_STORAGE_KEYS.startNotificationId), AsyncStorage.removeItem(FASTING_STORAGE_KEYS.endNotificationId), ]); } catch (error) { console.warn('清除断食通知ID失败', error); } };