A.S. Update 2, License: as I’ve been asked about which license this code is under: I put this into Public Domain. No warranty whatsoever. Still I’d be happy about attribution but don’t require such.
Ever tried to get e.g. the Last-Modified
HTTP response header field into a
NSDate object? That’s no real fun, because
- this standard formatting isn’t digested by default,
- the required format string doesn’t quite work as documented.
Let’s concentrate on ‘Full Date’ according to RFC 1123:
1NSString *src = @"Fri, 14 Aug 2009 14:45:31 GMT";
The NSDateFormatter switched to Unicode Format strings with OSX 10.4 (see the reference), so I use
1NSDateFormatter *df = [[NSDateFormatter alloc] init];
2df.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
3df.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
Caution: zzz
as timezone format should give the same results, but doesn’t. It says ...GMT+00:00
.
As the textual parts must be english (see RFC 1123 for all the wording), I add
1df.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
to be independent of the user’s current locale.
That’s it!
Update:
If [NSDateFormatter dateFromString:...]
and [NSDateFormatter stringFromDate:...]
are threadsafe, a category-implementation could look like:
1#import
2
3/** Category on NSDate to support rfc1123 formatted date strings.
4 http://blog.mro.name/2009/08/nsdateformatter-http-header/ and
5 http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
6 */
7@interface NSDate (NSDateRFC1123)
8
9/**
10 Convert a RFC1123 'Full-Date' string
11 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1)
12 into NSDate.
13 */
14+(NSDate*)dateFromRFC1123:(NSString*)value_;
15
16/**
17 Convert NSDate into a RFC1123 'Full-Date' string
18 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1).
19 */
20-(NSString*)rfc1123String;
21
22@end
23
24@implementation NSDate (NSDateRFC1123)
25
26+(NSDate*)dateFromRFC1123:(NSString*)value_
27{
28 if(value_ == nil)
29 return nil;
30 static NSDateFormatter *rfc1123 = nil;
31 if(rfc1123 == nil)
32 {
33 rfc1123 = [[NSDateFormatter alloc] init];
34 rfc1123.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
35 rfc1123.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
36 rfc1123.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss z";
37 }
38 NSDate *ret = [rfc1123 dateFromString:value_];
39 if(ret != nil)
40 return ret;
41
42 static NSDateFormatter *rfc850 = nil;
43 if(rfc850 == nil)
44 {
45 rfc850 = [[NSDateFormatter alloc] init];
46 rfc850.locale = rfc1123.locale;
47 rfc850.timeZone = rfc1123.timeZone;
48 rfc850.dateFormat = @"EEEE',' dd'-'MMM'-'yy HH':'mm':'ss z";
49 }
50 ret = [rfc850 dateFromString:value_];
51 if(ret != nil)
52 return ret;
53
54 static NSDateFormatter *asctime = nil;
55 if(asctime == nil)
56 {
57 asctime = [[NSDateFormatter alloc] init];
58 asctime.locale = rfc1123.locale;
59 asctime.timeZone = rfc1123.timeZone;
60 asctime.dateFormat = @"EEE MMM d HH':'mm':'ss yyyy";
61 }
62 return [asctime dateFromString:value_];
63}
64
65-(NSString*)rfc1123String
66{
67 static NSDateFormatter *df = nil;
68 if(df == nil)
69 {
70 df = [[NSDateFormatter alloc] init];
71 df.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
72 df.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
73 df.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
74 }
75 return [df stringFromDate:self];
76}
77
78@end
79
80//////////////////////////////////////////////////////////
81// A testcase to try things out.
82//////////////////////////////////////////////////////////
83
84#import
85
86/**
87 http://blog.mro.name/2009/08/nsdateformatter-http-header/ and
88 http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
89 */
90@interface NSDateFormatterTC : SenTestCase
91{
92
93}
94
95@end
96
97@implementation NSDateFormatterTC
98
99-(void)testAsctime
100{
101 STAssertEqualObjects(@"Sun, 06 Nov 1994 08:49:37 GMT",
102 [[NSDate dateFromRFC1123:@"Sun Nov 6 08:49:37 1994"] rfc1123String], @"fail");
103 STAssertEqualObjects(@"Wed, 16 Nov 1994 08:49:37 GMT",
104 [[NSDate dateFromRFC1123:@"Wed Nov 16 08:49:37 1994"] rfc1123String], @"fail");
105}
106
107-(void)testRFC850
108{
109 STAssertEqualObjects(@"Sun, 06 Nov 1994 08:49:37 GMT",
110 [[NSDate dateFromRFC1123:@"Sunday, 06-Nov-94 08:49:37 GMT"] rfc1123String], @"fail");
111}
112
113-(void)testRFC1123
114{
115 NSString *s = @"Fri, 14 Aug 2009 14:45:31 GMT";
116 STAssertEqualObjects(s, [[NSDate dateFromRFC1123:s] rfc1123String], @"fail");
117
118 s = @"Sun, 06 Nov 1994 08:49:37 GMT";
119 STAssertEqualObjects(s, [[NSDate dateFromRFC1123:s] rfc1123String], @"fail");
120
121 // a strange asymmetry:
122 s = @"Sat, 01 Jan 0001 00:00:00 GMT";
123 STAssertEqualObjects(@"Sat, 01 Jan 0001 01:27:48 GMT",
124 [[NSDate dateFromRFC1123:s] rfc1123String], @"fail");
125}
126
127-(void)testRawRFC1123
128{
129 NSDateFormatter *rfc1123 = [[NSDateFormatter alloc] init];
130 rfc1123.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
131 rfc1123.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
132 rfc1123.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
133
134 NSString *s = @"Fri, 14 Aug 2009 14:45:31 GMT";
135 STAssertEqualObjects(s, [rfc1123 stringFromDate:[rfc1123 dateFromString:s]], @"fail");
136
137 s = @"Sun, 06 Nov 1994 08:49:37 GMT";
138 STAssertEqualObjects(s, [rfc1123 stringFromDate:[rfc1123 dateFromString:s]], @"fail");
139
140 s = @"Sat, 01 Jan 0001 08:00:00 GMT";
141 STAssertEqualObjects(s, [rfc1123 stringFromDate:[rfc1123 dateFromString:s]], @"fail");
142
143 [rfc1123 release];
144}
145@end