iOS - NSDateFormatter dateFromString returns nil only in one case
After a few comments, it has been determined that the code in the question is being run with the locale of es_PE
. This is the country of Peru. The date in question is January 1, 1990. By default, NSDateFormatter
uses the local timezone and when parsing date strings that have no time, midnight is assumed.
In Peru, in the year 1990, day light savings began at midnight, January 1st, 1990. This means that clocks went from December 31, 1989 at 11:59:59pm straight to January 1, 1990 at 1:00:00am. There was no midnight on January 1, 1990.
This is why the attempt to convert the string 01-01-1990
failed for this user. There was no midnight for this date in Peru (and possibly a few other locales, if any, that had day light saving start at the same time). Most people testing this code would claim it works just fine since most people testing this code don't live in Peru.
I found a useful website with helpful information. See http://www.timeanddate.com/time/change/peru/lima?year=1990 for details about Peru and day light savings time. Note that in 1989 and 1991, Peru did not use day light savings time.
Convert string with unknown format (any format) to date
Xcode 11.4 • Swift 5.2 or later
You can use NSDataDetector as follow:
extension String {
var nsString: NSString { self as NSString }
var length: Int { nsString.length }
var nsRange: NSRange { .init(location: 0, length: length) }
var detectDates: [Date]? {
try? NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
.matches(in: self, range: nsRange)
.compactMap(\.date)
}
}
extension Collection where Iterator.Element == String {
var dates: [Date] { compactMap(\.detectDates).flatMap{$0}
}
}
Testing:
let dateStrings = ["January 3, 1966","Jan 3, 1966", "3 Jan 1966"]
for dateString in dateStrings {
if let dateDetected = dateString.detectDates?.first {
print(dateDetected)
// 1966-01-03 14:00:00 +0000
// 1966-01-03 14:00:00 +0000
// 1966-01-03 14:00:00 +0000
}
}
let dateStrings = ["January 3, 1966","Jan 3, 1966", "3 Jan 1966"]
for date in dateStrings.dates {
print(date)
// 1966-01-03 14:00:00 +0000
// 1966-01-03 14:00:00 +0000
// 1966-01-03 14:00:00 +0000
}
How to fix date formatter after adding 30 days to date
There are many issues here and lots of unnecessary code.
- Use the special en_US_POSIX locale when parsing a date string using a fixed format.
- You don't need
stringFromThityDaysBefore
. YourthirtyDaysBeforeToday
is already aDate
30 days afterendingDate
. - Don't set both the
dateFormat
anddate/timeStyle
of a date formatter. Only set one or the other. - At the end, you want a new string representing the new date that is 30 days later. Your code fails because you convert a
Date
to aString
using string interpolation and then you try to parse that string with your 2nd date formatter.
Here's the fixed code:
let timeStarting = "10:50"
let dateStarting = "03/10/2016"
let dateFormater = DateFormatter()
dateFormater.dateFormat = "dd/MM/yyyy HH:mm"
dateFormater.locale = Locale(identifier: "en_US_POSIX")
let timeAndDate = "\(dateStarting) \(timeStarting)"
let endingDate = dateFormater.date(from: timeAndDate)!
//ADD 30 DAYS TO ENDING DATE
let numberOfDays = 30
let thirtyDaysAfter = Calendar.current.date(byAdding: .day, value: numberOfDays, to: endingDate)!
//FORMAT AGAIN TO SHOW THE DATE IN AS (EX.: 13.10.2016)
let dateFormater2 = DateFormatter()
dateFormater2.dateFormat = "dd.MM.yy"
let newDateString = dateFormater2.string(from: thirtyDaysAfter)
The gives the result of 02.11.16
for newDateString
.
One last thing to consider - don't use a dateFormat
on the 2nd date formatter. Use date/timeStyle
. It's always best to use a style for any date you wish to display to the user. This ensures it is formatted best for the user's locale.
Swift : How to convert String to string with time format?
You need 2 NSDateFormatter
s, one for dateFromString
, one for stringFromDate
let inFormatter = NSDateFormatter()
inFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
inFormatter.dateFormat = "HH:mm"
let outFormatter = NSDateFormatter()
outFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
outFormatter.dateFormat = "hh:mm"
let inStr = "16:50"
let date = inFormatter.dateFromString(inStr)!
let outStr = outFormatter.stringFromDate(date)
println(outStr) // -> outputs 04:50
Swift 5
let inFormatter = DateFormatter()
inFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
inFormatter.dateFormat = "hh:mm:ssa"
let outFormatter = DateFormatter()
outFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
outFormatter.dateFormat = "hh:mm a"
let inStr = "16:50"
let datess = inFormatter.date(from: inStr)!
let outStr = outFormatter.string(from: datess)
print(outstr)
Related Topics
Is There a Neat Way to Represent a Fraction as an Attributed String
Swift 3 Errors with Additional Data
Node.Physicsbody.Joints Downcasting Error
How to Pass Arguments into a Function with Completion Swift
What Are 3 Items in a Swift Method Parameter For
How to Sort an Array of Posts by Their Elements
Ambiguous Method Overload with Closures in Swift, But Only When Closure Returns a Value
How to Properly Test Against Certain Values in Nseventmodifierflags via Swift
Where to Place App Delegate Code in App.Swift File
Performing a Completion Handler Before App Launches
How to Get All Characters of the Font with Ctfontcopycharacterset() in Swift
Presenting a Uiviewcontroller from Skscene Shows Black Screen
Nsmanagedobject Subclasses Duplicate Declaration
Contextual Member Has No Associated Value in Swift 3
With Data (Not Nsdata), in Fact How Actually Do You Make a Utf8 Version of a Jpeg
Label Showing Top of Screen Instead of Being on the Inputaccessoryview
Value of Optional Type 'Nsurl' Not Unwrapped; Did You Mean to Use '!' or ''