会社のエンタープライズアプリケーション用にこれを修正しました。
既知のフォーマット文字列(sqliteデータベースから日付を解析するために使用するものなど)を使用する日付フォーマッターのこの問題を修正する必要があります。
ただし、修正されません。
- isLenientがtrueに設定されているNSDateFormatters。
- 解析にフォーマット文字列の代わりにスタイルを使用するNSDateFormatters。
iOS5または5.1ではマイナスの副作用は発生しないようです。それ以前は何もテストしていません。ただし、NSDateFormatterの内部を少し混乱させているため、AppStoreの送信プロセスに合格しない可能性があります。ただし、エンタープライズプログラムでプログラムを作成する場合(またはアドホック展開を使用する場合)、これは問題にはなりません。また、isLenientがオンになっている場合は邪魔にならないようにしますが、問題が発生しないという保証はありません。
これは一時的な解決策であることを強調したいと思います。考えられるすべての状況でこれをテストしたわけではないので、自己責任で実装する必要があります。
次のカテゴリを作成しました。
NSDateFormatter + HotFix.h
#import <Foundation/Foundation.h>
@interface NSDateFormatter (HotFix)
- (NSDate*)dateFromString:(NSString *)string;
@end
NSDateFormatter + HotFix.m
#import "NSDateFormatter+HotFix.h"
#import <objc/runtime.h>
@implementation NSDateFormatter (HotFix)
- (NSDate*)dateFromString:(NSString *)string
{
if (!string) return nil;
//HACK: Use the original implementation
void* baseFormatter = nil;
object_getInstanceVariable(self, "_formatter", &baseFormatter);
if (!baseFormatter) return nil;
//Use the underlying CFDateFormatter to parse the string
CFDateRef rawDate = CFDateFormatterCreateDateFromString(kCFAllocatorDefault, (CFDateFormatterRef)baseFormatter, (CFStringRef)string, NULL);
NSDate* source = (NSDate*)rawDate;
//We do not support lenient parsing of dates (or styles), period.
if (source && !self.isLenient && self.dateStyle == NSDateFormatterNoStyle && self.timeStyle == NSDateFormatterNoStyle)
{
//If it worked, then find out if the format string included a year (any cluster of 1 to 5 y characters)
NSString* format = [self dateFormat];
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"y{1,5}" options:NSRegularExpressionCaseInsensitive error:NULL];
NSArray* matches = [regex matchesInString:format options:0 range:NSMakeRange(0, [format length])];
if ([matches count] > 0)
{
for (NSTextCheckingResult* result in matches)
{
//Check for the y grouping being contained within quotes. If so, ignore it
if (result.range.location > 0 && result.range.location + result.range.length < [format length] - 1)
{
if ([format characterAtIndex:result.range.location - 1] == '\'' &&
[format characterAtIndex:result.range.location + result.range.length + 1] == '\'') continue;
}
NSString* possibleYearString = [string substringWithRange:result.range];
NSInteger possibleYear = [possibleYearString integerValue];
if (possibleYear > 3500)
{
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* dateComp = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:source];
dateComp.year = possibleYear;
return [calendar dateFromComponents:dateComp];
}
}
}
}
return [source autorelease];
}
@end
NSDateFormatterの既存のdateFromStringメソッドを置き換えます。これは、文字列を通常どおりに解析し、formatStringに年形式の文字のセットが含まれているかどうかを確認することで機能します。含まれている場合は、手動で年を引き出し、3500より大きいかどうかを確認します。最後に、この場合は、正しく解析された年になるように出力を書き換えます。
プロジェクトに含めるだけで有効になります。NSDateFormatterを使用するすべてのファイルにヘッダーをインポートする必要はありません。.mをコンパイルするだけでクラスが変更されます。dateFromString:を変更する他のカテゴリがある場合、このクラスの効果を定義することはできません。
これがお役に立てば幸いです。