As of iOS 7, NSDateFormatter does indeed create an NSDate when presented with a string in this format:
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:@"@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ""];
NSLog(@"non–nil date, even honoring the 7–minute–offset in the time–zone on iOS 7: %@",
[formatter dateFromString:@"2011-07-12T18:07:31+02:07"]);
For iOS 6, the answer is to not use an NSDateFormatter…
Okay, up to this point I have read
regarding how to use NSDateFormatter
in order to create an NSDate
out of a string.
I have stumbled upon Peter Hosey's ISO8601DateFormatter, as well.
Looking into his implementation, I wonder:
Isn't there a way that is both correct and sane to get a string like this one 2011-07-12T18:07:31+02:00
into an NSDate
?
GMT
prefixing the "+"-sign, but...I can hack it to work for my application (using the format @"yyyy'-'MM'-'dd'T'HH':'mm':'ssz':'00"
) but that is — of course — incorrect because it will discard the minute-information of the timezone.
I could also replace the last colon with an empty string, but I would consider that a hack as well.
So, is there some secret sauce to make NSDateFormatter
take that string from above and give me a valid and correct NSDate
?
I have somewhere found the tip, that one could use +[NSDate dateWithNaturalLanguageString:]
to achieve my goal. This — however — only sets the date, but not the time! (Well it does set the time, but only taking the timezone-offset into account and not the HH:mm:ss part...)
Thread Safety On earlier versions of the operating system, or when using the legacy formatter behavior or running in 32-bit in macOS, NSDateFormatter is not thread safe, and you therefore must not mutate a date formatter simultaneously from multiple threads.
You can make use of the following DateFormat. SimpleDateFormat myDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); myDate. setTimeZone(TimeZone.
NSDate objects encapsulate a single point in time, independent of any particular calendrical system or time zone. Date objects are immutable, representing an invariant time interval relative to an absolute reference date (00:00:00 UTC on 1 January 2001).
let date = Date(); let dateFormatter = DateFormatter(); Date will give us the current date and time with respect to our current time zone. For me, it's Wednesday, June 1st, 2022. dateFormatter is an instance of the DateFormatter class that allows us to operate various formatting functions on our date .
This question is a bit old, but I was having the same problem. I came up with some code which is an answer and might be useful to others...
I use a regex to parse an ISO-8601 string and grab the output into a bunch of strings you can then use to create your own string to pass into NSDateFormatter (i.e. removing colons etc) or if you always are going to want the same output string, just create that from the results of a call to NSRegularExpression.
// ISO-8601 regex:
// YYYY-MM-DDThh:mm[:ss[.nnnnnnn]][{+|-}hh:mm]
// Unfortunately NSDateFormatter does not parse iso-8601 out of the box,
// so we need to use a regex and build up a date string ourselves.
static const char * REGEX_ISO8601_TIMESTAMP =
"\\A(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})" // Mandatory - YYYY-MM-DDThh:mm
"(?:"
":(\\d{2})" // Optional - :ss
"(?:"
"[.](\\d{1,6})" // Optional - .nnnnnn
")?"
")?"
"(?:"
"([+-])(\\d{2}):(\\d{2})|Z" // Optional -[+-]hh:mm or Z
")?\\z";
// Extract all the parts of the timestamp
NSError *error = NULL;
NSString *regexString = [[NSString alloc] initWithUTF8String:REGEX_ISO8601_TIMESTAMP];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString
options:NSRegularExpressionCaseInsensitive
error:&error];
NSArray *matches = [regex matchesInString:timestamp
options:0
range:NSMakeRange(0, [timestamp length])];
// Groups:
//
// elements start at 1 in the array returned from regex, as [0] contains the original string.
//
// MANDATORY - must exist as per ISO standard
// 1 - YYYY
// 2 - MM
// 3 - DD
// 4 - hh
// 5 - mm
// OPTIONAL (each one can be optional)
// 6 - ss
// 7 - nn (microseconds)
// 8 - offset sign (+/-)
// 9 - offset hour
// 10 - offset min
// put the parts into a string which will then be recognised by NSDateFormatter
// (which is acutally RFC822 format)
// mandatory init'd to nil, optional set to defaults.
NSString *YYYY, *MM, *DD, *hh, *mm, *ss, *nn, *sign, *Zhh, *Zmm;
NSRange tempRange;
for (NSTextCheckingResult *match in matches) {
NSRange matchRange = [match range];
NSInteger matchCount = [match numberOfRanges] - 1;
NSUInteger idx = 1;
if (idx < matchCount) {
tempRange = [match rangeAtIndex:idx++];
YYYY = tempRange.location != NSNotFound ? [timestamp substringWithRange:tempRange] : nil;
}
if (idx < matchCount) {
tempRange = [match rangeAtIndex:idx++];
MM = tempRange.location != NSNotFound ? [timestamp substringWithRange:tempRange] : nil;
}
if (idx < matchCount) {
tempRange = [match rangeAtIndex:idx++];
DD = tempRange.location != NSNotFound ? [timestamp substringWithRange:tempRange] : nil;
}
if (idx < matchCount) {
tempRange = [match rangeAtIndex:idx++];
hh = tempRange.location != NSNotFound ? [timestamp substringWithRange:tempRange] : nil;
}
if (idx < matchCount) {
tempRange = [match rangeAtIndex:idx++];
mm = tempRange.location != NSNotFound ? [timestamp substringWithRange:tempRange] : nil;
}
if (idx < matchCount) {
tempRange = [match rangeAtIndex:idx++];
ss = tempRange.location != NSNotFound ? [timestamp substringWithRange:tempRange] : nil;
}
if (idx < matchCount) {
tempRange = [match rangeAtIndex:idx++];
nn = tempRange.location != NSNotFound ? [timestamp substringWithRange:tempRange] : nil;
}
if (idx < matchCount) {
tempRange = [match rangeAtIndex:idx++];
sign = tempRange.location != NSNotFound ? [timestamp substringWithRange:tempRange] : nil;
}
if (idx < matchCount) {
tempRange = [match rangeAtIndex:idx++];
Zhh = tempRange.location != NSNotFound ? [timestamp substringWithRange:tempRange] : nil;
}
if (idx < matchCount) {
tempRange = [match rangeAtIndex:idx++];
Zmm = tempRange.location != NSNotFound ? [timestamp substringWithRange:tempRange] : nil;
}
}
Hope this helps someone!
Old question, but I found the right answer on someone's gist :
https://gist.github.com/soffes/840291
It parses and creates ISO-8601 Strings, and it's quicker than NSDateFormatter
Here is the code:
+ (NSDate *)dateFromISO8601String:(NSString *)string {
if (!string) {
return nil;
}
struct tm tm;
time_t t;
strptime([string cStringUsingEncoding:NSUTF8StringEncoding], "%Y-%m-%dT%H:%M:%S%z", &tm);
tm.tm_isdst = -1;
t = mktime(&tm);
return [NSDate dateWithTimeIntervalSince1970:t + [[NSTimeZone localTimeZone] secondsFromGMT]];
}
- (NSString *)ISO8601String {
struct tm *timeinfo;
char buffer[80];
time_t rawtime = [self timeIntervalSince1970] - [[NSTimeZone localTimeZone] secondsFromGMT];
timeinfo = localtime(&rawtime);
strftime(buffer, 80, "%Y-%m-%dT%H:%M:%S%z", timeinfo);
return [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With