Swift Why isn't my date object that's (Equatable) equal after converting it to a string and back?
The problem there is that Date
is stored as a FloatingPoint
value (timeIntervalSinceReferenceDate). There is fractional seconds being discarded there when converting your Date
to String
and back to Date
. Take a look at post.
Unit testing conformance to Decodable using a SingleValueDecodingContainer
I gave up trying to accomplish this without creating a wrapping type on the assumption that it's very hard to decode a string that isn't valid JSON to begin with ('1'
in my example).
So, I guess, the answer is: just create a wrapping type. ¯\_(ツ)_/¯
Swift DateFormatter doesn't convert date according to preset Dateformat
Here's an alternative solution to this question, which is suitable for a broader context of application. This solution is based on the suggestions of my mentor on the Exercism website.
My thanks also go to Leo Dabus who suggested that I add time zone information to the date formatter to avoid the effects of the implicitly adopted default local time zone.
The code is as follows:
import Foundation
struct Gigasecond {
public let gigasecond : Double = 1_000_000_000
private let rfc3339DateFormatter : DateFormatter = {
let myFormatter = DateFormatter()
myFormatter.timeZone = TimeZone(identifier: "UTC")
myFormatter.locale = Locale(identifier: "en_US_POSIX")
myFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
return myFormatter
}()
let description : String
init?(from dateString: String) {
let sourceDate = rfc3339DateFormatter.date(from: dateString)
guard let srcDate = sourceDate else {
description = ""
return
}
let destinationDate = Date(timeInterval: gigasecond, since: srcDate)
description = rfc3339DateFormatter.string(from: destinationDate)
}
}
Please let me know if there's anything I can do to improve the above code. Thank you ver much.
Swift Codable Decode Manually Optional Variable
Age is optional:
let age: String?
So try to decode in this way:
let age: String? = try values.decodeIfPresent(String.self, forKey: .age)
NSDateFormatter milliseconds bug
It seems that NSDateFormatter
works only with millisecond resolution, for the
following reasons:
By setting a breakpoint in
CFDateFormatterCreateDateFromString
, one can
see that this function is called fromdateFromString:
:(lldb) bt
* thread #1: tid = 0x26d03f, 0x018f47d0 CoreFoundation`CFDateFormatterCreateDateFromString, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
frame #0: 0x018f47d0 CoreFoundation`CFDateFormatterCreateDateFromString
frame #1: 0x0116e0ea Foundation`getObjectValue + 248
frame #2: 0x0116dfc7 Foundation`-[NSDateFormatter getObjectValue:forString:errorDescription:] + 206
frame #3: 0x0116879f Foundation`-[NSDateFormatter dateFromString:] + 71
* frame #4: 0x00002d56 foo`main(argc=1, argv=0xbfffee54) + 182 at main.mm:25CFDateFormatterCreateDateFromString()
is from
CFDateFormatter.c
which is open source. One can see that all calendrical calculations are made using the
ICU Calendar Classes.It is stated in calendar.h that
Calendar
usesUDate
which has a millisecond resolution:/**
* <code>Calendar</code> is an abstract base class for converting between
* a <code>UDate</code> object and a set of integer fields such as
* <code>YEAR</code>, <code>MONTH</code>, <code>DAY</code>, <code>HOUR</code>,
* and so on. (A <code>UDate</code> object represents a specific instant in
* time with millisecond precision. See UDate
* for information about the <code>UDate</code> class.)
* ...
How to configure DateFormatter to capture microseconds
Thanks to @MartinR for solving first half of my problem and to @ForestKunecke for giving me tips how to solve second half of the problem.
Based on their help I created ready to use solution which converts date from string and vice versa with microsecond precision:
public final class MicrosecondPrecisionDateFormatter: DateFormatter {
private let microsecondsPrefix = "."
override public init() {
super.init()
locale = Locale(identifier: "en_US_POSIX")
timeZone = TimeZone(secondsFromGMT: 0)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func string(from date: Date) -> String {
dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let components = calendar.dateComponents(Set([Calendar.Component.nanosecond]), from: date)
let nanosecondsInMicrosecond = Double(1000)
let microseconds = lrint(Double(components.nanosecond!) / nanosecondsInMicrosecond)
// Subtract nanoseconds from date to ensure string(from: Date) doesn't attempt faulty rounding.
let updatedDate = calendar.date(byAdding: .nanosecond, value: -(components.nanosecond!), to: date)!
let dateTimeString = super.string(from: updatedDate)
let string = String(format: "%@.%06ldZ",
dateTimeString,
microseconds)
return string
}
override public func date(from string: String) -> Date? {
dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
guard let microsecondsPrefixRange = string.range(of: microsecondsPrefix) else { return nil }
let microsecondsWithTimeZoneString = String(string.suffix(from: microsecondsPrefixRange.upperBound))
let nonDigitsCharacterSet = CharacterSet.decimalDigits.inverted
guard let timeZoneRangePrefixRange = microsecondsWithTimeZoneString.rangeOfCharacter(from: nonDigitsCharacterSet) else { return nil }
let microsecondsString = String(microsecondsWithTimeZoneString.prefix(upTo: timeZoneRangePrefixRange.lowerBound))
guard let microsecondsCount = Double(microsecondsString) else { return nil }
let dateStringExludingMicroseconds = string
.replacingOccurrences(of: microsecondsString, with: "")
.replacingOccurrences(of: microsecondsPrefix, with: "")
guard let date = super.date(from: dateStringExludingMicroseconds) else { return nil }
let microsecondsInSecond = Double(1000000)
let dateWithMicroseconds = date + microsecondsCount / microsecondsInSecond
return dateWithMicroseconds
}
}
Usage:
let formatter = MicrosecondPrecisionDateFormatter()
let date = Date(timeIntervalSince1970: 1490891661.074981)
let formattedString = formatter.string(from: date) // 2017-03-30T16:34:21.074981Z
Related Topics
Button State Activates on Wrong Cells
Tvos Textfield Transparent Background
Swift: Generic Overloads, Definition of "More Specialized"
Creating a Subclass of Skshapenode
Expression Pattern of Type 'String' Cannot Match Values of Type 'Nsstoryboardsegue.Identifier
Instance Member Cannot Be Used on Type | Closures
Searchbar Problem While Trying to Search Firestore and Reload the Tableview
Differences Generic Protocol Type Parameter VS Direct Protocol Type
Shows the Alert When Uitextfield's Are Full or Empty Swift
Inline Kvo of a Property in Another View Controller
Remove \\U{E2} Characters from String
Swift Callkit Sometimes Can't Activate Loudspeaker After Received Call (Only Incoming Call)
Data Ranged Subscribe Strange Behavior
Displaying State of an Async API Call in Swiftui
Hovering a Modelentity in Front of Arcamera