2012-11-06
Yet Another iOS JSON Date Parser
Here’s yet another fix for my JSON date parser for Objective-C. This version includes support for dates with a timezone offset, which Jeff requested a while ago and which I’ve finally run into (Grumpydev updated Nancy to produce this date format back in April).
#import <Foundation/Foundation.h>
@interface SZJSONDate : NSObject
+ (NSObject *)dateToJson:(NSDate *)date;
+ (NSDate *)jsonStringToNSDate:(NSString *)string;
@end
#import "SZJSONDate.h"
@implementation SZJSONDate
+ (NSObject *)dateToJson:(NSDate *)date {
if (date == nil) return [NSNull null];
NSString *value = [NSNumberFormatter localizedStringFromNumber:[NSNumber numberWithDouble:date. timeIntervalSince1970 * 1000] numberStyle:NSNumberFormatterNoStyle];
return [NSString stringWithFormat:@"/Date(%@)/", value];
}
+ (NSDate *)jsonStringToNSDate:(NSString *)string {
if (string == nil) return nil;
// Extract the numeric part of the date. Dates should be in the format
// "/Date(x)/", where x is a number. This format is supplied automatically
// by JSON serialisers in .NET. Alternatively, dates can be supplied with
// the time zone suffixed after x as a 5 character timezone offset,
// consisting of a positive/negative indicator ('+' or '-'), two digit hour
// offset, and two digit minute offset (ie. '-0700' for MST).
NSRange range = NSMakeRange(6, [string length] - 8);
NSString* substring = [string substringWithRange:range];
NSTimeInterval seconds;
NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
// We need to parse out the date portion and the timezone portion
// separately. It's possible that we won't have a timezone and the date is
// negative, so we have to be more specific when searching for the
// separator.
unichar timezoneSeparator = [substring characterAtIndex:substring.length - 5];
NSString *date = nil;
if (timezoneSeparator == '+' || timezoneSeparator == '-') {
date = [substring substringToIndex:substring.length - 5];
NSString *timeZone = [substring substringFromIndex:substring.length - 4];
NSString *timeZoneHours = [timeZone substringToIndex:2];
NSString *timeZoneMinutes = [timeZone substringFromIndex:2];
NSNumber* milliseconds = [formatter numberFromString:date];
int hours = [[formatter numberFromString:timeZoneHours] intValue];
int minutes = [[formatter numberFromString:timeZoneMinutes] intValue];
int offset = (minutes + (hours * 60)) * 60;
// NSTimeInterval is specified in seconds, with milliseconds as
// fractions. The value we get back from the web service is specified
// in milliseconds. Both values are since 1st Jan 1970 (epoch).
seconds = [milliseconds longLongValue] / 1000.0;
if (timezoneSeparator == '+') {
seconds += offset;
} else {
seconds -= offset;
}
} else {
date = substring;
NSNumber* milliseconds = [formatter numberFromString:date];
seconds = [milliseconds longLongValue] / 1000.0;
}
[formatter release];
// If we've encountered .NET's minimum date value we treat it as nil.
if ([date isEqualToString:@"-59011459200000"]) return nil;
return [NSDate dateWithTimeIntervalSince1970:seconds];
}
@end