import Foundation protocol DateItem { var id: String { get } var startDate: Date { get } var hasEndDate: Bool { get } var potentialEndDate: Date { get } } extension Sequence where Element: DateItem { func sortedByStartDateAndId() -> [Element] { sorted { (lhs, rhs) -> Bool in if lhs.startDate == rhs.startDate { return lhs.id < rhs.id } return lhs.startDate > rhs.startDate } } } extension DateItem { var endDate: Date? { hasEndDate ? potentialEndDate : nil } func dateText(in language: ContentLanguage) -> String { guard let endDate else { return DateItemStorage.dateString(for: startDate, in: language) } let endText = DateItemStorage.dateString(for: potentialEndDate, in: language) let prefix = DateItemStorage.datePrefixString(from: startDate, to: endDate, in: language) return prefix + " - " + endText } } private enum DateItemStorage { static func datePrefixString(from start: Date, to end: Date, in language: ContentLanguage) -> String { guard Calendar.current.isDate(start, equalTo: end, toGranularity: .year) else { // Different year, return full string return DateItemStorage.dateString(for: start, in: language) } guard Calendar.current.isDate(start, equalTo: end, toGranularity: .month) else { // Different month return DateItemStorage.dayAndMonth(of: start, in: language) } return DateItemStorage.day.string(from: start) } static let englishDate: DateFormatter = { let df = DateFormatter() df.locale = .init(identifier: "en") df.dateFormat = "d. MMMM yyyy" return df }() static let germanDate: DateFormatter = { let df = DateFormatter() df.locale = .init(identifier: "de") df.dateFormat = "d. MMMM yyyy" return df }() static let englishDayAndMonth: DateFormatter = { let df = DateFormatter() df.locale = .init(identifier: "en") df.dateFormat = "d. MMMM" return df }() static let germanDayAndMonth: DateFormatter = { let df = DateFormatter() df.locale = .init(identifier: "de") df.dateFormat = "d. MMMM" return df }() static let day: DateFormatter = { let df = DateFormatter() df.dateFormat = "d." return df }() static func dayAndMonth(of date: Date, in language: ContentLanguage) -> String { switch language { case .english: return englishDayAndMonth.string(from: date) case .german: return germanDayAndMonth.string(from: date) } } static func dateString(for date: Date, in language: ContentLanguage) -> String { switch language { case .english: return englishDate.string(from: date) case .german: return germanDate.string(from: date) } } }