Skip to content

Commit

Permalink
#9 Add Support for Custom Formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
JARMourato committed Sep 1, 2021
2 parents f43b0ec + 6329f36 commit af407f5
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -170,7 +170,7 @@ print(dates.rfc3339Date.description) // Prints "1996-12-20 00:39:57 +0000"
print(dates.timestamp.description) // Prints "2001-01-01 00:00:00 +0000"
````

Note that there's no built-in support for ISO8601 dates with precision greater than millisecond (e.g. microsecond or nanosecond), because Apple doesn't officially supports such precision natively, yet. Should you feel the necessity to have those, PRs are always welcome!
Note that there's no built-in support for ISO8601 dates with precision greater than millisecond (e.g. microsecond or nanosecond), because Apple doesn't officially supports such precision natively, yet. Should you feel the necessity to have those, or any other custom date formatter, you can implement your own `DateConvertible` and use `.custom(dateConvertible)` DateCodingStrategy. If you think your use case should make its way into the official library, PRs are always welcome!

## Advanced Usage

Expand Down
10 changes: 10 additions & 0 deletions Sources/Wrappers/CodableDate.swift
Expand Up @@ -84,6 +84,9 @@ public enum DateCodingStrategy {
case rfc3339
/// Time interval since 1970.
case timestamp
/// A custom date parser to be used, instead of a string formatter.
/// - Note: it's strongly advised that your implementation of DateConvertible converts to and from Date in a lossless manner, although not required.
case custom(DateConvertible)

public func date(from value: String) -> Date? {
switch self {
Expand All @@ -95,6 +98,7 @@ public enum DateCodingStrategy {
case .timestamp:
guard let timestamp = Double(value) else { return nil }
return Date(timeIntervalSince1970: timestamp)
case let .custom(parser): return parser.date(from: value)
}
}

Expand All @@ -106,6 +110,7 @@ public enum DateCodingStrategy {
case .rfc2822: return DateCodingStrategy.rfc2822Formatter.string(from: date)
case .rfc3339: return DateCodingStrategy.rfc3339Formatter.string(from: date)
case .timestamp: return "\(date.timeIntervalSince1970)"
case let .custom(parser): return parser.string(from: date)
}
}

Expand Down Expand Up @@ -134,6 +139,11 @@ public enum DateCodingStrategy {
}
}

public protocol DateConvertible {
func date(from value: String) -> Date?
func string(from date: Date) -> String
}

// MARK: Equatable Conformance

extension CodableDate: Equatable where T: Equatable {
Expand Down
14 changes: 14 additions & 0 deletions Tests/KodableTests.swift
Expand Up @@ -602,6 +602,16 @@ final class KodableTests: XCTestCase {
}

func testCodableDate() throws {
struct MyDateParser: DateConvertible {
func date(from _: String) -> Date? {
Date(timeIntervalSince1970: 123)
}

func string(from _: Date) -> String {
"Kodable"
}
}

struct Dates: Kodable {
@CodableDate(decoding: .enforceType) var iso8601: Date
@CodableDate("iso8601") var isoDate: Date?
Expand All @@ -611,6 +621,7 @@ final class KodableTests: XCTestCase {
@CodableDate(.rfc3339, "rfc3339") var rfc3339Date: Date
@CodableDate(.timestamp, "timestamp", decoding: .lossless) var nonOptionalTimestamp: Date
@CodableDate(.timestamp, "timestamp", decoding: .lossless) var timestamp: Date?
@CodableDate(.custom(MyDateParser()), "custom_date") var customDate: Date?
@CodableDate var optionalDate: Date?

@CodableDate(.timestamp, "timestamp_non_existent", default: KodableTests.testDate)
Expand Down Expand Up @@ -645,6 +656,7 @@ final class KodableTests: XCTestCase {
XCTAssertEqual(decoded.rfc3339Date.description, "1996-12-20 00:39:57 +0000")
XCTAssertEqual(decoded.nonOptionalTimestamp.description, "2001-01-01 00:00:00 +0000")
XCTAssertEqual(decoded.timestamp?.description, "2001-01-01 00:00:00 +0000")
XCTAssertEqual(decoded.customDate?.description, "1970-01-01 00:02:03 +0000")
XCTAssertEqual(decoded.defaultTimestamp.description, KodableTests.testDate.description)
XCTAssertEqual(decoded.optionalDate, nil)
XCTAssertEqual(decoded.ignoredDate, nil)
Expand All @@ -659,6 +671,7 @@ final class KodableTests: XCTestCase {
XCTAssertEqual(newObject.rfc3339Date.description, "1996-12-20 00:39:57 +0000")
XCTAssertEqual(newObject.nonOptionalTimestamp.description, "2001-01-01 00:00:00 +0000")
XCTAssertEqual(newObject.timestamp?.description, "2001-01-01 00:00:00 +0000")
XCTAssertEqual(newObject.customDate?.description, "1970-01-01 00:02:03 +0000")
XCTAssertEqual(newObject.defaultTimestamp.description, KodableTests.testDate.description)
XCTAssertEqual(newObject.optionalDate, nil)
XCTAssertEqual(newObject.ignoredDate, nil)
Expand Down Expand Up @@ -784,6 +797,7 @@ final class KodableTests: XCTestCase {
"rfc2822": "Thu, 19 Dec 1996 16:39:57 GMT",
"rfc3339": "1996-12-19T16:39:57-08:00",
"timestamp": 978_307_200.0,
"custom_date": "lorem ipsum",
"id": 2_623_488,
"title": " Create New Project ",
"empty": " ",
Expand Down

0 comments on commit af407f5

Please sign in to comment.