You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift iOS中如何检测含上周、下月等的自然语言日期?

Swift中识别自然语言相对日期的解决方案

问题背景

使用NSDataDetector的.date检测类型可以识别包含具体日期组件的字符串(如“next Monday at 3 PM”),但无法识别“last week”、“next month”、“last year”这类相对日期表述;而RelativeDateTimeFormatter仅支持将Date对象转换为相对日期字符串,无法反向解析。

可行解决方案

1. 结合NSDataDetector与自定义规则补全

先通过NSDataDetector处理常规可识别的日期,若未匹配到结果,再用正则匹配常见相对日期关键词,通过Calendar手动计算对应日期:

func extractNaturalLanguageDate(from input: String) -> Date? {
    // 第一步:尝试NSDataDetector识别常规日期
    let dateCheckingType = NSTextCheckingResult.CheckingType.date.rawValue
    guard let detector = try? NSDataDetector(types: dateCheckingType) else { return nil }
    
    let matches = detector.matches(
        in: input,
        options: [],
        range: NSRange(location: 0, length: input.utf16.count)
    )
    
    if let firstMatch = matches.first, let detectedDate = firstMatch.date {
        return detectedDate
    }
    
    // 第二步:处理相对日期的正则匹配与计算
    let relativeDateRules: [(pattern: String, calendarComponent: Calendar.Component, offset: Any)] = [
        ("last\\s+week", .weekOfYear, -1),
        ("next\\s+week", .weekOfYear, 1),
        ("last\\s+month", .month, -1),
        ("next\\s+month", .month, 1),
        ("last\\s+year", .year, -1),
        ("next\\s+year", .year, 1),
        ("(\\d+)\\s+weeks?\\s+ago", .weekOfYear, { (match: NSTextCheckingResult) in -Int(match.range(at: 1).substring(from: input))! }),
        ("(\\d+)\\s+months?\\s+ago", .month, { (match: NSTextCheckingResult) in -Int(match.range(at: 1).substring(from: input))! })
    ]
    
    extension NSRange {
        func substring(from string: String) -> String {
            guard let range = Range(self, in: string) else { return "" }
            return String(string[range])
        }
    }
    
    let calendar = Calendar.current
    for rule in relativeDateRules {
        guard let regex = try? NSRegularExpression(pattern: rule.pattern, options: .caseInsensitive) else { continue }
        guard let match = regex.firstMatch(in: input, options: [], range: NSRange(location: 0, length: input.utf16.count)) else { continue }
        
        var offsetValue = 0
        if let fixedOffset = rule.offset as? Int {
            offsetValue = fixedOffset
        } else if let dynamicOffset = rule.offset as? ((NSTextCheckingResult) -> Int) {
            offsetValue = dynamicOffset(match)
        }
        
        return calendar.date(byAdding: rule.calendarComponent, value: offsetValue, to: Date())
    }
    
    return nil
}

// 测试示例
if let date = extractNaturalLanguageDate(from: "we had the meeting last month") {
    print(date.formatted(date: .complete, time: .omitted))
}
if let date = extractNaturalLanguageDate(from: "I will be free next Monday at 3 PM") {
    print(date.formatted(date: .complete, time: .shortened))
}
if let date = extractNaturalLanguageDate(from: "3 weeks ago") {
    print(date.formatted(date: .complete, time: .omitted))
}

2. 利用NaturalLanguage框架识别日期关键词

使用NLTagger识别文本中的日期相关词汇,再根据词汇类型计算对应日期:

import NaturalLanguage

func extractDateWithNLTagger(from input: String) -> Date? {
    let tagger = NLTagger(tagSchemes: [.nameTypeOrLexicalClass])
    tagger.string = input
    
    let options: NLTagger.Options = [.omitWhitespace, .omitPunctuation]
    var relativeComponent: Calendar.Component?
    var offset = 0
    
    tagger.enumerateTags(
        in: input.startIndex..<input.endIndex,
        unit: .word,
        scheme: .nameTypeOrLexicalClass,
        options: options
    ) { tag, range in
        let word = input[range].lowercased()
        switch word {
        case "last":
            offset = -1
        case "next":
            offset = 1
        case "week", "weeks":
            relativeComponent = .weekOfYear
        case "month", "months":
            relativeComponent = .month
        case "year", "years":
            relativeComponent = .year
        default:
            break
        }
        return true
    }
    
    if let component = relativeComponent {
        return Calendar.current.date(byAdding: component, value: offset, to: Date())
    }
    
    // 若未识别到相对日期,回退到NSDataDetector逻辑
    return extractNaturalLanguageDate(from: input)
}

注意事项

  • 正则规则需要根据需求扩展,覆盖更多相对日期表述(如“a month ago”、“this weekend”等);
  • 多语言场景下,需适配对应语言的关键词与正则;
  • 若需要更复杂的自然语言解析能力,可考虑使用专门的日期解析第三方库。

内容的提问来源于stack exchange,提问作者Tobi Omotayo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 06:53:15