Formating date to sql date formate in Swift - swift

I have a method that converts my SQL date to "MM-dd-yyyy, HH:mm" in swift. I need to be able to convert this back to "yyyy-MM-dd'T'HH:mm:ss". This will be in server time and also eastern time zone.
Converting to "MM-dd-yyyy, HH:mm":
static func dateView(_ DateString: String) -> String {
var returnDate = ""
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let string = String(DateString)
if let date = dateFormatter.date(from: string) {
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short
returnDate = dateFormatter.string(from: date)
}
return returnDate
}
Trying to convert to "yyyy-MM-dd'T'HH:mm:ss":
static func dateToSQLDate(_ DateString: String) -> String {
var returnDate = ""
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "MM/dd/yy HH:mm"
let string = String(DateString)
if let date = dateFormatter.date(from: string) {
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
//dateFormatter.timeStyle = .short
returnDate = dateFormatter.string(from: date)
}
return returnDate
}
Example would be:
var date = "3/10/16, 10:00AM"
dateToSQLDate(date)
Expected Out: 2016-03-10T10:00:00
Any ideas to what I am doing wrong?

Your date format is wrong.
Compare the string "3/10/16, 10:00AM" with the date format "MM/dd/yy HH:mm". There are three issues:
The comma is missing
The AM/PM specifier a is missing
12 hour mode is hh
static func dateToSQLDate(_ string: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "MM/dd/yy, hh:mma"
guard let date = dateFormatter.date(from: string) else { return "" }
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
return dateFormatter.string(from: date)
}

Try this
static func dateToSQLDate(_ DateString: String) -> String {
var returnDate = ""
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let string = String(DateString)
if let date = dateFormatter.date(from: string) {
dateFormatter.dateFormat = "MM/dd/yy HH:mm"
returnDate = dateFormatter.string(from: date)
}
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
return returnDate
}

Others have answered your question, but a few observations:
It is probably prudent to always save your IS8601 timestamp strings in GMT/UTC/Zulu timezone (by setting the timeZone of your ISO8601 date string formatters to TimeZone(secondsFromGMT: 0), perhaps adding the X qualifier in the string to make it completely unambiguous. It shouldn’t be “server time zone” and probably shouldn’t be the device’s local time zone either.
Even better, I might suggest using the ISO8601DateFormatter instead, which sets timezones, locales, etc., automatically, making configuration less fragile.
I’d suggest instantiating two separate date formatters, one for the ISO 8601 date format (the one with the T in it), and another for presenting dates in your UI. While the ISO 8601 date formatter should use the en_US_POSIX locale (and if you use ISO8601DateFormatter, that’s taken care of for you), the UI date formatter should not (because you presumably want to show the date in the user’s device’s current locale).
I personally would suggest that your model objects don’t use a string for the date, but rather use a Date object, instead. So, use ISO8601DateFormatter when converting between Date and API strings, store the Date in your model object, and only use the DateFormatter with dateStyle and timeStyle when presenting the Date in the user interface.
So this means that rather than two string-to-string routines, you have string-to-date (and vice versa) for each of the two formatters.
Date formatters are notoriously computationally expensive to create. So you might consider creating these two formatters once, save them in ivars, and then you don’t have to repeatedly reinstantiate them.

Related

How can I show month and year in 12's timezone in swift?

func getMonYearDateFormat() -> String {
let dateFormatter = DateFormatter()//EEEE, d, MMM
dateFormatter.dateFormat = is24Hour() ? "yyyy-MM-dd' 'HH:mm:ss" : "yyyy-MM-dd' 'h:mm:ss a"
dateFormatter.timeZone = TimeZone.current
dateFormatter.locale = Locale.current
guard let oldDate = dateFormatter.date(from: self) else { return "" }
let convertDateFormatter = DateFormatter()
convertDateFormatter.dateFormat = "MMMM yyyy"
return convertDateFormatter.string(from: oldDate)
}
I show month and year as title in table view. I wrote this function to convert the date of the relevant card to month and year. If the device is in the 24-hour time zone, the function works properly. However, if the device is in the 12th time zone, it returns blank and I cannot see the month and year of the relevant card. I couldn't find the part that I wrote incorrectly in the function.
I've tested your code with 12 hour format and it works. The only thing I've. change is to use the 12 hour format without call is24Hour() because you did not provide that code. So I suspect the point of failure is the is24Hour() function.
As mentioned in the comments I would recommend you to investigate how you get this string and if possibly use the original Date object instead if it exists but below is a solution that I think handles the 12 vs 24h issue in an efficient way.
The logic is to first convert using 24h and if that returns nil then convert using the 12h format.
I have declared all formatters I use as static properties so they only get created one since creating a DateFormatter is expensive
extension String {
private static let formatter24: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd' 'HH:mm:ss"
return dateFormatter
}()
private static let formatter12: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd' 'h:mm:ss a"
return dateFormatter
}()
private static let formatterMonthYear: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM yyyy"
return dateFormatter
}()
func getMonYearDateFormat() -> String {
let date = Self.formatter24.date(from: self) ?? Self.formatter12.date(from: self)
guard let date else { return "" }
return Self.formatterMonthYear.string(from: date)
}
}

