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

如何使用Microsoft DateTimeRecognizer提取日期范围中的单个日期文本?

如何使用Microsoft DateTimeRecognizer提取日期范围中的单个日期文本?

你遇到的问题很典型——Microsoft DateTimeRecognizer会将「3pm Monday to 3pm next Tuesday」这类日期范围识别为一个整体的范围类型实体(比如datetimeV2.datetimerange),而不是两个独立的日期/时间实体,所以你现有的逻辑会直接跳过这种情况。下面我来拆解下具体的解决方案,帮你扩展代码以支持提取范围中的单个日期文本和对应时间:


1. 先理解范围类型的识别结果结构

当Recognizer识别到日期/时间范围时,返回的ModelResult会有这些核心特征:

  • TypeName会是datetimeV2.daterange(日期范围)、datetimeV2.timerange(时间范围)或datetimeV2.datetimerange(日期时间范围)
  • Children属性会包含两个子ModelResult,分别对应范围的起始端点和结束端点
  • 每个子ModelResult的结构和你之前处理的单个日期/时间实体完全一致:TypeName为datetimeV2.date/time/datetime,Start和End标记了该端点在原字符串中的准确位置

2. 两种扩展方案(根据你的需求选择)

方案一:扩展函数,返回范围中的所有端点

如果你需要完整提取范围里的两个日期/时间(比如同时拿到「3pm Monday」和「3pm next Tuesday」),建议调整函数返回类型为包含多个结果的列表:

using System.Collections.Generic;
using System.Linq;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.DateTime;
using JetBrains.Annotations;

public static class DateTimeExtensions
{
    public static bool TryGetDateTimesAtStringStart(
        this string timexString, 
        DateTime? referenceTime,
        [NotNullWhen(true)] out List<(DateTime? Date, string Text)>? dateTimeInfos)
    {
        dateTimeInfos = null;
        var results = DateTimeRecognizer.RecognizeDateTime(
            timexString, 
            Culture.English, 
            refTime: referenceTime ?? DateTime.Today);

        if (results.FirstOrDefault() is not ModelResult mainResult)
        {
            return false;
        }

        // 处理单个日期/时间/日期时间的原有逻辑
        if (mainResult.Start == 0 && 
            mainResult.TypeName is "datetimeV2.date" or "datetimeV2.time" or "datetimeV2.datetime")
        {
            var resolutionValues = (IList<Dictionary<string, string>>)mainResult.Resolution["values"];
            var mainResolution = resolutionValues[0];
            var dateTime = HandlePointInTime(mainResolution);
            
            if (dateTime != null)
            {
                dateTimeInfos = new List<(DateTime? Date, string Text)>
                {
                    (dateTime, timexString.Substring(0, mainResult.End - mainResult.Start + 1))
                };
                return true;
            }
            return false;
        }

        // 新增:处理日期/时间范围的逻辑
        if (mainResult.Start == 0 && 
            mainResult.TypeName is "datetimeV2.daterange" or "datetimeV2.timerange" or "datetimeV2.datetimerange")
        {
            dateTimeInfos = new List<(DateTime? Date, string Text)>();

            // 遍历范围的两个端点子实体
            foreach (var childResult in mainResult.Children)
            {
                if (childResult.TypeName is "datetimeV2.date" or "datetimeV2.time" or "datetimeV2.datetime")
                {
                    var resolutionValues = (IList<Dictionary<string, string>>)childResult.Resolution["values"];
                    var childResolution = resolutionValues[0];
                    var dateTime = HandlePointInTime(childResolution);
                    
                    if (dateTime != null)
                    {
                        // 从原字符串中提取该端点对应的文本
                        var endpointText = timexString.Substring(childResult.Start, childResult.End - childResult.Start + 1);
                        dateTimeInfos.Add((dateTime, endpointText));
                    }
                }
            }

            return dateTimeInfos.Any();
        }

        return false;
    }

    // 保留你原有的HandlePointInTime方法
    private static DateTime? HandlePointInTime(Dictionary<string, string> resolution)
    {
        // 这里是你原有的解析逻辑,比如从timex或其他字段转换为DateTime
        // 示例实现(根据你的实际逻辑调整):
        if (resolution.TryGetValue("value", out string value) && DateTime.TryParse(value, out var dt))
        {
            return dt;
        }
        return null;
    }
}

方案二:保持原函数签名,仅提取范围的第一个端点

如果你希望原函数的输入输出结构不变,只需要拿到范围中的第一个日期/时间(比如「3pm Monday to 3pm next Tuesday」里的「3pm Monday」),可以修改原函数的判断逻辑:

using System.Linq;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.DateTime;
using JetBrains.Annotations;

public static class DateTimeExtensions
{
    public static bool TryGetDateTimeAtStringStart(
        this string timexString, 
        DateTime? referenceTime,
        [NotNullWhen(true)] out DateTime? dateTime, 
        [NotNullWhen(true)] out string? text)
    {
        dateTime = null;
        text = null;
        var results = DateTimeRecognizer.RecognizeDateTime(
            timexString, 
            Culture.English, 
            refTime: referenceTime ?? DateTime.Today);

        if (results.FirstOrDefault() is not ModelResult mainResult)
        {
            return false;
        }

        // 原有单个日期/时间的处理逻辑
        if (mainResult.Start == 0 && 
            mainResult.TypeName is "datetimeV2.date" or "datetimeV2.time" or "datetimeV2.datetime")
        {
            var resolutionValues = (IList<Dictionary<string, string>>)mainResult.Resolution["values"];
            var mainResolution = resolutionValues[0];
            dateTime = HandlePointInTime(mainResolution);
            
            if (dateTime != null)
            {
                text = timexString.Substring(0, mainResult.End - mainResult.Start + 1);
                return true;
            }
            return false;
        }

        // 新增:处理范围类型,提取第一个端点
        if (mainResult.Start == 0 && 
            mainResult.TypeName is "datetimeV2.daterange" or "datetimeV2.timerange" or "datetimeV2.datetimerange")
        {
            // 找到范围中的第一个有效日期/时间端点
            var firstEndpoint = mainResult.Children.FirstOrDefault(
                c => c.TypeName is "datetimeV2.date" or "datetimeV2.time" or "datetimeV2.datetime");
            
            if (firstEndpoint != null)
            {
                var resolutionValues = (IList<Dictionary<string, string>>)firstEndpoint.Resolution["values"];
                var childResolution = resolutionValues[0];
                dateTime = HandlePointInTime(childResolution);
                
                if (dateTime != null)
                {
                    text = timexString.Substring(firstEndpoint.Start, firstEndpoint.End - firstEndpoint.Start + 1);
                    return true;
                }
            }
        }

        return false;
    }

    private static DateTime? HandlePointInTime(Dictionary<string, string> resolution)
    {
        // 你的原有解析逻辑,示例实现:
        if (resolution.TryGetValue("value", out string value) && DateTime.TryParse(value, out var dt))
        {
            return dt;
        }
        return null;
    }
}

3. 关键注意事项

  • 确保你的HandlePointInTime方法能兼容子端点的Resolution数据(格式和单个日期的Resolution完全一致,无需额外修改)
  • DateTimeRecognizer会自动处理各种范围分隔符(比如to、until、from...to...、-等),Children属性会准确对应每个端点的位置
  • 如果需要支持非英语场景,记得将Culture.English替换为对应的文化信息(比如Culture.Chinese)

内容来源于Stack Exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 11:48:05