New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unable to decode date #3
Comments
I was able to fix the issue for me by changing class OptionalFractionalSecondsDateFormatter: DateFormatter {
static let withoutSeconds: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "UTC")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" //without milliseconds
return formatter
}()
func setup() {
self.calendar = Calendar(identifier: .iso8601)
self.locale = Locale(identifier: "en_US_POSIX")
self.timeZone = TimeZone(identifier: "UTC")
self.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SZZZZZ" //with milliseconds
}
...
Basically the timezone date formatter symbol was wrong. I am opening a PR to fix it, I am not sure if my database has the wrong format since I am not a PostgreSQL expert, but in case it's a bug in |
Hey @BalestraPatrick thank you for contributing, but unfortunately I can't merge your pull request because it will broke standard postgres dates decoding. FluentQuery allows you to use custom date formatter while decoding, e.g. .decode(MyModel.self, dateDecodingStrategy: .formatted(<YOUR_CUSTOM_DATE_FORMATTER>)) So in your case you should implement your own custom date formatter e.g. by copying class BPDateFormatter: DateFormatter {
static let withoutSeconds: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "UTC")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" //without milliseconds
return formatter
}()
func setup() {
self.calendar = Calendar(identifier: .iso8601)
self.locale = Locale(identifier: "en_US_POSIX")
self.timeZone = TimeZone(identifier: "UTC")
self.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SZZZZZ" //with milliseconds
}
override init() {
super.init()
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func date(from string: String) -> Date? {
if let result = super.date(from: string) {
return result
}
return BPDateFormatter(from: string)
}
} so now you can use it like this .decode(MyModel.self, dateDecodingStrategy: .formatted(BPDateFormatter())) Or even you could create an extension to simplify decoding and force to use BPDateFormatter everywhere import Foundation
import FluentPostgreSQL
import PostgreSQL
import FluentQuery
class BPDateFormatter: DateFormatter {}
typealias PostgresRow = [PostgreSQL.PostgreSQLColumn: PostgreSQL.PostgreSQLData]
extension EventLoopFuture where T == [PostgresRow] {
func bpDecode<T>(_ to: T.Type) throws -> EventLoopFuture<[T]> where T: Decodable {
return map { return try $0.decode(T.self, dateDecodingStrategy: .formatted(BPDateFormatter())) }
}
}
extension Array where Element == PostgresRow {
func bpDecode<T>(_ to: T.Type) throws -> [T] where T: Decodable {
return try map { try $0.decode(T.self, dateDecodingStrategy: .formatted(BPDateFormatter())) }
}
}
extension Dictionary where Key == PostgreSQL.PostgreSQLColumn, Value == PostgreSQL.PostgreSQLData {
func bpDecode<T>(_ to: [T.Type]) throws -> T where T: Decodable {
return try decode(T.self, dateDecodingStrategy: .formatted(BPDateFormatter()))
}
func bpDecode<T>(_ to: T.Type) throws -> T where T: Decodable {
let convertedRowValues = map { (QueryField(name: $0.name), $1) }
let convertedRow = Dictionary<QueryField, PostgreSQL.PostgreSQLData>(uniqueKeysWithValues: convertedRowValues)
return try FQDataDecoder(PostgreSQLDatabase.self, entity: nil, dateDecodingStrategy: .formatted(BPDateFormatter())).decode(to, from: convertedRow)
}
} with that extension you will be able to call 💭Looks like I should add this example to readme! 🎉 Thank you again for spotting that! 👍 |
That makes sense! I remember seeing a way to pass in my |
It looks like the only difference is that you're using timezones. |
@BalestraPatrick ok, I found the way! |
By the way this is my playground code. class OptionalFractionalSecondsDateFormatter: DateFormatter {
func setup() {
self.calendar = Calendar(identifier: .iso8601)
self.locale = Locale(identifier: "en_US_POSIX")
self.timeZone = TimeZone(identifier: "UTC")
//with milliseconds and without timezone
self.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSSSS'ZZZZZ"
}
override init() {
super.init()
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func date(from string: String) -> Date? {
//with milliseconds and without timezone
if let result = super.date(from: string) {
return result
}
//without milliseconds and without timezone
dateFormat = "yyyy-MM-dd HH:mm:ss'ZZZZZ"
if let result = super.date(from: string) {
return result
}
//with milliseconds and timezone
dateFormat = "yyyy-MM-dd HH:mm:ss.SSSSSSZZZZZ"
if let result = super.date(from: string) {
return result
}
//without milliseconds and with timezone
dateFormat = "yyyy-MM-dd HH:mm:ssZZZZZ"
return super.date(from: string)
}
}
let formatter = OptionalFractionalSecondsDateFormatter()
print("date1: \(formatter.date(from: "2018-07-12 13:46:05.846234"))")
print("date2: \(formatter.date(from: "2018-07-12 13:56:00"))")
print("date3: \(formatter.date(from: "2017-09-29 01:20:47.307779+01"))")
print("date4: \(formatter.date(from: "2017-09-29 01:20:47.307779+0130"))")
print("date5: \(formatter.date(from: "2017-09-29 02:00:00+01"))")
print("date6: \(formatter.date(from: "2017-09-29 02:00:00+0130"))") |
@MihaelIsaev The new release seems to work perfectly in my project! Thank you very much 👍 |
…t dates with timezone
Hey there! In my PostgreSQL database I have two types of dates in the same column:
2017-12-08 23:20:47.307779+01
2017-09-29 02:00:00+02
One looks like to have milliseconds while the other one doesn't. Trying to decode this table from the database results in:
FluentError.decodingError: The data couldn’t be read because it isn’t in the correct format. (Accessory nested model)
I tried digging into the
FluentQuery
codebase and I figured out that the following method returns nil:It seems that it's trying to decode the date with the
withoutSeconds
formatter and is failing. Do you know what's going on? Are you trying to decode the dates with both milliseconds and without to make sure it can be decoded correctly?Thanks a lot!
The text was updated successfully, but these errors were encountered: