//
|
// SwiftDate
|
// Parse, validate, manipulate, and display dates, time and timezones in Swift
|
//
|
// Created by Daniele Margutti
|
// - Web: https://www.danielemargutti.com
|
// - Twitter: https://twitter.com/danielemargutti
|
// - Mail: hello@danielemargutti.com
|
//
|
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
|
//
|
|
import Foundation
|
|
public class ISOFormatter: DateToStringTrasformable {
|
|
public struct Options: OptionSet {
|
public let rawValue: Int
|
|
public init(rawValue: Int) {
|
self.rawValue = rawValue
|
}
|
|
/// The date representation includes the year. The format for year is inferred based on the other specified options.
|
/// - If withWeekOfYear is specified, YYYY is used.
|
/// - Otherwise, yyyy is used.
|
public static let withYear = ISOFormatter.Options(rawValue: 1 << 0)
|
|
/// The date representation includes the month. The format for month is MM.
|
public static let withMonth = ISOFormatter.Options(rawValue: 1 << 1)
|
|
/// The date representation includes the week of the year.
|
/// The format for week of year is ww, including the W prefix.
|
public static let withWeekOfYear = ISOFormatter.Options(rawValue: 1 << 2)
|
|
/// The date representation includes the day. The format for day is inferred based on provided options:
|
/// - If withMonth is specified, dd is used.
|
/// - If withWeekOfYear is specified, ee is used.
|
/// - Otherwise, DDD is used.
|
public static let withDay = ISOFormatter.Options(rawValue: 1 << 3)
|
|
/// The date representation includes the time. The format for time is HH:mm:ss.
|
public static let withTime = ISOFormatter.Options(rawValue: 1 << 4)
|
|
/// The date representation includes the timezone. The format for timezone is ZZZZZ.
|
public static let withTimeZone = ISOFormatter.Options(rawValue: 1 << 5)
|
|
/// The date representation uses a space ( ) instead of T between the date and time.
|
public static let withSpaceBetweenDateAndTime = ISOFormatter.Options(rawValue: 1 << 6)
|
|
/// The date representation uses the dash separator (-) in the date.
|
public static let withDashSeparatorInDate = ISOFormatter.Options(rawValue: 1 << 7)
|
|
/// The date representation uses the colon separator (:) in the time.
|
public static let withFullDate = ISOFormatter.Options(rawValue: 1 << 8)
|
|
/// The date representation includes the hour, minute, and second.
|
public static let withFullTime = ISOFormatter.Options(rawValue: 1 << 9)
|
|
/// The format used for internet date times, according to the RFC 3339 standard.
|
/// Equivalent to specifying withFullDate, withFullTime, withDashSeparatorInDate,
|
/// withColonSeparatorInTime, and withColonSeparatorInTimeZone.
|
public static let withInternetDateTime = ISOFormatter.Options(rawValue: 1 << 10)
|
|
// The format used for internet date times; it's similar to .withInternetDateTime
|
// but include milliseconds ('yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ').
|
public static let withInternetDateTimeExtended = ISOFormatter.Options(rawValue: 1 << 11)
|
|
/// Print the timezone in format `ZZZ` instead of `ZZZZZ`
|
/// An example outout maybe be `+0200` instead of `+02:00`.
|
public static let withoutTZSeparators = ISOFormatter.Options(rawValue: 1 << 12)
|
|
/// Evaluate formatting string
|
public var dateFormat: String {
|
if contains(.withInternetDateTimeExtended) || contains(.withoutTZSeparators) {
|
if contains(.withoutTZSeparators) {
|
return "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ"
|
}
|
return "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
|
}
|
|
if contains(.withInternetDateTime) {
|
if contains(.withoutTZSeparators) {
|
return "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ"
|
}
|
return "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
|
}
|
|
var format: String = ""
|
if contains(.withFullDate) {
|
format += "yyyy-MM-dd"
|
} else {
|
if contains(.withYear) {
|
if contains(.withWeekOfYear) {
|
format += "YYYY"
|
} else if contains(.withMonth) || contains(.withDay) {
|
format += "yyyy"
|
} else {
|
// not valid
|
}
|
}
|
if contains(.withMonth) {
|
if contains(.withYear) || contains(.withDay) || contains(.withWeekOfYear) {
|
format += "MM"
|
} else {
|
// not valid
|
}
|
}
|
if contains(.withWeekOfYear) {
|
if contains(.withDay) {
|
format += "'W'ww"
|
} else {
|
if contains(.withYear) || contains(.withMonth) {
|
if contains(.withDashSeparatorInDate) {
|
format += "-'W'ww"
|
} else {
|
format += "'W'ww"
|
}
|
} else {
|
// not valid
|
}
|
}
|
}
|
|
if contains(.withDay) {
|
if contains(.withWeekOfYear) {
|
format += "FF"
|
} else if contains(.withMonth) {
|
format += "dd"
|
} else if contains(.withYear) {
|
if contains(.withDashSeparatorInDate) {
|
format += "-DDD"
|
} else {
|
format += "DDD"
|
}
|
} else {
|
// not valid
|
}
|
}
|
}
|
|
let hasDate = (contains(.withFullDate) || contains(.withMonth) || contains(.withDay) || contains(.withWeekOfYear) || contains(.withYear))
|
if hasDate && (contains(.withFullTime) || contains(.withTimeZone) || contains(.withTime)) {
|
if contains(.withSpaceBetweenDateAndTime) {
|
format += " "
|
} else {
|
format += "'T'"
|
}
|
}
|
|
if contains(.withFullTime) {
|
format += "HH:mm:ssZZZZZ"
|
} else {
|
if contains(.withTime) {
|
format += "HH:mm:ss"
|
}
|
if contains(.withTimeZone) {
|
if contains(.withoutTZSeparators) {
|
return "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ"
|
}
|
format += "ZZZZZ"
|
}
|
}
|
|
return format
|
}
|
}
|
|
public static func format(_ date: DateRepresentable, options: Any?) -> String {
|
let formatOptions = ((options as? ISOFormatter.Options) ?? ISOFormatter.Options([.withInternetDateTime]))
|
let formatter = date.formatter(format: formatOptions.dateFormat) {
|
$0.locale = Locales.englishUnitedStatesComputer.toLocale() // fix for 12/24h
|
$0.timeZone = date.region.timeZone
|
$0.calendar = Calendars.gregorian.toCalendar()
|
}
|
return formatter.string(from: date.date)
|
}
|
|
}
|