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

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

Basic 10.6 compatibility fixes for Perl embedding. Apparently DynaLoader is no longer necessary (was it ever?).

File size: 6.7 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 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 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 if (minorVersion == 6) {
76 libName = @"libParseDate-10.6";
77 } else {
78 return;
79 }
80
81 NSString *libPath = [[NSBundle mainBundle] pathForResource: libName ofType: @"dylib"];
82 if (libPath == nil)
83 return;
84
85 void *lib = dlopen([libPath fileSystemRepresentation], RTLD_LAZY | RTLD_GLOBAL);
86 const char *libError;
87 if ( (libError = dlerror()) != NULL) {
88 NSLog(@"failed to dlopen %@: %s", libPath, libError);
89 return;
90 }
91
92 parse_natural_language_date = dlsym(lib, "parse_natural_language_date");
93 if ( (libError = dlerror()) != NULL) {
94 NSLog(@"failed to look up parse_natural_language_date in %@: %s", libPath, libError);
95 parse_natural_language_date = NULL;
96 return;
97 }
98}
99
100+ (NJRDateFormatter *)dateFormatter;
101{
102 NJRDateFormatter *formatter = [[self alloc] init];
103 NSMutableArray *tryFormatters = [[NSMutableArray alloc] init];
104
105 for (const NSDateFormatterStyle *s = formatterStyles ; *s != NSDateFormatterNoStyle ; *s++) {
106 NSDateFormatter *tryFormatter = dateFormatterWithStyle(*s);
107 [tryFormatters addObject: tryFormatter];
108 [tryFormatter release];
109 }
110 // XXX do this in init
111 formatter->tryFormatters = tryFormatters;
112
113 return [formatter autorelease];
114}
115
116+ (NJRDateFormatter *)timeFormatter;
117{
118 NJRDateFormatter *formatter = [[self alloc] init];
119 NSMutableArray *tryFormatters = [[NSMutableArray alloc] init];
120 NSDateFormatter *tryFormatter;
121
122 for (const NSDateFormatterStyle *s = formatterStyles ; *s != NSDateFormatterNoStyle ; *s++) {
123 tryFormatter = timeFormatterWithStyle(*s);
124 [tryFormatters addObject: tryFormatter];
125 [tryFormatter release];
126 }
127 for (NSString **s = timeFormats ; *s != nil ; *s++) {
128 tryFormatter = timeFormatterWithFormat(*s);
129 [tryFormatters addObject: tryFormatter];
130 [tryFormatter release];
131 }
132 formatter->tryFormatters = tryFormatters;
133
134 return [formatter autorelease];
135}
136
137- (void)dealloc;
138{
139 [tryFormatters release]; tryFormatters = nil;
140 [super dealloc];
141}
142
143#pragma mark primitive formatter
144
145- (NSString *)stringForObjectValue:(id)obj;
146{
147 return [super stringForObjectValue: obj];
148}
149
150- (NSAttributedString *)attributedStringForObjectValue:(id)obj withDefaultAttributes:(NSDictionary *)attrs;
151{
152 return [super attributedStringForObjectValue: obj
153 withDefaultAttributes: attrs];
154}
155
156- (BOOL)getObjectValue:(id *)anObject forString:(NSString *)string errorDescription:(NSString **)error
157{
158 if ([super getObjectValue: anObject forString: string errorDescription: error])
159 return YES;
160
161 NSDate *date;
162 NSEnumerator *e = [tryFormatters objectEnumerator];
163 NSDateFormatter *tryFormatter;
164 NSString *cleaned = nil;
165
166 // don't let time specifications ending in "a" or "p" trigger Date::Manip
167 if ([self timeStyle] != NSDateFormatterNoStyle &&
168 [string length] > 1 &&
169 ![[NSCharacterSet letterCharacterSet]
170 characterIsMember: [string characterAtIndex: [string length] - 2]]) {
171
172 NSString *am = [self AMSymbol], *pm = [self PMSymbol];
173 if (am != nil && [am length] > 1 && pm != nil && [pm length] > 1) {
174
175 NSString *a = [am substringToIndex: 1], *p = [pm substringToIndex: 1];
176 if (![a isCaseInsensitiveLike: p]) {
177
178 NSString *last = [string substringFromIndex: [string length] - 1];
179 if ([last isCaseInsensitiveLike: a])
180 string = [string stringByAppendingString: [am substringFromIndex: 1]];
181 if ([last isCaseInsensitiveLike: p])
182 string = [string stringByAppendingString: [pm substringFromIndex: 1]];
183 }
184 }
185 }
186
187 while ( (tryFormatter = [e nextObject]) != nil) {
188 date = [tryFormatter dateFromString: string];
189
190 if (date == nil)
191 continue;
192
193 if (([tryFormatter dateStyle] != NSDateFormatterNoStyle) ||
194 ([tryFormatter timeStyle] != NSDateFormatterNoStyle))
195 goto success;
196
197 // XXX ICU-based "format" formatters return 0 instead of nil
198 if ([date timeIntervalSince1970] == 0)
199 continue;
200
201 // even non-lenient ICU-based "format" formatters are insufficiently strict,
202 // permitting arbitrary characters before and after the parsed string
203 NSString *formatted = [tryFormatter stringFromDate: date];
204 if (cleaned == nil)
205 cleaned = [[string componentsSeparatedByString: @" "] componentsJoinedByString: @""];
206 if ([cleaned characterAtIndex: 0] != '0' && [formatted characterAtIndex: 0] == '0')
207 formatted = [formatted substringFromIndex: 1];
208
209 if ([formatted isCaseInsensitiveLike: cleaned])
210 goto success;
211 }
212
213 if (parse_natural_language_date == NULL) return NO;
214
215 date = parse_natural_language_date(string);
216 if (date != nil) goto success;
217
218 return NO;
219
220success:
221 *anObject = date;
222 if (error != NULL) *error = nil;
223 return YES;
224}
225
226#pragma mark miscellaneous
227
228+ (BOOL)naturalLanguageParsingAvailable;
229{
230 return (parse_natural_language_date != NULL);
231}
232@end
Note: See TracBrowser for help on using the repository browser.