From UTC to local time doesn't consider daylight saving swift

I am using this function to convert UTC time to my local time stamp.
func UTCToLocal(date:String, timeZone: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "H:mm:ss"
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
let dt = dateFormatter.date(from: date)
dateFormatter.timeZone = TimeZone(identifier: timeZone)
dateFormatter.dateFormat = "h:mm a"
return dateFormatter.string(from: time)
}
But this doesn't check DST. So, I am getting one hour difference. Can anyone help me with some general solution for this problem?
Thanks to Leo who have figured out the issue. I have updated the functions as:
func UTCToLocal(date:String, timeZone: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let dt = dateFormatter.date(from: date)
dateFormatter.timeZone = TimeZone(identifier: timeZone)
dateFormatter.dateFormat = "dd-MM-yyyy hh:mm aa"
return dateFormatter.string(from: dt!)
}
Now the date string in function paramter have value in this format "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'". This solves the issue.
The issue here is the lack of the date. You are always passing only the time components without the day so the daylight savings time Will always be correspondent to January 1st 2001. If you need today’s date you need to set the date formatter defaultDate property to the startOfDay for today. Btw don’t forget to set the locale first to “en_US_POSIX” before setting the fixed date format.

How to convert UTC string to local date in swift?

I have following UTC like 2020-07-15T12:32:38+00:00. I actually need to convert this string to local date in phone taking into account a timezone. Here is my try that I see from other stackoverflow answers:
func UTCToLocal(date: String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
//I thought than below can help
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
return dateFormatter.date(from: date)
}
Another approach:
func UTCToLocal2(date: String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
/// I thought that specifying current time zone would help
dateFormatter.timeZone = TimeZone.current
return dateFormatter.date(from: date)
}
But none of approach works. My 2020-07-15T12:32:38+00:00 is not converted to date with my timezone. Where is my mistake?

DateFormatter returning nil for valid String when decoding JSON

