【问题标题】:How do I get the closest DateTime from a List<DateTime>如何从 List<DateTime> 中获取最接近的 DateTime
【发布时间】:2015-03-03 03:34:42
【问题描述】:

假设我有最近的 DateTime 和所有可能日期的列表。我将如何有效地在列表中找到最接近去年日期的日期时间?

假设我的清单包含以下内容:

2014-03-07
2014-03-14
2014-03-21
2014-03-28
...
2015-03-06
2015-03-13
2015-03-20

我最近的日期是 2015-03-20,但我想检索去年的日期 2014-03-21

这是我目前拥有的,但如果去年的日期是一天假(例如,我的时间段每周存储),它将不起作用。

public DateTime LastYearDate()
{
    List<DateTime> times = GetAllDates();
    times.Sort();
    times.Reverse();
    DateTime currentDate = times.First();
    return times.Where(dt => dt == currentDate.AddYears(-1)).First();
}

我不确定我会用什么来递归计算最接近的日期,所以如果你对我应该采取的方向有任何想法(参考任何 Linq 函数来检查),我将不胜感激。

【问题讨论】:

  • “去年的日期”到底是什么意思?如果您能提供一个简短但完整的示例,将会有所帮助。
  • 我的意思是我提供的日期时间,但一年前。我用一个例子更新了这个问题。
  • 列表是否已经排序?您可以从最近的条目中返回 52 个条目。
  • @mikez 列表 与时间段不一致,可以是周、月、季度、年等。

标签: c# linq list datetime


【解决方案1】:

只需按列表中的日期与您要查找的日期之间的差异排序:

var dateToFind = currentDate.AddYears(-1);
times.OrderBy(t => (t - dateToFind).Duration).FirstOrDefault();

(两个日期的差是TimeSpan的实例;Duration属性返回绝对值)

【讨论】:

  • 您不需要按绝对时间跨度订购吗?
  • 这只会在列表中找到最早的日期而不是最接近的日期。
  • 如果最接近去年的日期比您提供的日期早一天会发生什么?例如,我有 2014 年 3 月 21 日和 2014 年 3 月 26 日。我现在的日期是 2015 年 3 月 25 日。这只会给我 2014 年 3 月 21 日,而不是 2014 年 3 月 26 日。
  • @EthanDeLong 由Math.Abs((t-dateToFind).TotalDays) 订购
  • @spender,好点子!我修好了,谢谢。顺便说一句,有一种比 Math.Abs​​ 更简单的方法:Timespan.Duration 属性。
【解决方案2】:

在排序后,您可以使用二进制搜索来尝试找到完全匹配的内容。如果List&lt;T&gt;.BinarySearch 返回一个非负数,您就知道您找到了完全匹配。否则,您可以应用按位补码运算符来查找将插入值的索引。然后,您需要检查该索引之前或该索引处的值是否离目标更远。所以是这样的:

var target = currentDate.AddYears(-1);
List<DateTime> times = GetAllDates();
if (times.Count == 0)
{
    // ??? Work out what you want to do here, e.g. throw an exception
}
times.Sort();
var index = times.BinarySearch(target);
if (index >= 0)
{
    return times[index];
}
int insertIndex = ~index;
// Handle boundary cases
if (insertIndex == 0)
{
    return times[0];
}
if (insertIndex == times.Count)
{
    return times[insertIndex - 1];
}
// Okay, two options - find the closest
var timeBefore = times[insertIndex - 1];
var timeAfter = times[insertIndex];
// TODO: Work out what you want to do if they're equidistant.
return target - timeBefore > timeAfter - target ? timeAfter : timeBefore;

话虽如此,spender 对 Thomas Levesque 回答的评论给出了一个非常简单的解决方案:

var target = currentDate.AddYears(-1);
List<DateTime> times = GetAllDates();
if (times.Count == 0)
{
    // ??? Work out what you want to do here, e.g. throw an exception
}
return times.OrderBy(t => (target - t).Duration).First();

请注意,TimeSpan.Duration 始终为非负数;这就像 Math.Abs 但对于 TimeSpan 值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-11
    • 2023-03-10
    • 1970-01-01
    • 2015-06-22
    • 2016-07-19
    • 2011-07-12
    • 2020-10-13
    相关资源
    最近更新 更多