How to parse ISO 8601 using NSDateFormatter with optional milliseconds part - ios

I'm trying to use an NSDateFormatter to parse dates that are in either of these formats
#"2013-02-01T14:21:00"
or
#"2013-02-01T14:21:56.345"
Currently I am using the below method to parse the string and return a date:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss"];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"]];
NSDate *date = [dateFormatter dateFromString:dateToFormat];
This works fine for the first style of date but returns nil for a string that includes the milliseconds part.
I suppose I could test for the existence of the milliseconds and strip them but I was wondering if I could change the date format to treat the .SSS as optional?
Thanks for your help

As far as I know there is no way to make optional parameters.
The usual solution is to use two formatters, one for each format.
To decide which formatter to use, you can either
Count the number of characters in the date string (as suggested in Parsing a RFC 822 date with NSDateFormatter)
Just try both formatters and get the first non-nil result.
Since your date formats are similar, you can go with only one formatter and if the date string is too short, append .000 before using the formatter.

The correct approach since iOS 10 is to use ISO8601DateFormatter specifically created to handle all variations of ISO 8601 date strings. Please see the example below:
let date = Date()
var string: String
let formatter = ISO8601DateFormatter()
string = formatter.string(from: date)
let GMT = TimeZone(abbreviation: "GMT")
let options: ISO8601DateFormatOptions = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone]
string = ISO8601DateFormatter.string(from: date, timeZone: GMT, formatOptions: options)
And Objective-C version:
NSDate *date = [NSDate date];
NSString *string;
NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init];
string = [formatter stringFromDate:date];
NSTimeZone *GMT = [NSTimeZone timeZoneWithAbbreviation: #"GMT"];
NSISO8601DateFormatOptions options = NSISO8601DateFormatWithInternetDateTime | NSISO8601DateFormatWithDashSeparatorInDate | NSISO8601DateFormatWithColonSeparatorInTime | NSISO8601DateFormatWithTimeZone;
string = [NSISO8601DateFormatter stringFromDate:date timeZone:GMT formatOptions:options];

I wrote an universal parser which dropped milliseconds part.
#implementation JSONModel(NSPAdditions)
- (NSDate *)NSDateFromNSString:(NSString*)string {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]];
NSArray* parts = [string componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:#" T"]];
if ([parts count] <= 1) {
return [formatter dateFromString:string];
}
NSString *part0 = parts[0];
NSAssert([part0 length] == [#"yyyy-MM-dd" length], #"Date format error");
NSString *part1 = parts[1];
if ([part1 length] > [#"HH:mm:ss" length]) {
part1 = [part1 substringToIndex:[#"HH:mm:ss" length]];
}
NSString *fmted = [NSString stringWithFormat:#"%# %#", part0, part1];
[formatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
return [formatter dateFromString:fmted];
}
#end

Related

NSDateFormatterLongStyle string to NSDate

I have a UIDataPicker in my viewController with default location, when my user finishes selecting the date I run this code:
NSString *dateString = [NSDateFormatter localizedStringFromDate:[self.dataPicker date]
dateStyle:NSDateFormatterLongStyle
timeStyle:NSDateFormatterNoStyle];
With that code I can storage the date in the following format:
May 31, 2016
Later in my code I need to convert this string into a real date format, for this I use the code below:
-(NSDate*)convertStringToDate:(NSString*)date{
NSString *dateString = date;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
[dateFormatter setDateFormat:#"MM/dd/yyyy"];
return [dateFormatter dateFromString:dateString];
}
But this code return a null value. As the datepicker is set by default, my system can receive any date format, but in the end I want it to be converted to the format en_us.
How I can solve this problem?
Don't store the date as a string; store it as an offset, in seconds, from some reference date.
i.e:
uint64_t offset = (uint64_t)[[self.dataPicker date] timeIntervalSinceReferenceDate];
// store this 64-bit unsigned integer.
This takes less space and is quicker to convert to/from an NSDate object.
You can leave the offset as an NSTimeInterval (64-bit floating point double) if you prefer, but as you aren't storing date & time, uint64_t should do...
Use this code,
-(NSDate*)convertStringToDate:(NSString*)date{
NSString *dateString = date;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
[dateFormatter setDateFormat:#"MMM d, yyyy"];
return [dateFormatter dateFromString:dateString];
}
hope its helpful
The formatting string depends on the locale you are using. From the localizedStringFromDate documentation:
Returns string representation of a given date formatted for the
current locale using the specified date and time styles.
This method uses a date formatter configured with the current default
settings. The returned string is the same as if you configured and
used a date formatter as shown in the following example:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.formatterBehavior = NSDateFormatterBehavior10_4;
formatter.dateStyle = dateStyle; formatter.timeStyle = timeStyle;
NSString *result = [formatter stringForObjectValue:date];
Means, you should do the next:
-(NSDate*)convertStringToDate:(NSString*)dateString {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.formatterBehavior = NSDateFormatterBehavior10_4;
dateFormatter.dateStyle = NSDateFormatterLongStyle;
dateFormatter.timeStyle = NSDateFormatterNoStyle;
return [dateFormatter dateFromString:dateString];
}

NSString to NSDate with format conversation issue

I've already searched StackOverflow.com for an answer to this question, still without any success. Already looked here:
iOS - Converting time and date to user time zone
Date Format - time zone
Get current iPhone device timezone date and time from UTC-5 timezone date and time iPhone app?
IOS how to set date format
Nothing of those worked.
So I have this NSString date format: 2014-05-07T10:28:52.000Z trying to convert it to NSDate, this is what I'm using:
+ (NSDate*)stringToDate:(NSString*)string format:(NSString*)format
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:format];
return [dateFormatter dateFromString:string];
}
This is how I'm using it:
NSDate *date = [AppUtils stringToDate:youtube.postDate format:#"yyyy-MM-dd'T'HH:mm:ssZZZ"];
I've also tried those formats:
yyyy-MM-dd'T'HH:mm:ss.ZZZ
yyyy-MM-dd'T'HH:mm.ss.ZZZ
yyyy-MM-dd'T'HH:mm.ssZZZ
How could I convert it to NSDate successfully?
Thanks in advance!
If the date format is fixed, what I do is below.
Replace T by space
Replace Z by blank
And then do formatting...
NSString *dateReceivedInString = #"2014-05-07T10:28:52.000Z";
dateReceivedInString = [dateReceivedInString stringByReplacingOccurrencesOfString:#"T" withString:#" "];
dateReceivedInString = [dateReceivedInString stringByReplacingOccurrencesOfString:#"Z" withString:#""];
Now do the formatting using
yyyy-MM-dd HH:mm:ss
Edit 1
If you want to work with your case, use below
yyyy-MM-dd'T'HH:mm:ss.SSSZ
Edit 2
I tried this and it is working.
NSString *dateReceivedInString = #"2014-05-07T10:28:52.000Z";
dateReceivedInString = [dateReceivedInString stringByReplacingOccurrencesOfString:#"T" withString:#" "];
dateReceivedInString = [dateReceivedInString stringByReplacingOccurrencesOfString:#"Z" withString:#""];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss.SSS"];
NSLog(#"ddddd====%#", [dateFormatter dateFromString:dateReceivedInString]);
Edit 3
To make working with your case use below.
NSString *dateReceivedInString = #"2014-05-07T10:28:52.000Z";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
NSLog(#"ddddd====%#", [dateFormatter dateFromString:dateReceivedInString]);
Here are 2 methods that I use to convert RFC3339 Date string to NSDate,
And also Method for converting NSDate to RFC3339 Date string
Method for converting RFC3339 Date string to NSDate
+ (NSDate *)dateForRFC3339DateTimeString:(NSString *)rfc3339DateTimeString
{
NSDateFormatter *rfc3339DateFormatter = [[NSDateFormatter alloc] init];
[rfc3339DateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'"];
[rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
// Convert the RFC 3339 date time string to an NSDate.
return [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];}
Method for converting NSDate to RFC3339 Date string
+ (NSString *)RFC3339DateTimeFromDate:(NSDate *)aDate
{
NSDateFormatter *rfc3339DateFormatter = [[NSDateFormatter alloc] init];
[rfc3339DateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'"];
[rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
return [rfc3339DateFormatter stringFromDate:aDate];
}

Setting up AM/PM for my time in iOS

My original date format is : 2014-03-14T10:35:24.537
So I first separate the time and date with componentsSeparatedByString, then I save the second half (the time part) to NSString time, while eliminating the microseconds. to the format 10:35. I'm trying to get it to add PM/AM but setDateFormat:#"hh:mm a" is not doing it. What am I doing wrong?
NSArray *components = [datestr componentsSeparatedByString:#"T"];
NSString *time = components[1];
time = [time substringWithRange:NSMakeRange(0, 5)];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"hh:mm a"];
NSDate *timeFromString = [formatter dateFromString:time];
NSLog(#"%#", timeFromString);
When I log timeFromString, I get a null.
EDIT: I changed the formatter above to [formatter setDateFormat:#"hh:mm"]; and now the timeFromString logs as: 2000-01-01 18:35:00 +0000 when data coming in is 2014-03-14T10:35:28.42
A date formatter is used to convert dates to or from a string. A single date formatter cannot be used to convert between two different date formats. (At least, not without mutating the date formatter between operations.)
Use one formatter to convert the original string to a date. That formatter should not include AM/PM, since your original string doesn't.
Use a second formatter to convert the date to a new string. That formatter should include AM/PM, if you desire one.
Here 's how you should parse and convert the date:
//the date string
NSString *datestr = #"2014-03-14T10:35:24.537";
//strip the date
NSArray *components = [datestr componentsSeparatedByString:#"T"];
NSString *time = components[1];
time = [time substringWithRange:NSMakeRange(0, 5)];
//parse string to a date
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"HH:mm"];
NSDate *timeFromString = [formatter dateFromString:time];
//Desired format
NSDateFormatter *timeformat = [[NSDateFormatter alloc] init];
[timeformat setDateFormat:#"hh:mm aa"];
NSString *finalString = [timeformat stringFromDate:timeFromString];
NSLog(#"final = %#",finalString);
OUTPUT:
final = 10:35 AM

Convert JSON date into UNIX timestamp - iOS [duplicate]

I have a timestamp coming from server that looks like this:
2013-04-18T08:49:58.157+0000
I've tried removing the colons, I've tried all of these:
Converting an ISO 8601 timestamp into an NSDate: How does one deal with the UTC time offset?
Why NSDateFormatter can not parse date from ISO 8601 format
Here is where I am at:
+ (NSDate *)dateUsingStringFromAPI:(NSString *)dateString {
NSDateFormatter *dateFormatter;
dateFormatter = [[NSDateFormatter alloc] init];
//#"yyyy-MM-dd'T'HH:mm:ss'Z'" - doesn't work
//#"yyyy-MM-dd'T'HH:mm:ssZZZ" - doesn't work
//#"yyyy-MM-dd'T'HH:mm:sss" - doesn't work
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"GMT"]];
// NSDateFormatter does not like ISO 8601 so strip the milliseconds and timezone
dateString = [dateString substringWithRange:NSMakeRange(0, [dateString length]-5)];
return [dateFormatter dateFromString:dateString];
}
One of my biggest questions is, is the date format I have above really ISO 8601? All the examples I have seen from people the formats of each are slightly different. Some have ...157-0000, others don't have anything at the end.
This works for me:
NSString *dateString = #"2013-04-18T08:49:58.157+0000";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
// Always use this locale when parsing fixed format date strings
NSLocale *posix = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
[formatter setLocale:posix];
NSDate *date = [formatter dateFromString:dateString];
NSLog(#"date = %#", date);
There is New API from Apple! NSISO8601DateFormatter
NSString *dateSTR = #"2005-06-27T21:00:00Z";
NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init];
NSDate *date = [formatter dateFromString:dateSTR];
NSLog(#"%#", date);
I also have the native API, which is way cleaner... This is the implementation I got in my DateTimeManager class:
+ (NSDate *)getDateFromISO8601:(NSString *)strDate{
NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init];
NSDate *date = [formatter dateFromString: strDate];
return date;
}
Just copy and paste the method, it would do the trick. Enjoy it!
The perfect and best solution that worked for me is:
let isoFormatter = ISO8601DateFormatter();
isoFormatter.formatOptions = [ISO8601DateFormatter.Options.withColonSeparatorInTime,
ISO8601DateFormatter.Options.withFractionalSeconds,
ISO8601DateFormatter.Options.withFullDate,
ISO8601DateFormatter.Options.withFullTime,
ISO8601DateFormatter.Options.withTimeZone]
let date = isoFormatter.date(from: dateStr);
For further more detail, you can refer to apple's official documentation: https://developer.apple.com/documentation/foundation/nsiso8601dateformatter

Regular expression for date formats in iOS

In my project i need to check some format coming from server, So i want to write a regular expression to check the format.
This is the format "03/31/2013 08:00:00" and other format is "03/31/2003 08:00 AM/PM"
How can i check this date formats. Can any one help me.
Regards
Kiran
^(?:\d{2}\/){2}\d{4} \d{2}:\d{2}(?::\d{2}| (?:AM|PM))?$
Note that it won't check for incorrect values.
If you want to get NSDate from this string, you should use NSDateFormetter instead:
NSString * date = #"03/31/2013 08:00:00"; // source string
NSDateFormatter * date_formatter = [[NSDateFormatter alloc] init];
[date_formatter setDateFormat: #"MM/dd/yyyy HH:mm:ss"]; // your date format
NSDate * result = [date_formatter dateFromString: date]; // converting
NSLog (#"%#", [result description]); // log your date result
[date_format release];
Not a real answer to your question about regex but, if you want to get the date from the dateString received from server you can try a trial and error method with the dateFormat of NSDateFormatter.
NSString *dateFormat1 = #"MM/dd/yyyy HH:mm:ss";
NSString *dateFormat2 = #"MM/dd/yyyy hh:mm:ss a";
NSString *dateString = #"03/31/2013 08:00:00 AM";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
[dateFormatter setLocale:enUSPOSIXLocale];
[dateFormatter setDateFormat:dateFormat1];
NSDate *date = [dateFormatter dateFromString:dateString];
if (!date) {
[dateFormatter setDateFormat:dateFormat2];
date = [dateFormatter dateFromString:dateString];
}
NSLog(#"Date : %#",date);

Resources