在C#中,在DateTime数组中查找间隙的最佳方法是什么?

在C#中,在DateTime数组中查找间隙的最佳方法是什么?,c#,datetime,collections,C#,Datetime,Collections,我有一个相隔一个月的日期列表,因为所有日期都是“一个月的第一个星期一”。在某些情况下,缺少月份,因此我需要编写一个函数来确定所有日期是否连续 因此,例如,如果这是日期列表,函数将返回true,因为所有项目都是“本月的第一个星期五”,并且没有间隔。下面的示例将返回true var date = new DateTime(2013, 1, 4); var date1 = new DateTime(2013, 2, 1); var date2 = new DateTime(2013, 3, 1)

我有一个相隔一个月的日期列表,因为所有日期都是“一个月的第一个星期一”。在某些情况下,缺少月份,因此我需要编写一个函数来确定所有日期是否连续

因此,例如,如果这是日期列表,函数将返回true,因为所有项目都是“本月的第一个星期五”,并且没有间隔。下面的示例将返回true

 var date = new DateTime(2013, 1, 4);
 var date1 = new DateTime(2013, 2, 1);
 var date2 = new DateTime(2013, 3, 1);
 var date3 = new DateTime(2013, 4, 5);

 var dateArray = new DateTime[]{date, date1, date2, date3};
 bool isConsecutive = IsThisListConsecutive(dateArray);
下面的示例将返回false,因为尽管它们也是“本月的第一个星期五”,但缺少2013年3月的项目

 var date = new DateTime(2013, 1, 4);
 var date1 = new DateTime(2013, 2, 1);
 var date3 = new DateTime(2013, 4, 5);

 var dateArray = new DateTime[]{date, date1, date3};
 bool isConsecutive = IsThisListConsecutive(dateArray);
因此,我试图为IsThisListContinuous()方法找出正确的逻辑:

这是我的第一次尝试:(注意,我已经预先知道所有日期都是一周中的同一天和一个月中的同一周,所以我唯一要找的就是缺少一个插槽)

private bool Isthislist连续(IEnumerable orderedSlots)
{
DateTime firstDate=orderedSlots.First();
整数计数=0;
foreach(orderedSlots中的var插槽)
{
if(slot.Month!=firstDate.AddMonths(count.Month)
{
返回false;
}
计数++;
}
返回true;
}

如果列表从一年过渡到另一年,则上述代码除外。我想获得关于创建此函数的更好方法以及如何重写该行以处理跨年日期的任何建议。

我建议查看
TimeSpan
结构。由于运算符重载,您可以通过减去两个日期获得
TimeSpan
,然后接收表示两个日期之间差异的
TimeSpan


注意:这是完全未经测试的,日期检查可能非常糟糕或有点多余,但这是我现在能想到的最好的方法^^

public bool AreSameWeekdayEveryMonth(IEnumerable<DateTime> dates)
{
    var en = dates.GetEnumerator();
    if (en.MoveNext())
    {
        DayOfWeek weekday = en.Current.DayOfWeek;
        DateTime previous = en.Current;
        while (en.MoveNext())
        {
            DateTime d = en.Current;
            if (d.DayOfWeek != weekday || d.Day > 7)
                return false;
            if (d.Month != previous.Month && ((d - previous).Days == 28 || (d - previous).Days == 35))
                return false;
            previous = d;
        }
    }
    return true;
}
public bool是SameWeekdayeverymonth(IEnumerable日期)
{
var en=dates.GetEnumerator();
if(en.MoveNext())
{
DayOfWeek weekday=en.Current.DayOfWeek;
DateTime previous=当前日期;
while(en.MoveNext())
{
DateTime d=当前日期;
如果(d.DayOfWeek!=工作日| | d.Day>7)
返回false;
如果(d.Month!=上一个月&((d-上一个).Days==28 | | |(d-上一个).Days==35))
返回false;
先前=d;
}
}
返回true;
}

我可能误解了您的意图,但我认为如果您不必处理古代日期,这会起作用。查看转换为“总月数”的日期是否存在任何差距


好的,你的代码在几年过去的时候不起作用,因为1月1日可能是一年的星期一,下一年的星期二。如果我这样做,我会先检查一下

a) 它们是每个月一周中的同一天(使用DateTime.DayOfWeek)

b) 它们是每个月的同一周* 使用扩展方法DayOfMonth(参见链接) **

(你说你已经知道a&b是真的,所以让我们继续第三个条件)

c) 我们必须确定他们是否在连续几个月内

//order the list of dates & place it into an array for ease of looping
DateTime[] orderedSlots = slots.OrderBy( t => t).ToArray<DateTime>();


//create a variable to hold the date from the previous month
DateTime temp = orderedSlots[0];


for(i= 1; index < orderedSlots.Length; index++)
{
    if((orderedSlots[index].Month != temp.AddMonths(1).Month |
        orderedSlots[index].Year  != temp.AddMonths(1).Year)){
        return false;
    }

    previousDate =  orderedSlots[index];
}

