Having some problems parsing date. I have an array of supported formats and once I receive the date (string) from API, I try to parse it iterating through the formats until I get a valid NSDate object.
A snippet from Xcode Playground --
let dateString = "02/06/1987" // --> want to parse into this Feb 6, not Jun 2
let dateFormatIncorrect = "dd.MM.yyyy"
let dateFormatCorrect = "MM/dd/yyyy"
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = dateFormatIncorrect
let date = dateFormatter.dateFromString(dateString)! // "Jun 2, 1987, 12:00 AM"
dateFormatter.dateFormat = dateFormatCorrect
let date2 = dateFormatter.dateFromString(dateString)! // "Feb 6, 1987, 12:00 AM"
Why does it parse the date even though the format is clearly incorrect for a given string? Could not find anything in the docs regarding date formatter ignoring separators.
I realise the proper solution would be to have a fixed format returned from API but was wondering what is happening here?
Thanks.
It seems that NSDateFormatter is extremely lenient when parsing a date string.
Unfortunately, I could not find a reference for this, but even with
dateFormatIncorrect = "'aaa'dd'bbb'MM'ccc'yyyy'ddd'"
the date string "02/06/1987" is successfully parsed. There is a lenient property,
but that is false by default, and setting it explicitly makes no difference.
As a workaround, you could convert the parsed date back to a string, and only if
the result is equal to the original string, the date is accepted:
extension NSDateFormatter {
func checkedDateFromString(string : String) -> NSDate? {
if let date = self.dateFromString(string) {
if self.stringFromDate(date) == string {
return date
}
}
return nil
}
}
Using this custom extension,
dateFormatter.checkedDateFromString(dateString)
returns nil for the incorrect date format.
Generally, if you work with fixed date formats, you should also set the locale
to "en_US_POSIX"
dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
(see What is the best way to deal with the NSDateFormatter locale "feechur"?). However, this makes no difference for this
particular problem.
Update for Swift 3:
extension DateFormatter {
func checkedDate(from: String) -> Date? {
if let date = date(from: from), string(from: date) == from {
return date
}
return nil
}
}
This could be related to the fact that NSDateFormatter will anyways respects the users settings when using fixed formats
Although in principle a format string specifies a fixed format, by
default NSDateFormatter still takes the user’s preferences (including
the locale setting) into account
So may be the locale defined in your preference uses '/' for separator and satisfies the 'incorrect format'. Even if that is not the case, apple noted in several places that NSDateFormatter might not act consistently. So try setting a fixed locale as below and see if that helps
NSLocale *locale = [[NSLocale alloc]
initWithLocaleIdentifier:#"en_US_POSIX"];
[dateFormatter setLocale:locale];
See these links for detail: apple tech note . Note directly related to separators, but that could be related.
Had a similar issue:
NSDateFormatter returns date object from invalid input
Filed a bug report at Apple.
Result: Will not be fixed, as the change could break working code, in addition it is more error tolerant and thus provides some kind of convenience.
Please know that our engineering team has determined that this issue
behaves as intended based on the information provided.
It appears that ICU’s udat_parseCalendar() is very lenient and still
is able to parse even if the string doesn’t exactly match the format.
We understand preferring that the formatter return nil in these cases
but (1) there’s no easy way for us to know that the input string
doesn’t match the format since ICU allows it and doesn’t throw an
error and (2) suddenly returning nil in these cases would almost
certainly be a bincompat issue.
In my case I had the option to either modify the unit tests and be more tolerant in case of invalid input or have an additional checkup (based on the recommended approach, which is the accepted answer for the post) whether the resulting NSDate's string fits to the input string.
Related
One day, the app worked. The next day I updated to Xcode 11 and now the app crashes with "unexpectedly found nil" on line 27 (when executing line 15) in the picture.
I asked my co-worker who doesn't yet have Xcode 11, and his doesn't crash. we are on the same branch/commit...everything.
Any advice? any way around this?
My code:
// ticket.timeOrdered == "2019-10-03 22:54:57 +0000"
let ticketDate = ticket.timeOrdered.asCrazyDate.timeIntervalSince1970
extension String {
var asCrazyDate: Date {
let dateFormatterGet = DateFormatter()
dateFormatterGet.dateFormat = "yyyy-MM-dd HH:mm:ss +zzzz"
dateFormatterGet.timeZone = .current
return dateFormatterGet.date(from: self)!
}
}
The date format string is incorrect. The +zzzz is not an acceptable format. See the timezone related sections of the table in Date Format Patterns. The + shouldn’t be there. And zzzz is for long descriptions of the time zone (e.g. “Pacific Daylight Time”). You can verify this by using the same formatter to build a string from Date() and you’ll see that it’s not resulting in the +0000 like you might have expected.
The latest SDK’s date formatter is no longer as forgiving regarding these sorts of format string errors as the previous versions were. But rather than reverting your Xcode version, you really should just fix that date format string. For example, you could use Z instead of +zzzz, which will correctly interpret the +0000 (or whatever) as the time zone portion of the string.
A few other suggestions, if you don’t mind:
You don’t need asCrazyDate in this example. There’s no point in getting a date, using string interpolation to build the string representation, and then using a formatter to convert the string back to a date (which you originally started with). You can just use the Date directly:
func getDate() -> TimeInterval {
return Date().timeIntervalSince1970
}
Date formatters are notoriously computationally intensive to create, and if you’re using this computed property a lot, that can really affect performance. It’s better to instantiate date formatters once, if possible.
If you’re trying to build some invariant date string for some reason, it’s better to use something like ISO8601DateFormatter. So don’t build your date strings using string interpolation, and don’t build your own formatter.
let formatter = ISO8601DateFormatter()
let now = Date()
let string = formatter.string(from: now) // not "\(now)"
let date = formatter.date(from: string)
print(now, string, date)
If you’re stuck with this date format (perhaps you’ve already stored dates using this string format), you can use the custom dateFormat string, if you must. But as Technical Q&A 1480 suggests, you might want to set the locale (and I’d suggest setting the timeZone, too, so your date strings are comparable).
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
This question already has an answer here:
DateFormatter doesn't return date for "HH:mm:ss"
(1 answer)
Closed 5 years ago.
Within our iphone app we parse a date gotten from an api call. The date returns correctly and is a valid date. Now only on some devices does it crash with the error of unexpectedly found nil while unwrapping an Optional value. Here is the code in question:
//formatDate(date: date, format: FullDateFormat)
class func formatDate(date: String, format: String)->String{
if date.characters.count == 0 {return "" }
let formatter = DateFormatter()
formatter.dateFormat = Constants.FullDateFormat
let nsDate = formatter.date(from: date)
formatter.dateFormat = format
return formatter.string(from: nsDate!)
}
nsDate is not being formatted as it is nil.
The Constants.FullDateFormat is a static string defined as "M/d/yyyy h:mm:ss a" as the date will always come in this format
The call to the class function will look like this
let newDate = Helpers.formatDate(date: "9/27/2017 9:26:51 AM", format: "h:mm a")
Some devices crash while majority don't. If we don't use the class function the app works correctly. I don't see any cause for it so if anyone sees why this may be happening and a possible solution, please let me know.
This may be a duplicate but didn't show up in any of the searches I performed. Thanks to community they pointed to another similar questions with answers already at stackoverflow. My apologies if this is a duplicate.
It is a matter of locales. DateFormatter is dependent on the device's current location settings, including date and time.
You can ensure that the formatter's locale is always static by setting its locale to en_US_POSIX:
formatter.locale = Locale(identifier: "en_US_POSIX")
See Apple's link for more details:
https://developer.apple.com/documentation/foundation/nsdateformatter
I'm new to swift and would appreciate some help with string manipulation. I'm trying to get the current date off NSDate and put it into a text field for an app I'm working on. I tried to use NSDateFormatter to put the ios system date into the international form or dd-MM-yyyy, but I just keep getting all these errors and nothing works. I could use the American date format, I just really need it to work. I don't really know swift that much, but I know that other tutorials I tried to follow on stack overflow directed me to put some code in the view controller using NSDate. I worked on some other tutorials and tried to make them do what I needed to and this is the result. It used to create a date and timestamp but I tried to cut the parts out that deal with time. I think I just made it worse.
func convertDateFormatter(date: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
guard let date = dateFormatter.date(from: date) else {
assert(false, "no date from string")
return ""
}
dateFormatter.dateFormat = "dd-MM-yyyy"
let timeStamp = dateFormatter.string(from: date)
return timeStamp
}
My version of swift doesn't recognize NSDate, it wants to change it to just Date, I don't know how it affects how I am supposed to go about doing this. I changed it to just Date in the code and it still doesn't work.
In addition, yesterday my mobile apps teacher and I tried to equate a custom variable and the text field, but it does not work.
var UIDateStamp = UITextField().self
I could be wording my search incorrectly but I have searched this same query all the different ways I could come up with, but every solution I have tried thus far gives me a lot of errors that my coding class and I cannot solve.
I would greatly appreciate help with this issue.
If you need to system date they you need create function without parameter.
Swift 3
func convertDateFormatter() -> String {
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy" // change format as per needs
let result = formatter.string(from: date)
return result
}
If you want a date format depending on the current locale use the timeStyle and dateStyle properties.
This code – as computed property – returns M/d/yy for the US locale
var timeStamp : String {
let formatter = DateFormatter()
formatter.timeStyle = .none
formatter.dateStyle = .short
return formatter.string(from: Date())
}
A date style medium returns MMM d, yyyy
I developed an application in which I use a singleton for keeping a single instance of an NSDateFormatter.
My date formatter is initialized as below:
timeDateFormatter = NSDateFormatter()
timeDateFormatter.locale = NSLocale(localeIdentifier:EN_US_POSIX_LocaleIdentifier)
timeDateFormatter.dateFormat = StringDateType.DetailSigningHour.rawValue
let EN_US_POSIX_LocaleIdentifier = "en_US_POSIX"
With 24-Hour Time turned off the application runs as it should, but when going to Settings-->General-->Date&Time and turning on 24-Hour Time, then going and tapping on the app icon, the app tries to come up and then immediately exit.
I read that this may be an unknown issue for Apple.
Can you help me with some more information about this?
Update
When the system time was changed from 12-hour format to 24-hour format, my date formatter was messed up.
The good part is that the system is sending a notification (NSCurrentLocaleDidChangeNotification) letting you know that the locale has changed. All what I have done was to add an observer for this notification and to re-initialize my date formatter.
Setting the locale to en_US_POSIX, you force the 12-hour mode, regardless of the user's 24/12-hour mode setting.
I'm going to assume that you're force-unwrapping the result of timeDateFormatter.dateFromString with a wrong date format.
If you do something like this:
let timeDateFormatter = NSDateFormatter()
timeDateFormatter.locale = NSLocale(localeIdentifier:"en_US_POSIX")
timeDateFormatter.dateFormat = "hh:mm"
And force-unwrap the result:
let d = timeDateFormatter.dateFromString("11:42")!
You will get a crash at runtime if the date string is more than 12h:
let d = timeDateFormatter.dateFromString("13:42")! // crash
because "hh:mm" deals with 12h format only.
To use 24h format you should use "HH:mm":
timeDateFormatter.dateFormat = "HH:mm"
And to avoid crashes, avoid force-unwrapping:
if let d = timeDateFormatter.dateFromString("11:42") {
print(d)
} else {
// oops
}
If my diagnostic is wrong, please add details to your question. :)
I do not know that issue, however I always used my formatters this way, maybe this is also convenient to you.
let formatter = NSDateFormatter()
formatter.timeStyle = NSDateFormatterStyle.ShortStyle // here are different values possible, and all are good declared.
formatter.dateStyle = .MediumStyle // Same possible values like in timeStyle.
I have a date in String that arrives in the following format:
"2015-11-15 14:16:15 +0000"
and I want to display it in a localised format and even calculate the age.
To get to NSDate I use this code:
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss +SSSS"
let birthDate = self.dateFormatter.dateFromString(userProfile["birthdate"] as! String)
Then to display the date (in a short format) I change the dateFormatter
dateFormatter.dateFormat = "yyyy-MM-dd"
let birthDateShort = self.dateFormatter.stringFromDate(birthDate!)
I could of course just pull out the text from the initial string if I didn't need the NSDate format for other reasons. But it still seems like a long roundtrip , is this the "correct" method?
This is exactly the right thing to do. And it's not a "long roundtrip". A string is just a string: just a representation, a bunch of letters / glyphs for a human being to read. A date is serious calendar-related piece of information. You need that date as a central pivot point for doing anything calendrically meaningful. And that's exactly what you're doing. NSDateFormatter is provided exactly so you can do the very kind of thing you are doing.