Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Type-safe date format strings using string interpolation. Requires Swift 5.0.
// Type-safe date format strings using string interpolation
// Requires Swift 5.0.
import Foundation
enum DateFormatComponent {
case era(AlphaStyle)
case year(MinimumDigits)
case yearForWeekOfYear(MinimumDigits)
case quarter(AlphaNumericStyle)
case month(AlphaNumericStyle)
case day(MinimumDigits)
case dayOfYear(MinimumDigits)
case dayOfWeekInMonth
case amPM(AlphaStyle)
case hour12(MinimumDigits)
case hour24(MinimumDigits)
case minute(MinimumDigits)
case second(MinimumDigits)
case fractionalSecond(NumberOfDigits)
case timeZone(AlphaStyle)
struct MinimumDigits: ExpressibleByIntegerLiteral {
var value: Int
init(integerLiteral value: Int) {
self.value = value
}
}
struct NumberOfDigits: ExpressibleByIntegerLiteral {
var value: Int
init(integerLiteral value: Int) {
self.value = value
}
}
enum AlphaStyle: Int {
case abbreviated = 1
case long = 4
case narrow = 5
}
enum AlphaNumericStyle: Int {
/// One digit
case one = 1
/// Two digits
case two = 2
case abbreviated = 3
case full = 4
case narrow = 5
}
var formatString: String {
switch self {
case let .era(style): return String(repeating: "G", count: style.rawValue)
case let .year(digits): return String(repeating: "y", count: digits.value)
case let .yearForWeekOfYear(digits): return String(repeating: "Y", count: digits.value)
case let .quarter(style): return String(repeating: "Q", count: style.rawValue)
case let .month(style): return String(repeating: "M", count: style.rawValue)
case let .day(digits): return String(repeating: "d", count: digits.value)
case let .dayOfYear(digits): return String(repeating: "D", count: digits.value)
case .dayOfWeekInMonth: return "F"
case let .amPM(style): return String(repeating: "a", count: style.rawValue)
case let .hour12(digits): return String(repeating: "h", count: digits.value)
case let .hour24(digits): return String(repeating: "H", count: digits.value)
case let .minute(digits): return String(repeating: "m", count: digits.value)
case let .second(digits): return String(repeating: "s", count: digits.value)
case let .fractionalSecond(digits): return String(repeating: "S", count: digits.value)
case let .timeZone(style): return String(repeating: "Z", count: style.rawValue)
}
}
}
struct DateFormat {
var value: String
}
extension DateFormat: ExpressibleByStringInterpolation {
init(stringLiteral value: String) {
self.init(value: value)
}
init(stringInterpolation: StringInterpolation) {
self.init(value: stringInterpolation.value)
}
struct StringInterpolation: StringInterpolationProtocol {
var value: String = ""
init(literalCapacity: Int, interpolationCount: Int) {
value.reserveCapacity(literalCapacity)
}
mutating func appendLiteral(_ literal: String) {
guard !literal.isEmpty else { return }
value.append("'\(literal)'")
}
mutating func appendInterpolation(_ interpolation: DateFormatComponent) {
value.append(interpolation.formatString)
}
}
}
// Example:
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
let dateFormat: DateFormat = "\(.year(1))-\(.month(.narrow))-\(.day(2))"
dateFormat.value
formatter.dateFormat = dateFormat.value
formatter.string(from: Date())
let iso8601: DateFormat = """
\(.year(1))-\(.month(.two))-\(.day(2))\
T\
\(.hour24(2)):\(.minute(2)):\(.second(2))\
\(.timeZone(.narrow))
"""
iso8601.value
formatter.dateFormat = iso8601.value
formatter.string(from: Date())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.