mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-04-23 04:20:21 +08:00
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:
57
Code/Support/RKDotNetDateFormatter.h
Normal file
57
Code/Support/RKDotNetDateFormatter.h
Normal 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
|
||||
92
Code/Support/RKDotNetDateFormatter.m
Normal file
92
Code/Support/RKDotNetDateFormatter.m
Normal 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;
|
||||
}
|
||||
|
||||
@@ -12,3 +12,4 @@
|
||||
#import "RKLog.h"
|
||||
#import "NSString+RestKit.h"
|
||||
#import "RKPathMatcher.h"
|
||||
#import "RKDotNetDateFormatter.h"
|
||||
|
||||
@@ -275,6 +275,9 @@
|
||||
25F5182613724866009B2E22 /* RKObjectRelationshipMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 25F5182413724866009B2E22 /* RKObjectRelationshipMapping.m */; };
|
||||
25FB6D5613E4836C00F48969 /* RKObjectDynamicMapping.h in Headers */ = {isa = PBXBuildFile; fileRef = 252CF8B013E250730093BBD6 /* RKObjectDynamicMapping.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
25FB6DBD13E4858400F48969 /* RKObjectDynamicMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 252CF8B113E250730093BBD6 /* RKObjectDynamicMapping.m */; };
|
||||
3703ABAC14190D5700DB697A /* RKDotNetDateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3703ABAA14190D5700DB697A /* RKDotNetDateFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
3703ABAD14190D5700DB697A /* RKDotNetDateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3703ABAB14190D5700DB697A /* RKDotNetDateFormatter.m */; };
|
||||
3703ABB0141941FB00DB697A /* RKDotNetDateFormatterSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 3703ABAF141941FB00DB697A /* RKDotNetDateFormatterSpec.m */; };
|
||||
37CA4C501410A4D1009A3DCE /* RKFixCategoryBug.h in Headers */ = {isa = PBXBuildFile; fileRef = 258F846E1410574B007AABCD /* RKFixCategoryBug.h */; };
|
||||
37CA4C7C1410A7CF009A3DCE /* SOCKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 37CA4C6F1410A7CF009A3DCE /* SOCKit.h */; };
|
||||
37CA4C7D1410A7CF009A3DCE /* SOCKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 37CA4C701410A7CF009A3DCE /* SOCKit.m */; };
|
||||
@@ -703,6 +706,9 @@
|
||||
25E9682D13E6156100ABAE92 /* RKObjectMappingDefinition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKObjectMappingDefinition.h; sourceTree = "<group>"; };
|
||||
25F5182313724865009B2E22 /* RKObjectRelationshipMapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKObjectRelationshipMapping.h; sourceTree = "<group>"; };
|
||||
25F5182413724866009B2E22 /* RKObjectRelationshipMapping.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectRelationshipMapping.m; sourceTree = "<group>"; };
|
||||
3703ABAA14190D5700DB697A /* RKDotNetDateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKDotNetDateFormatter.h; sourceTree = "<group>"; };
|
||||
3703ABAB14190D5700DB697A /* RKDotNetDateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKDotNetDateFormatter.m; sourceTree = "<group>"; };
|
||||
3703ABAF141941FB00DB697A /* RKDotNetDateFormatterSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKDotNetDateFormatterSpec.m; sourceTree = "<group>"; };
|
||||
37CA4C6F1410A7CF009A3DCE /* SOCKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOCKit.h; sourceTree = "<group>"; };
|
||||
37CA4C701410A7CF009A3DCE /* SOCKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SOCKit.m; sourceTree = "<group>"; };
|
||||
37CA4C851410ABD2009A3DCE /* RKPathMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKPathMatcher.h; sourceTree = "<group>"; };
|
||||
@@ -1146,6 +1152,8 @@
|
||||
251939B213A94B5F0073A39B /* NSString+RestKit.h */,
|
||||
251939B313A94B5F0073A39B /* NSString+RestKit.m */,
|
||||
258F846E1410574B007AABCD /* RKFixCategoryBug.h */,
|
||||
3703ABAA14190D5700DB697A /* RKDotNetDateFormatter.h */,
|
||||
3703ABAB14190D5700DB697A /* RKDotNetDateFormatter.m */,
|
||||
);
|
||||
path = Support;
|
||||
sourceTree = "<group>";
|
||||
@@ -1472,6 +1480,7 @@
|
||||
258E490013C51FE600C9C883 /* RKJSONParserJSONKitSpec.m */,
|
||||
37DEBA61141123BB00FDF847 /* RKPathMatcherSpec.m */,
|
||||
37DEBA641411298300FDF847 /* NSStringRestKitSpec.m */,
|
||||
3703ABAF141941FB00DB697A /* RKDotNetDateFormatterSpec.m */,
|
||||
);
|
||||
path = Support;
|
||||
sourceTree = "<group>";
|
||||
@@ -1666,6 +1675,7 @@
|
||||
258F846F1410574B007AABCD /* RKFixCategoryBug.h in Headers */,
|
||||
37CA4C7C1410A7CF009A3DCE /* SOCKit.h in Headers */,
|
||||
37CA4C871410ABD2009A3DCE /* RKPathMatcher.h in Headers */,
|
||||
3703ABAC14190D5700DB697A /* RKDotNetDateFormatter.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -2185,6 +2195,7 @@
|
||||
251939B713A94B670073A39B /* NSString+RestKit.m in Sources */,
|
||||
37CA4C7D1410A7CF009A3DCE /* SOCKit.m in Sources */,
|
||||
37CA4C881410ABD2009A3DCE /* RKPathMatcher.m in Sources */,
|
||||
3703ABAD14190D5700DB697A /* RKDotNetDateFormatter.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -2298,6 +2309,7 @@
|
||||
25E5E66B1415665C00233720 /* RKObjectSerializerSpec.m in Sources */,
|
||||
37DEBA62141123BB00FDF847 /* RKPathMatcherSpec.m in Sources */,
|
||||
37DEBA651411298300FDF847 /* NSStringRestKitSpec.m in Sources */,
|
||||
3703ABB0141941FB00DB697A /* RKDotNetDateFormatterSpec.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
56
Specs/Support/RKDotNetDateFormatterSpec.m
Normal file
56
Specs/Support/RKDotNetDateFormatterSpec.m
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// RKDotNetDateFormatterSpec.m
|
||||
// RestKit
|
||||
//
|
||||
// Created by Greg Combs on 9/8/11.
|
||||
// Copyright (c) 2011 RestKit. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RKSpecEnvironment.h"
|
||||
#import "RKDotNetDateFormatter.h"
|
||||
|
||||
@interface RKDotNetDateFormatterSpec : RKSpec
|
||||
|
||||
@end
|
||||
|
||||
@implementation RKDotNetDateFormatterSpec
|
||||
|
||||
- (void)itShouldCreateADateFromDotNetWithTimeZone {
|
||||
NSString *dotNetString = @"/Date(1000212360000-0400)/";
|
||||
RKDotNetDateFormatter *formatter = [[RKDotNetDateFormatter alloc] init];
|
||||
NSDate *date = [formatter dateFromString:dotNetString];
|
||||
[formatter release];
|
||||
assertThat([date description], is(equalTo(@"2001-09-11 12:46:00 +0000")));
|
||||
}
|
||||
|
||||
- (void)itShouldCreateADateFromDotNetWithoutTimeZoneAssumingGMT {
|
||||
NSString *dotNetString = @"/Date(1112715000000)/";
|
||||
RKDotNetDateFormatter *formatter = [[RKDotNetDateFormatter alloc] init];
|
||||
NSDate *date = [formatter dateFromString:dotNetString];
|
||||
[formatter release];
|
||||
assertThat([date description], is(equalTo(@"2005-04-05 15:30:00 +0000")));
|
||||
}
|
||||
|
||||
- (void)itShouldFailToCreateADateFromInvalidStrings {
|
||||
RKDotNetDateFormatter *formatter = [[RKDotNetDateFormatter alloc] init];
|
||||
NSDate *date = [formatter dateFromString:nil];
|
||||
assertThat(date, is(equalTo(nil)));
|
||||
date = [formatter dateFromString:@"(null)"];
|
||||
assertThat(date, is(equalTo(nil)));
|
||||
date = [formatter dateFromString:@"1112715000-0500"];
|
||||
assertThat(date, is(equalTo(nil)));
|
||||
[formatter release];
|
||||
}
|
||||
|
||||
- (void)itShouldCreateADotNetStringFromADateUsingATimeZoneOffset {
|
||||
RKDotNetDateFormatter *formatter = [[RKDotNetDateFormatter alloc] init];
|
||||
NSDate *referenceDate = [NSDate dateWithTimeIntervalSince1970:1000212360];
|
||||
NSTimeZone *timeZoneEST = [NSTimeZone timeZoneWithAbbreviation:@"EST"];
|
||||
[formatter setTimeZone:timeZoneEST];
|
||||
NSString *string = [formatter stringFromDate:referenceDate];
|
||||
[formatter release];
|
||||
assertThat(string, is(equalTo(@"/Date(1000212360000-0400)/")));
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user