source: trunk/Cocoa/Pester/Source/NJRDateFormatter.m@ 580

Last change on this file since 580 was 577, checked in by Nicholas Riley, 15 years ago

NJRDateFormatter.m: Remove an unused function; actually get ICU-based date formatters working (previously, only Date::Manip was in use); add additional date formatters; resolve issue with "a" and "p" (or localized equivalents) not being interpreted as expected.

File size: 6.6 KB
Line 
1//
2// NJRDateFormatter.m
3// Pester
4//
5// Created by Nicholas Riley on Wed Oct 09 2002.
6// Copyright (c) 2002 Nicholas Riley. All rights reserved.
7//
8
9#import "NJRDateFormatter.h"
10#import "ParseDate.h"
11#include <dlfcn.h>
12
13static NSDateFormatter *protoFormatter() {
14 NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
15 [formatter setLenient: YES];
16 return formatter;
17}
18
19static NSDateFormatter *dateFormatterWithStyle(NSDateFormatterStyle style) {
20 NSDateFormatter *formatter = protoFormatter();
21 [formatter setTimeStyle: NSDateFormatterNoStyle];
22 [formatter setDateStyle: style];
23 return formatter;
24}
25
26static NSDateFormatter *timeFormatterWithStyle(NSDateFormatterStyle style) {
27 NSDateFormatter *formatter = protoFormatter();
28 [formatter setTimeStyle: style];
29 [formatter setDateStyle: NSDateFormatterNoStyle];
30 return formatter;
31}
32
33static NSDateFormatter *timeFormatterWithFormat(NSString *format) {
34 NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
35 [formatter setDateFormat: format];
36 [formatter setLenient: NO];
37 return formatter;
38}
39
40static const NSDateFormatterStyle formatterStyles[] = {
41 NSDateFormatterShortStyle,
42 NSDateFormatterMediumStyle,
43 NSDateFormatterLongStyle,
44 NSDateFormatterFullStyle,
45 NSDateFormatterNoStyle
46};
47
48// note: these formats must be 0-padded where appropriate and contain no spaces
49// or attempts to force them into strict interpretation will fail
50static const NSString *timeFormats[] = {
51 @"hha",
52 @"HHmmss",
53 @"HHmm",
54 @"HH",
55 nil
56};
57
58@implementation NJRDateFormatter
59
60#pragma mark initialize-release
61
62+ (void)initialize;
63{
64 long minorVersion, majorVersion;
65 Gestalt(gestaltSystemVersionMajor, &majorVersion);
66 Gestalt(gestaltSystemVersionMinor, &minorVersion);
67 if (majorVersion != 10)
68 return;
69
70 NSString *libName;
71 if (minorVersion == 4) {
72 libName = @"libParseDate-10.4";
73 } else if (minorVersion == 5) {
74 libName = @"libParseDate-10.5";
75 } else {
76 return;
77 }
78
79 NSString *libPath = [[NSBundle mainBundle] pathForResource: libName ofType: @"dylib"];
80 if (libPath == nil)
81 return;
82
83 void *lib = dlopen([libPath fileSystemRepresentation], RTLD_LAZY | RTLD_GLOBAL);
84 const char *libError;
85 if ( (libError = dlerror()) != NULL) {
86 NSLog(@"failed to dlopen %@: %s", libPath, libError);
87 return;
88 }
89
90 parse_natural_language_date = dlsym(lib, "parse_natural_language_date");
91 if ( (libError = dlerror()) != NULL) {
92 NSLog(@"failed to look up parse_natural_language_date in %@: %s", libPath, libError);
93 parse_natural_language_date = NULL;
94 return;
95 }
96}
97
98+ (NJRDateFormatter *)dateFormatter;
99{
100 NJRDateFormatter *formatter = [[self alloc] init];
101 NSMutableArray *tryFormatters = [[NSMutableArray alloc] init];
102
103 for (const NSDateFormatterStyle *s = formatterStyles ; *s != NSDateFormatterNoStyle ; *s++) {
104 NSDateFormatter *tryFormatter = dateFormatterWithStyle(*s);
105 [tryFormatters addObject: tryFormatter];
106 [tryFormatter release];
107 }
108 // XXX do this in init
109 formatter->tryFormatters = tryFormatters;
110
111 return [formatter autorelease];
112}
113
114+ (NJRDateFormatter *)timeFormatter;
115{
116 NJRDateFormatter *formatter = [[self alloc] init];
117 NSMutableArray *tryFormatters = [[NSMutableArray alloc] init];
118 NSDateFormatter *tryFormatter;
119
120 for (const NSDateFormatterStyle *s = formatterStyles ; *s != NSDateFormatterNoStyle ; *s++) {
121 tryFormatter = timeFormatterWithStyle(*s);
122 [tryFormatters addObject: tryFormatter];
123 [tryFormatter release];
124 }
125 for (NSString **s = timeFormats ; *s != nil ; *s++) {
126 tryFormatter = timeFormatterWithFormat(*s);
127 [tryFormatters addObject: tryFormatter];
128 [tryFormatter release];
129 }
130 formatter->tryFormatters = tryFormatters;
131
132 return [formatter autorelease];
133}
134
135- (void)dealloc;
136{
137 [tryFormatters release]; tryFormatters = nil;
138 [super dealloc];
139}
140
141#pragma mark primitive formatter
142
143- (NSString *)stringForObjectValue:(id)obj;
144{
145 return [super stringForObjectValue: obj];
146}
147
148- (NSAttributedString *)attributedStringForObjectValue:(id)obj withDefaultAttributes:(NSDictionary *)attrs;
149{
150 return [super attributedStringForObjectValue: obj
151 withDefaultAttributes: attrs];
152}
153
154- (BOOL)getObjectValue:(id *)anObject forString:(NSString *)string errorDescription:(NSString **)error
155{
156 if ([super getObjectValue: anObject forString: string errorDescription: error])
157 return YES;
158
159 NSDate *date;
160 NSEnumerator *e = [tryFormatters objectEnumerator];
161 NSDateFormatter *tryFormatter;
162 NSString *cleaned = nil;
163
164 // don't let time specifications ending in "a" or "p" trigger Date::Manip
165 if ([self timeStyle] != NSDateFormatterNoStyle &&
166 [string length] > 1 &&
167 ![[NSCharacterSet letterCharacterSet]
168 characterIsMember: [string characterAtIndex: [string length] - 2]]) {
169
170 NSString *am = [self AMSymbol], *pm = [self PMSymbol];
171 if (am != nil && [am length] > 1 && pm != nil && [pm length] > 1) {
172
173 NSString *a = [am substringToIndex: 1], *p = [pm substringToIndex: 1];
174 if (![a isCaseInsensitiveLike: p]) {
175
176 NSString *last = [string substringFromIndex: [string length] - 1];
177 if ([last isCaseInsensitiveLike: a])
178 string = [string stringByAppendingString: [am substringFromIndex: 1]];
179 if ([last isCaseInsensitiveLike: p])
180 string = [string stringByAppendingString: [pm substringFromIndex: 1]];
181 }
182 }
183 }
184
185 while ( (tryFormatter = [e nextObject]) != nil) {
186 date = [tryFormatter dateFromString: string];
187
188 if (date == nil)
189 continue;
190
191 if (([tryFormatter dateStyle] != NSDateFormatterNoStyle) ||
192 ([tryFormatter timeStyle] != NSDateFormatterNoStyle))
193 goto success;
194
195 // XXX ICU-based "format" formatters return 0 instead of nil
196 if ([date timeIntervalSince1970] == 0)
197 continue;
198
199 // even non-lenient ICU-based "format" formatters are insufficiently strict,
200 // permitting arbitrary characters before and after the parsed string
201 NSString *formatted = [tryFormatter stringFromDate: date];
202 if (cleaned == nil)
203 cleaned = [[string componentsSeparatedByString: @" "] componentsJoinedByString: @""];
204 if ([cleaned characterAtIndex: 0] != '0' && [formatted characterAtIndex: 0] == '0')
205 formatted = [formatted substringFromIndex: 1];
206
207 if ([formatted isCaseInsensitiveLike: cleaned])
208 goto success;
209 }
210
211 if (parse_natural_language_date == NULL) return NO;
212
213 date = parse_natural_language_date(string);
214 if (date != nil) goto success;
215
216 return NO;
217
218success:
219 *anObject = date;
220 if (error != NULL) *error = nil;
221 return YES;
222}
223
224#pragma mark miscellaneous
225
226+ (BOOL)naturalLanguageParsingAvailable;
227{
228 return (parse_natural_language_date != NULL);
229}
230@end
Note: See TracBrowser for help on using the repository browser.