import Foundation /// 上游返回的时间戳格式并不统一(`yyyy-MM-dd HH:mm:ss`、ISO8601、秒/毫秒时间戳都出现过), /// 列表里需要一个稳定的短读法,解析不出来时原样回退,避免出现"--"这种假结论。 public enum TimestampText { private static let parsers: [DateFormatter] = { let patterns = [ "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd HH:mm:ss", "yyyy-MM-dd", ] return patterns.map { pattern in let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.calendar = Calendar(identifier: .gregorian) formatter.timeZone = .current formatter.dateFormat = pattern return formatter } }() private static let utcParser: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.calendar = Calendar(identifier: .gregorian) formatter.timeZone = TimeZone(secondsFromGMT: 0) formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" return formatter }() private static let timeFormat: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "zh_CN") formatter.dateFormat = "HH:mm" return formatter }() private static let monthDayFormat: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "zh_CN") formatter.dateFormat = "MM-dd HH:mm" return formatter }() private static let fullFormat: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "zh_CN") formatter.dateFormat = "yyyy-MM-dd" return formatter }() public static func date(from raw: String) -> Date? { let text = raw.trimmingCharacters(in: .whitespacesAndNewlines) guard text.isEmpty == false else { return nil } if let number = Double(text), text.allSatisfy({ $0.isNumber }) { // 13 位是毫秒,10 位是秒。 return Date(timeIntervalSince1970: text.count > 11 ? number / 1000 : number) } let head = text.hasSuffix("Z") ? String(text.dropLast()) : text for parser in parsers { if let parsed = parser.date(from: head) { return text.hasSuffix("Z") ? utcParser.date(from: head) ?? parsed : parsed } } return nil } /// 今天只看时分,今年看月日,跨年才写全年份。 public static func short(_ raw: String, now: Date = Date()) -> String { guard let parsed = date(from: raw) else { return raw.trimmingCharacters(in: .whitespacesAndNewlines) } let calendar = Calendar.current if calendar.isDate(parsed, inSameDayAs: now) { return timeFormat.string(from: parsed) } if calendar.isDate(parsed, equalTo: now, toGranularity: .year) { return monthDayFormat.string(from: parsed) } return fullFormat.string(from: parsed) } }