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

Last change on this file since 577 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
RevLine 
[21]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"
[367]10#import "ParseDate.h"
11#include <dlfcn.h>
[21]12
[577]13static NSDateFormatter *protoFormatter() {
14 NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
15 [formatter setLenient: YES];
16 return formatter;
[21]17}
18
[577]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
[360]40static const NSDateFormatterStyle formatterStyles[] = {
41 NSDateFormatterShortStyle,
42 NSDateFormatterMediumStyle,
43 NSDateFormatterLongStyle,
44 NSDateFormatterFullStyle,
45 NSDateFormatterNoStyle
46};
47
[577]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
[360]58@implementation NJRDateFormatter
59
60#pragma mark initialize-release
61
62+ (void)initialize;
[43]63{
[367]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 }
[360]96}
97
98+ (NJRDateFormatter *)dateFormatter;
99{
100 NJRDateFormatter *formatter = [[self alloc] init];
101 NSMutableArray *tryFormatters = [[NSMutableArray alloc] init];
102
[577]103 for (const NSDateFormatterStyle *s = formatterStyles ; *s != NSDateFormatterNoStyle ; *s++) {
104 NSDateFormatter *tryFormatter = dateFormatterWithStyle(*s);
[360]105 [tryFormatters addObject: tryFormatter];
106 [tryFormatter release];
[43]107 }
[360]108 // XXX do this in init
109 formatter->tryFormatters = tryFormatters;
110
111 return [formatter autorelease];
[43]112}
[21]113
[360]114+ (NJRDateFormatter *)timeFormatter;
115{
116 NJRDateFormatter *formatter = [[self alloc] init];
117 NSMutableArray *tryFormatters = [[NSMutableArray alloc] init];
[577]118 NSDateFormatter *tryFormatter;
[360]119
[577]120 for (const NSDateFormatterStyle *s = formatterStyles ; *s != NSDateFormatterNoStyle ; *s++) {
121 tryFormatter = timeFormatterWithStyle(*s);
[360]122 [tryFormatters addObject: tryFormatter];
123 [tryFormatter release];
124 }
[577]125 for (NSString **s = timeFormats ; *s != nil ; *s++) {
126 tryFormatter = timeFormatterWithFormat(*s);
127 [tryFormatters addObject: tryFormatter];
128 [tryFormatter release];
129 }
[360]130 formatter->tryFormatters = tryFormatters;
131
132 return [formatter autorelease];
133}
134
[43]135- (void)dealloc;
136{
[360]137 [tryFormatters release]; tryFormatters = nil;
[43]138 [super dealloc];
139}
140
[360]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
[21]154- (BOOL)getObjectValue:(id *)anObject forString:(NSString *)string errorDescription:(NSString **)error
155{
[360]156 if ([super getObjectValue: anObject forString: string errorDescription: error])
157 return YES;
158
159 NSDate *date;
160 NSEnumerator *e = [tryFormatters objectEnumerator];
161 NSDateFormatter *tryFormatter;
[577]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 }
[360]184
185 while ( (tryFormatter = [e nextObject]) != nil) {
186 date = [tryFormatter dateFromString: string];
[577]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;
[360]209 }
210
[577]211 if (parse_natural_language_date == NULL) return NO;
[367]212
[360]213 date = parse_natural_language_date(string);
214 if (date != nil) goto success;
215
216 return NO;
217
218success:
[21]219 *anObject = date;
[360]220 if (error != NULL) *error = nil;
[21]221 return YES;
222}
223
[360]224#pragma mark miscellaneous
225
226+ (BOOL)naturalLanguageParsingAvailable;
227{
[367]228 return (parse_natural_language_date != NULL);
[360]229}
[21]230@end
Note: See TracBrowser for help on using the repository browser.