设置NSDataDetector的上下文日期

时间:2014-01-20 21:06:30

标签: ios objective-c nsdate nsdatadetector

假设今天是2014年1月20日。如果我使用NSDataDetector从字符串“明天下午4点”中提取日期,我将获得 2014-01-21T16:00 。大。

但是,假设我希望NSDataDetector 假装当前日期是2014年1月14日。这样,当我解析“明天下午4点”时,我会得到 2014-01-15T16:00 。如果我在设备上更改系统时间,我会得到我想要的。但是,有没有办法以编程方式指定它?

感谢。

3 个答案:

答案 0 :(得分:2)

出于测试目的,您可以使用名为method swizzling的技术。诀窍是用你自己的方法替换NSDate方法之一。

如果您使用自己的实施替换+[NSDate date]NSDataDetector会在您指定的任何时候考虑“现在”。

生产代码中的混合系统类方法存在风险。以下示例代码通过利用知道它使用NSDataDetector来忽略NSDate NSDate私人。如果iOS的下一次更新改变了NSDataDetector的内部,你的生产应用程序可能会意外停止为最终用户正常工作,那么很多潜在的陷阱之一就是。

像这样向#include <objc/runtime.h> @implementation NSDate(freezeDate) static NSDate *_freezeDate; // Freeze NSDate to a point in time. // PROBABLY NOT A GOOD IDEA FOR PRODUCTION CODE +(void)freezeToDate:(NSDate*)date { if(_freezeDate != nil) [NSDate unfreeze]; _freezeDate = date; Method _original_date_method = class_getClassMethod([NSDate class], @selector(date)); Method _fake_date_method = class_getClassMethod([self class], @selector(fakeDate)); method_exchangeImplementations(_original_date_method, _fake_date_method); } // Unfreeze NSDate so that now will really be now. + (void)unfreeze { if(_freezeDate == nil) return; _freezeDate = nil; Method _original_date_method = class_getClassMethod([NSDate class], @selector(date)); Method _fake_date_method = class_getClassMethod([self class], @selector(fakeDate)); method_exchangeImplementations(_original_date_method, _fake_date_method); } + (NSDate *)fakeDate { return _freezeDate; } @end 添加一个类别(旁白:如果要构建要在设备上运行的库,Encapsulation从libs加载类别):

- (void)someTestingFunction:(NSNotification *)aNotification
{
    // Set date to be frozen at a point one week ago from now.
    [NSDate freezeToDate:[NSDate dateWithTimeIntervalSinceNow:(-3600*24*7)]];

    NSString *userInput = @"tomorrow at 7pm";
    NSError *error = nil;
    NSRange range = NSMakeRange(0, userInput.length);
    NSDataDetector *dd = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeDate error:&error];
    [dd enumerateMatchesInString:userInput
                         options:0
                           range:range
                      usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) {
                          NSLog(@"From one week ago: %@", match);
                      }];

    // Return date to normal
    [NSDate unfreeze];

    [dd enumerateMatchesInString:userInput
                         options:0
                           range:range
                      usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) {
                          NSLog(@"From now: %@", match);
                      }];

}

以下是它的使用:

{{1}}

哪个输出:

2014-01-20 19:35:57.525 TestObjectiveC2[6167:303] From one week ago: {0, 15}{2014-01-15 03:00:00 +0000}
2014-01-20 19:35:57.526 TestObjectiveC2[6167:303] From now: {0, 15}{2014-01-22 03:00:00 +0000}

答案 1 :(得分:1)

使用NSDataDetector无法做到这一点。请与Apple file a bug申请此功能。

答案 2 :(得分:0)

据我所知,通过NSDataDetector无法做到这一点。

也就是说,手动应用偏移应该非常简单 - 只需从NSDateComponenets计算所需偏移量的NSCalendar,然后从NSDataDetector获取检测到的日期,根据需要添加day日期组件偏移量。

    NSDate *futureDate = [NSDate dateWithTimeIntervalSinceNow:100000]; // Offset by about a day from now
    __block NSDate *offsetDetectedDate = nil;

    NSString *string = @"Let's have lunch Tomorrow at Noon";
    NSDataDetector *dataDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeDate error:nil];
    [dataDetector enumerateMatchesInString:string options:0 range:NSMakeRange(0, [string length]) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
        if (result.resultType == NSTextCheckingTypeDate) {
            NSDateComponents *offsetDateComponents = [[NSCalendar currentCalendar] components:NSDayCalendarUnit fromDate:result.date toDate:futureDate options:0];
            offsetDetectedDate = [[NSCalendar currentCalendar] dateByAddingUnit:NSDayCalendarUnit value:offsetDateComponents.day toDate:result.date options:0];
            *stop = YES;
        }
    }];

    NSLog(@"%@", offsetDetectedDate);