8

假设今天是 2014 年 1 月 20 日。如果我使用 NSDataDetector 从字符串"tomorrow at 4pm"中提取日期,我将得到2014-01-21T16:00。伟大的。

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

谢谢。

4

3 回答 3

2

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

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

在生产代码中混杂系统类方法是有风险的。以下示例代码通过利用知道它是私有的来忽略封装。许多潜在的陷阱之一是,如果 iOS 的下一次更新更改了 NSDataDetector 的内部结构,您的生产应用程序可能会意外停止为最终用户正常工作NSDataDetectorNSDate

添加一个NSDate像这样的类别(顺便说一句:如果您正在构建要在设备上运行的库,您可能需要指定 -all_load 链接器标志以从库加载类别):

#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

这是它正在使用的:

- (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);
                      }];

}

哪个输出:

2014-01-20 19:35:57.525 TestObjectiveC2[6167:303] 从一周前开始:{0, 15}{2014-01-15 03:00:00 +0000}
2014-01-20 19:35:57.526 TestObjectiveC2[6167:303] 从现在开始:{0, 15}{2014-01-22 03:00:00 +0000}
于 2014-01-21T03:53:45.353 回答
1

NSDataDetector 无法做到这一点。请向 Apple 提交错误以请求此功能。

于 2014-03-19T10:52:23.117 回答
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);
于 2014-01-20T23:08:21.930 回答