import Foundation nonisolated enum DateSection: Hashable { case today case yesterday case thisWeek case thisMonth case year(Int) var label: String { switch self { case .today: return "今天" case .yesterday: return "昨天" case .thisWeek: return "本周" case .thisMonth: return "本月" case .year(let y): return "\(y) 年" } } var sortIndex: Int { switch self { case .today: return 0 case .yesterday: return 1 case .thisWeek: return 2 case .thisMonth: return 3 case .year(let y): return 10_000 - y } } } enum TimelineGrouping { static func section(for date: Date, now: Date = .now, calendar: Calendar = .current) -> DateSection { if calendar.isDate(date, inSameDayAs: now) { return .today } if let yesterday = calendar.date(byAdding: .day, value: -1, to: now), calendar.isDate(date, inSameDayAs: yesterday) { return .yesterday } if calendar.isDate(date, equalTo: now, toGranularity: .weekOfYear) { return .thisWeek } if calendar.isDate(date, equalTo: now, toGranularity: .month) { return .thisMonth } let year = calendar.component(.year, from: date) return .year(year) } static func group(_ entries: [TimelineEntry], now: Date = .now, calendar: Calendar = .current) -> [(section: DateSection, items: [TimelineEntry])] { var buckets: [DateSection: [TimelineEntry]] = [:] for entry in entries { let key = section(for: entry.date, now: now, calendar: calendar) buckets[key, default: []].append(entry) } return buckets .map { ($0.key, $0.value.sorted { $0.date > $1.date }) } .sorted { $0.0.sortIndex < $1.0.sortIndex } } } func formatDuration(_ interval: TimeInterval) -> String { let totalMinutes = Int(max(0, interval) / 60) let days = totalMinutes / (60 * 24) let hours = (totalMinutes % (60 * 24)) / 60 let minutes = totalMinutes % 60 if days > 0 && hours > 0 { return "\(days) 天 \(hours) 小时" } if days > 0 { return "\(days) 天" } if hours > 0 && minutes > 0 { return "\(hours) 小时 \(minutes) 分" } if hours > 0 { return "\(hours) 小时" } if minutes > 0 { return "\(minutes) 分钟" } return "刚刚" }