return true;
记住,要使用get week of month扩展方法,必须将代码包含在 我确信在文本编辑器中会出现输入错误。

好吧,下面是我对如何解决这个问题的初步想法

首先,是定义一个函数,该函数将日期转换为对应于其显示顺序的序数值


从这里开始,只需迭代列表并确保整数按顺序出现,而不存在间隙或停顿,这是一个很小的问题-也就是说,每个项/整数都比前一项多出一个。

因此,为了实现这一点,我们将从一个简单的helper方法开始,该方法获取一个序列并返回一个配对序列,这些配对序列构成了每个项及其前一项

public static IEnumerable<Tuple<T, T>> Pair<T>(this IEnumerable<T> source)
{
    T previous;
    using (var iterator = source.GetEnumerator())
    {
        if (iterator.MoveNext())
            previous = iterator.Current;
        else
            yield break;

        while(iterator.MoveNext())
        {
            yield return Tuple.Create(previous, iterator.Current);
            previous = iterator.Current;
        }
    }
}
使用它,我们可以轻松地抓取每个日期的月份,并查看它是否是前一个月之后的月份。如果所有对都是这样,那么我们有连续的几个月

private static bool IsThisListConsecutive(IEnumerable<DateTime> orderedSlots)
{
    return orderedSlots.Pair()
        .All(pair => AreSameMonth(pair.Item1.AddMonths(1), pair.Item2));
}
private static bool IsthislistContinuous(IEnumerable orderedSlots)
{
返回orderedSlots.Pair()
.All(pair=>AreSameMonth(pair.Item1.AddMonths(1),pair.Item2));
}

代码中的
orderedSlot
来自哪里?另外,我认为您使用“连续”这个词的方式很奇怪。@poke-我在orderedSlots的代码中修改了类型。你能想出一个比“连续”更好的词来表达我的意思吗。是不是打算把
date
date1
定为星期三,而
date2
date3
定为星期四?@poke-我把2012年而不是2013年定为一年。我刚刚修复了这个(谢谢你指出)。所有日期都是一周中的同一天和一个月中的同一周使用Timespan的问题是,如果使用模数运算符和一点逻辑,则减去日期对比较逻辑没有帮助(因为它不是直接相隔一个月),您可以充分适应相隔30天、60天或任何天数的日期(但仍然相隔偶数个月)。@NathanAnderson并非所有月份的长度和工作日都相同(尤其是第一个/最后一个月)不要统一与月日对齐。日期非常棘手。@pst非常有效。正如我们所见,重复出现是一个棘手的问题,有很多边缘情况。过去我利用了一个名为
Aspose.iCalendar
的程序集来帮我解决这个问题,但似乎不再提供此组件。这似乎是一个类似的解决方案:我以前使用过一个名为recurse.net的开源项目,它可以为日历/日程安排应用程序生成重复日期列表。你可以将“引擎”拉到一些类中并放弃GUI,但GUI有助于lear
    if( orderedSlots[index].Month != temp.AddMonths(1).Month |
        orderedSlots[index].Year  != temp.AddMonths(1).Year) |
        orderedSlots[index].DayOfWeek != temp.DayOfWeek      |
        orderedSlots[index].GetWeekOfMonth != temp.AddMonths(1).GetWeekOfMonth){
        return false;
    }
int ToOrdinal(DateTime d, DateTime baseline) {
   if (d.Day <= 7
       && d.DayInWeek == baseline.DayInWeek) {
      // Since there is only one "First Friday" a month, and there are
      // 12 months in year we can easily compose the ordinal.
      // (As per default.kramer's comment, months normalized to [0,11].)
      return d.Year * 12 + (d.Month - 1);
   } else {
      // Was not correct "kind" of day -
      // Maybe baseline is Tuesday, but d represents Wednesday or
      // maybe d wasn't in the first week ..
      return 0;
   }
}

var dates = ..;
var baseline = dates.FirstOrDefault();
var ordinals = dates.Select(d => ToOrdinal(d, baseline));
[24156 + 0, 24156 + 1, 24156 + 2, 24156 + 3]
[24156 + 0, 24156 + 1, /* !!!! */ 24156 + 3]
public static IEnumerable<Tuple<T, T>> Pair<T>(this IEnumerable<T> source)
{
    T previous;
    using (var iterator = source.GetEnumerator())
    {
        if (iterator.MoveNext())
            previous = iterator.Current;
        else
            yield break;

        while(iterator.MoveNext())
        {
            yield return Tuple.Create(previous, iterator.Current);
            previous = iterator.Current;
        }
    }
}
public static bool AreSameMonth(DateTime first, DateTime second)
{
    return first.Year == second.Year 
        && first.Month == second.Month;
}
private static bool IsThisListConsecutive(IEnumerable<DateTime> orderedSlots)
{
    return orderedSlots.Pair()
        .All(pair => AreSameMonth(pair.Item1.AddMonths(1), pair.Item2));
}