|
// |
|
// DateTime.swift |
|
// HLACore |
|
// |
|
// Created by WeyHan Ng on 24/09/2022. |
|
// |
|
|
|
import Foundation |
|
|
|
// Input format is the format of the date string being converted to Swift Date. |
|
public enum InputFormat { |
|
case isoDate |
|
case isoDateTime |
|
case isoDateTimeWithoutTimeZone |
|
} |
|
|
|
// Output format is the date string format used to convert Swift Date to string. |
|
public enum OutputFormat { |
|
case isoDate |
|
case isoDateTime |
|
case isoDateTimeWithoutTimeZone |
|
case localDate |
|
case localDateTimeWithoutSeconds |
|
case localTime |
|
} |
|
|
|
|
|
// |
|
// Usage: |
|
// To convert "yyyy-MM-dd'T'HH:mm:ssXXXXX" formatted string to Swift Date. |
|
// TimeZone ("XXXXX") can be the special zone "Z" meaning 0 offset from UTC or |
|
// ("[+-]99:99") plus or minus sign followed by 2 digit hours followed by a |
|
// colon followed by 2 digit minutes offset from UTC. .e.g.: |
|
// |
|
// let date1 = StaticDateFormatter.date(from: "2022-09-24T10:36:27-05:00") |
|
// let date2 = StaticDateFormatter.date(from: "2022-09-24T15:36:27Z") |
|
// |
|
// Note: The above date1 and date2 are the same moment. |
|
// |
|
// To convert "yyyy-MM-dd'T'HH:mm:ss" formatted string to Swift Date. |
|
// TimeZone is not given in the date-time string but otherwise the same as the |
|
// above example. e.g.: |
|
// |
|
// let date3 = StaticDateFormatter.date(from: "2022-09-24T15:36:27", |
|
// format: .isoDateTimeWithoutTimeZone) |
|
// |
|
// The above assumes local time zone and is hardcoded in StaticDateFormatter. |
|
// Change the local time zone as required for your own usage. |
|
// |
|
// To convert Swift Date to string using the default date-time format. |
|
// To use the default date-time format, the `format` argument can be left out. |
|
// e.g.: |
|
// |
|
// let dateStr1 = StaticDateFormatter.string(from: Date()) |
|
// |
|
// To convert Swift Date to a specific date-time format. |
|
// |
|
// let dateStr2 = StaticDateFormatter.string(from: Date(), |
|
// format: .localDateTimeWithoutSeconds) |
|
// |
|
// The output format hardcoded here to avoid typo in code. Only the enums |
|
// associated with the hardcoded format is expose and used in the project to |
|
// select the formats used for the conversion. |
|
// |
|
|
|
public class StaticDateFormatter { |
|
// Add or remove the static formatter as required to tailor for your specific |
|
// needs. Make the same changes to the InputFormat and OutputFormat enum as |
|
// required. |
|
private static let isoDateFormatter = ISO8601DateFormatter() |
|
private static let isoDateTimeFormatter = ISO8601DateFormatter() |
|
private static let isoDateTimeNoTimeZoneFormatter = ISO8601DateFormatter() |
|
private static let localDateFormatter = DateFormatter() |
|
private static let localDateTimeLongFormatter = DateFormatter() |
|
private static let localTimeFormatter = DateFormatter() |
|
|
|
private static var notSetup = true |
|
|
|
// Hide the initializer because this class is meant to be used as static object |
|
// and should never be instantiated. |
|
private init() { } |
|
|
|
public static func string(from date: Date, format: OutputFormat = .localDate) -> String { |
|
setupIfNeeded() |
|
|
|
switch format { |
|
case .isoDate: |
|
return isoDateFormatter.string(from: date) |
|
|
|
case .isoDateTime: |
|
return isoDateTimeNoTimeZoneFormatter.string(from: date) |
|
|
|
case .isoDateTimeWithoutTimeZone: |
|
return isoDateTimeNoTimeZoneFormatter.string(from: date) |
|
|
|
case .localDate: |
|
return localDateFormatter.string(from: date) |
|
|
|
case .localDateTimeWithoutSeconds: |
|
return localDateTimeLongFormatter.string(from: date) |
|
|
|
case .localTime: |
|
return localTimeFormatter.string(from: date) |
|
} |
|
} |
|
|
|
// Change the default for InputFormat to the most common uses in your project |
|
// or remove the defaults to force explicit choice at the calling side. |
|
public static func date(from string: String, format: InputFormat = .isoDateTimeWithoutTimeZone) -> Date? { |
|
setupIfNeeded() |
|
|
|
switch format { |
|
case .isoDate: |
|
return isoDateFormatter.date(from: string) |
|
|
|
case .isoDateTime: |
|
return isoDateTimeFormatter.date(from: string) |
|
|
|
case .isoDateTimeWithoutTimeZone: |
|
return isoDateTimeNoTimeZoneFormatter.date(from: string) |
|
} |
|
} |
|
|
|
private static func setupIfNeeded() { |
|
// Only setup formatter once per session. |
|
guard notSetup == true else { |
|
return |
|
} |
|
|
|
// Change the time zone to reflect your own usage. |
|
let localTimeZone = TimeZone(identifier: "America/Los_Angeles") |
|
|
|
// Formatter to parse ISO8601 date string. i.e. "yyyy-MM-dd" |
|
isoDateFormatter.timeZone = localTimeZone |
|
isoDateFormatter.formatOptions = [ |
|
.withFullDate, |
|
.withDashSeparatorInDate |
|
] |
|
|
|
// Formatter to parse ISO8601 date-time string. i.e. "yyyy-MM-dd'T'HH:mm:ssXXXXX" |
|
// where XXXXX is the time zone offset from UTC |
|
isoDateTimeFormatter.timeZone = localTimeZone |
|
isoDateTimeFormatter.formatOptions = [ |
|
.withFullDate, |
|
.withTime, |
|
.withDashSeparatorInDate, |
|
.withColonSeparatorInTime, |
|
.withTimeZone, |
|
.withColonSeparatorInTimeZone |
|
] |
|
|
|
// Formatter to parse ISO8601 datetime string that does not specify the |
|
// timezone. i.e. "yyyy-MM-dd'T'HH:mm:ss" |
|
isoDateTimeNoTimeZoneFormatter.timeZone = localTimeZone |
|
isoDateTimeNoTimeZoneFormatter.formatOptions = [ |
|
.withFullDate, |
|
.withTime, |
|
.withDashSeparatorInDate, |
|
.withColonSeparatorInTime |
|
] |
|
|
|
// Formatter to convert Date to US date string format. |
|
localDateFormatter.timeZone = localTimeZone |
|
localDateFormatter.dateFormat = "MM-dd-yyyy" |
|
|
|
// Formatter to convert Date to Malaysia date-time string format. |
|
localDateTimeLongFormatter.timeZone = localTimeZone |
|
localDateTimeLongFormatter.dateFormat = "MM-dd-yyyy hh:mm a" |
|
|
|
// Formatter to convert Date to Malaysia time string format. |
|
localTimeFormatter.timeZone = localTimeZone |
|
localTimeFormatter.dateFormat = "hh:mm a" |
|
|
|
|
|
// Turn off setup |
|
notSetup = false |
|
} |
|
} |