I'm trying to decode a date of the format 2019-11-08T01:26:45+00:00 in Swift. Not sure if this is relevant, but it's a String taken from JSON. I've tried using ISO8601DateFormatter, setting the date format manually, etc., but nothing works.
(response.timestamp has the value "2019-11-12T21:37:39+00:00", taken from JSON.)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
dateFormatter.locale = Locale.current
self.timestamp = dateFormatter.date(from: response.timestamp)
When I build and run the application and set a breakpoint after initializing date, the value is always nil.
The key observation is to make sure you’re using en_US_POSIX as the locale, as outlined in Working With Fixed Format Date Representations:
let string = "2019-11-12T21:37:39+00:00"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
let date = dateFormatter.date(from: string)
It’s worth noting though, while this formatter will successfully interpret date strings with +00:00, if and when you convert dates back to strings, it will use the literal Z common in ISO 8601/RFC 3339 dates, e.g. "2019-11-12T21:37:39Z". If you really want your resulting strings (if you’re converting dates to strings at all) to use the +00:00 convention, then you’ll want to use xxxxx instead of ZZZZZ. You’ll also want to specify a timeZone for your formatter, too:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssxxxxx"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let string = dateFormatter.string(from: date) // "2019-11-12T21:37:39+00:00"
So use ZZZZZ if you want the standard "2019-11-12T21:37:39Z", but use xxxxx if you want "2019-11-12T21:37:39+00:00".
By the way, for more information on all the different permutation of Z, ZZZZZ, X, xxxxx, etc., see the time zone section of table at Date Format Patterns.
When I build and run the application and set a breakpoint after initializing date, the value is always nil.
By the way, always print (or PO in lldb) the value. Sometimes the inspector window in Xcode gets out of sync and shows nil when there really is some value there.
As jms said, if you’re using JSONDecoder, you probably shouldn’t use date(from:) or string(from:) at all. Make your property a Date type, and then give your decoder a dateDecodingStrategy:
let json = """
{"date" : "2019-11-12T21:37:39+00:00"}
"""
let data = json.data(using: .utf8)!
struct ResponseObject: Codable {
let date: Date
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
do {
let object = try decoder.decode(ResponseObject.self, from: data)
} catch {
print(error)
}
func convertDateString(dateString:String,dateFormat:String = "") -> String {
if dateString.isEmpty {
return ""
}
let dateFormatter = DateFormatter()
let tempLocale = dateFormatter.locale // save locale temporarily
dateFormatter.locale = Locale(identifier: "en_US_POSIX") // set locale to reliable US_POSIX en_US_POSIX
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss+s:s"
let date = dateFormatter.date(from: dateString)!
dateFormatter.dateFormat = dateFormat.isEmpty ? "dd-MM-yyyy" : dateFormat
dateFormatter.locale = tempLocale // reset the locale
let dateStr = dateFormatter.string(from: date)
return dateStr
}
usage - your api response date in string format
let time1 = "2019-11-08T01:26:45+00:00"
print(convertDateString(dateString: time1,dateFormat: "EEEE, MMM d, yyyy"))
OUTPUT --->>>
Friday, Nov 8, 2019

Date formatter crash in swift

Please help me to modify this part of code,
if let dateVaule = UserDefaults().value(forKey: SplashSeenDate){
let dateStr = dateVaule as! String
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone.ReferenceType.local
let date = dateFormatter.date(from:dateStr)
if Global.sharedInstance.Splashs != nil && Global.sharedInstance.Splashs?.count != 0{
for (i,splash) in (Global.sharedInstance.Splashs?.enumerated().reversed())!{
if !checkTimeStamp(date: splash.create_timestamp,StoredDate: date!){
Global.sharedInstance.Splashs?.remove(at: i)
}
}
print(Global.sharedInstance.Splashs?.count ?? "-1")
}
}
Honestly speaking, i am very confuse with the date formatter.
when this project launch, it occured crash at
if !checkTimeStamp(date: splash.create_timestamp,StoredDate: date!){
However, I could not replicate this crash... **Only a few user with iOS10 occured this crash.**Please kindly advise, Thanks a lot!
func checkTimeStamp(date: String!,StoredDate: Date) -> Bool {
let dateFormatter: DateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.locale = Locale(identifier:"en_US_POSIX")
let datecomponents = dateFormatter.date(from: date)
if (datecomponents! >= StoredDate) {
return true
} else {
return false
}
}
You are converting String to Date in two different ways:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone.ReferenceType.local //###Why don't you use `TimeZone.current`?
let date = dateFormatter.date(from:dateStr)
And in checkTimeStamp(date: String!,StoredDate: Date) -> Bool:
let dateFormatter: DateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.locale = Locale(identifier:"en_US_POSIX")
let datecomponents = dateFormatter.date(from: date)
In your case, lacking this line is critical for your first conversion:
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
You can find many articles discussing about DateFormatter (or NSDateFormatter in old articles) returning nil.
When you use DateFormatter with fixed format like "yyyy-MM-dd HH:mm:ss", you must set locale to Locale(identifier: "en_US_POSIX").
Maybe you'd better define one conversion method (or computed property) to convert String to Date which fits for your app's requirements, and use it everywhere you need String to Date conversion:
extension String {
var toAppDate: Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone.current
dateFormatter.locale = Locale(identifier: "en_US_POSIX") //### <- Do not miss.
return dateFormatter.date(from: self)
}
}
Generally, your code uses too much forced-unwrapping or forced-casting. You can re-write it without using any forced-something:
if
let dateStr = UserDefaults().string(forKey: SplashSeenDate),
let date = dateStr.toAppDate,
let splashes = Global.sharedInstance.Splashs, !splashes.isEmpty
{
//### In most cases, `filter` is faster than repeated `remove`.
Global.sharedInstance.Splashs = splashes.filter {
checkTimeStamp(date: $0.create_timestamp, storedDate: date)
}
print(Global.sharedInstance.Splashs?.count ?? "-1")
}
(Assuming create_timestamp is not Optional, and renamed parameter label StoredDate: to storedDate:.)
Seems like splash.create_timestamp is getting nil and when you are passing in this function checkTimeStamp. It is getting crash. You can prevent the crash by changing date parameter inside checkTimeStamp function as optional and use guard statement before passing inside the function.