Added RKDotNetDateFormatter to translate back and forth between NSDate and strings like /Date(1234567890123-0500)/ Also includes header docs and specs. Fixes #264

This commit is contained in:
Greg Combs
2011-09-09 03:26:46 -05:00
parent 5c359edfc5
commit 9ce25f5689
5 changed files with 218 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
//
// RKDotNetDateFormatter.h
// RestKit
//
// Created by Greg Combs on 9/8/11.
// Copyright (c) 2011 RestKit. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
A subclass of NSDateFormatter that serves as translator between ASP.NET date serializations in JSON
strings and NSDate objects. This is useful for properly mapping these dates from an ASP.NET driven backend.
*NOTE* - DO NOT attempt to use setDateFormat: on this class. It will return invalid results.
*/
@interface RKDotNetDateFormatter : NSDateFormatter {
NSRegularExpression *dotNetExpression;
}
/**
Returns an NSDate object from an ASP.NET style date string respresentation, as seen in JSON.
Acceptable examples are:
/Date(1112715000000-0500)/
/Date(1112715000000)/
Where 1112715000000 is the number of milliseconds since January 1, 1970 00:00 GMT/UTC, and -0500 represents the
timezone offset from GMT in 24-hour time.
*NOTE* NSDate objects do not have timezones, and you should never change an actual date value based on a
timezone offset. However, timezones are important when presenting dates to the user. Therefore,
If an offset is present in the ASP.NET string (it should be), we actually ignore the offset portion because
we want to store the actual date value in its raw form, without any pollution of timezone information.
If, on the other hand, there is no offset in the ASP.NET string, we assume GMT (+0000) anyway.
In summation, for this class setTimeZone: is ignored except when using stringFromDate:
@param string The ASP.NET style string, /Date(1112715000000-0500)/
@return An NSDate object
@see stringFromDate
@see NSDateFormatter
@see NSTimeZone
*/
- (NSDate *)dateFromString:(NSString *)string;
/**
Returns an ASP.NET style date string from an NSDate, such as /Date(1112715000000+0000)/
Where 1112715000000 is the number of milliseconds since January 1, 1970 00:00 GMT/UTC, and +0000 is the
timezone offset from GMT in 24-hour time.
*NOTE* GMT (+0000) is assumed otherwise specified via setTimeZone:
@param date An NSDate
@return The ASP.NET style string, /Date(1112715000000-0500)/
@see dateFromString
@see NSDateFormatter
@see NSTimeZone
*/
- (NSString *)stringFromDate:(NSDate *)date;
@end

View File

@@ -0,0 +1,92 @@
//
// RKDotNetDateFormatter.m
// RestKit
//
// Created by Greg Combs on 9/8/11.
// Copyright (c) 2011 RestKit. All rights reserved.
//
#import "RKDotNetDateFormatter.h"
#import "RestKit.h"
BOOL isValidRange(NSRange rangeOfMatch);
NSTimeInterval secondsFromMilliseconds(NSTimeInterval millisecs);
NSTimeInterval millisecondsFromSeconds(NSTimeInterval seconds);
@interface RKDotNetDateFormatter()
- (NSString *)millisecondsFromString:(NSString *)string;
@end
@implementation RKDotNetDateFormatter
- (NSDate *)dateFromString:(NSString *)string {
NSString *milliseconds = [self millisecondsFromString:string];
if (!milliseconds) {
RKLogError(@"Attempted to interpret an invalid .NET date string: %@", string);
return nil;
}
NSTimeInterval seconds = secondsFromMilliseconds([milliseconds doubleValue]);
return [NSDate dateWithTimeIntervalSince1970:seconds];
}
- (NSString *)stringFromDate:(NSDate *)date {
if (!date) {
RKLogError(@"Attempted to represent an invalid date: %@", date);
return nil;
}
NSTimeInterval milliseconds = millisecondsFromSeconds([date timeIntervalSince1970]);
NSString *timeZoneOffset = [super stringFromDate:date];
return [NSString stringWithFormat:@"/Date(%1.0lf%@)/", milliseconds, timeZoneOffset];
}
- (id)init {
self = [super init];
if (self) {
self.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease];
self.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
[self setDateFormat:@"ZZ"]; // GMT offset, like "-0500"
NSString *pattern = @"\\/Date\\((\\d+)((?:[\\+\\-]\\d+)?)\\)\\/"; // /Date(mSecs)/ or /Date(mSecs-0400)/
dotNetExpression = [[NSRegularExpression alloc] initWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:NULL];
}
return self;
}
- (void)dealloc {
[dotNetExpression release];
[super dealloc];
}
- (NSString *)millisecondsFromString:(NSString *)string {
if (!string)
return nil;
NSTextCheckingResult *match = [dotNetExpression firstMatchInString:string options:NSMatchingCompleted range:NSMakeRange(0, [string length])];
if (!match)
return nil;
NSRange millisecRange = [match rangeAtIndex:1];
if (!isValidRange(millisecRange))
return nil;
//NSRange timeZoneRange = [match rangeAtIndex:2];
NSString *milliseconds = [string substringWithRange:millisecRange];
return milliseconds;
}
@end
BOOL isValidRange(NSRange rangeOfMatch) {
return (!NSEqualRanges(rangeOfMatch, NSMakeRange(NSNotFound, 0)));
}
NSTimeInterval secondsFromMilliseconds(NSTimeInterval millisecs) {
return millisecs / 1000.f;
}
NSTimeInterval millisecondsFromSeconds(NSTimeInterval seconds) {
return seconds * 1000.f;
}

View File

@@ -12,3 +12,4 @@
#import "RKLog.h"
#import "NSString+RestKit.h"
#import "RKPathMatcher.h"
#import "RKDotNetDateFormatter.h"