Created
March 15, 2022 11:18
-
-
Save ARamy23/45b86965d8627f8e0866d831ad488d6d to your computer and use it in GitHub Desktop.
Simplifies Date Parsing Process and displaying
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// MARK: - Date Format & Components | |
extension Date { | |
// MARK: - Helper types | |
enum Format: String, CaseIterable { | |
/// 15/03/2022, 11:02 AM | |
case ddMMyyyyHHmmA = "dd/MM/yyyy, HH:mm a" | |
/// "18/10/1993" | |
case ddMMyyyySlashed = "dd/MM/yyyy" | |
/// "18/10/1993" | |
case mmDDyyyySlashed = "MM/dd/yyyy" | |
/// "18-10-1993" | |
case ddMMyyyyDashed = "dd-MM-yyyy" | |
/// "1993-10-18" | |
case yyyyMMddDashed = "yyyy-MM-dd" | |
/// 2020-09-17 07:41:51 +0000 | |
case isoMilliSecondsFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" | |
/// "17.09.2020" | |
case ddMMyyyyDotted = "dd.MM.yyyy" | |
/// 2021-03-22T04:08:00Z | |
case isoFormat = "yyyy-MM-dd'T'HH:mm:ssZ" | |
/// 17 September 2020 - 09:41 | |
case ddMMyyyyHHmm = "dd MMMM yyyy - hh:mm" | |
/// 17 September 2020 - 09:41 AM | |
case ddMMyyyyHHmma = "dd MMMM yyyy - hh:mm a" | |
/// 09:59 PM | |
case hhmmA = "hh:mm a" | |
/// 9:59 PM | |
case hmmA = "h:mm a" | |
/// 9:59 PM | |
case mmss = "mm:ss" | |
/// Tuesday | |
case deliveryTypeFormatForDate = "EEEE" | |
/// 14:45 | |
case deliveryTypeFormatForHour = "HH:mm" | |
/// 14:45:59 | |
case HHmmss = "HH:mm:ss" | |
/// 2021-03-09 14:27:21 | |
case yyyyMMddHHmmSS = "yyyy-MM-dd HH:mm:ss" | |
/// 2021-03-09T14:27:21Z | |
case isoMilliSecondsFormatV2 = "yyyy-MM-dd'T'HH:mm:ssZZZ" | |
} | |
enum Components: String { | |
/// 1997 | |
case yearFull = "yyyy" | |
/// 97 (1997) | |
case yearShort = "yy" | |
/// 7 | |
case monthDigit = "M" | |
/// 07 | |
case monthDigitPadded = "MM" | |
/// Jul | |
case monthShort = "MMM" | |
/// July | |
case monthFull = "MMMM" | |
/// J (July) | |
case monthLetter = "MMMMM" | |
/// 5 | |
case dayOfMonth = "d" | |
/// Sat | |
case weekdayShort = "EEE" | |
/// Saturday | |
case weekdayFull = "EEEE" | |
/// S (Saturday) | |
case weekdayLetter = "EEEEE" | |
/// Localized **13** or **1 PM**, depending on the locale. | |
case hour = "j" | |
/// 20 | |
case minute = "m" | |
/// 08 | |
case second = "ss" | |
/// CST | |
case timeZone = "zzz" | |
/// **Central Standard Time** or **CST-06:00** or if full name is unavailable. | |
case timeZoneFull = "zzzz" | |
} | |
// MARK: - Date Formatting | |
func format( | |
with components: [Components], | |
in timeZone: TimeZone? = .current, | |
using dateFormatter: DateFormatter = DateFormatter(), | |
locale: Locale = .current | |
) -> String? { | |
let template = components.map(\.rawValue).joined(separator: " ") | |
guard let localizedFormat = DateFormatter.dateFormat(fromTemplate: template, options: 0, locale: locale) else { | |
return nil | |
} | |
dateFormatter.timeZone = timeZone | |
dateFormatter.dateFormat = localizedFormat | |
return dateFormatter.string(from: self) | |
} | |
} | |
class DatePresenter { | |
func present(usingString date: String) -> DateStringInput { | |
DateStringInput(date: date) | |
} | |
func present(usingDate date: Date) -> DateStringOutput { | |
DateStringOutput(date: date) | |
} | |
} | |
protocol DateFormatterBuilderProtocol: AnyObject { | |
associatedtype Builder | |
var formatter: DateFormatter { get } | |
} | |
protocol SelfAdaptingDateFormatterBuilderProtocol: DateFormatterBuilderProtocol where Builder == Self { } | |
extension SelfAdaptingDateFormatterBuilderProtocol { | |
@discardableResult | |
func using(format: String) -> Self { | |
formatter.dateFormat = format | |
return self | |
} | |
func using(format: Date.Format) -> Self { | |
formatter.dateFormat = format.rawValue | |
return self | |
} | |
@discardableResult | |
func using(localizedTemplate: String) -> Self { | |
formatter.setLocalizedDateFormatFromTemplate(localizedTemplate) | |
return self | |
} | |
@discardableResult | |
func using(localizedTemplate: [Date.Components]) -> Self { | |
self.using(localizedTemplate: localizedTemplate.map(\.rawValue).joined(separator: "")) | |
} | |
@discardableResult | |
func with(locale: Locale) -> Self { | |
formatter.locale = locale | |
return self | |
} | |
@discardableResult | |
func adoptLocaleCalendar() -> Self { | |
formatter.calendar = formatter.locale.calendar | |
return self | |
} | |
@discardableResult | |
func adoptLocaleTimeZone() -> Self { | |
formatter.timeZone = .init(identifier: formatter.locale.identifier) | |
return self | |
} | |
} | |
class DateFormatterBuilder: SelfAdaptingDateFormatterBuilderProtocol { | |
let formatter: DateFormatter = .init() | |
} | |
class DateStringInput: DateFormatterBuilder { | |
let date: String | |
init(date: String) { | |
self.date = date | |
} | |
func parse() -> DateStringOutput { | |
return .init( | |
date: formatter.date(from: date)! | |
) | |
} | |
} | |
class DateStringOutput: DateFormatterBuilder { | |
let date: Date | |
init(date: Date) { | |
self.date = date | |
} | |
func display() -> String { | |
return formatter.string(from: date) | |
} | |
} | |
// MARK: - Usage | |
let incomingDateFromBackend = DateStringInput( | |
date: "15/03/2022, 11:02 AM" | |
) | |
.using(format: .ddMMyyyyHHmmA) | |
let displayableToUser = incomingDateFromBackend | |
.parse() | |
.using(localizedTemplate: [.hour, .minute, .monthFull]) | |
.display() | |
print(displayableToUser) // March, 12:02 AM |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment