[英]In C#, what is the best way to find gaps in a DateTime array?
我有一個相隔一個月的日期列表,因為所有日期都是“本月的第一個星期一”。 在某些情況下,幾個月都會丟失,所以我需要編寫一個函數來確定所有日期是否都是連續的
因此,例如,如果這是日期列表,則函數將返回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);
所以我試圖找出IsThisListConsecutive()方法的正確邏輯:
這是我的第一次嘗試:(注意我已經預先知道所有日期都是一周的同一天和同月的一周,所以我唯一需要的是缺少的插槽)
private bool IsThisListConsecutive(IEnumerable<DateTime> orderedSlots)
{
DateTime firstDate = orderedSlots.First();
int count = 0;
foreach (var slot in orderedSlots)
{
if (slot.Month != firstDate.AddMonths(count).Month)
{
return false;
}
count++;
}
return true;
}
如果列表從一年跨越到另一年,則上面的代碼將起作用。 我希望得到關於創建此函數的更好方法的任何建議,以及如何重寫該行以處理跨越多年的日期。
因此,為了實現這一點,我們將從一個簡單的輔助方法開始,該方法接受一個序列並返回一系列對,這些對組成了每個項目的前一個項目。
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));
}
我建議看一下TimeSpan
結構。 由於運算符過載,您可以通過減去兩個日期來獲取TimeSpan
,然后接收表示兩個日期之間差異的TimeSpan
。
http://msdn.microsoft.com/en-us/library/system.timespan.aspx
注意:這是完全未經測試的,日期檢查可能非常糟糕或有些多余,但這是我現在能想到的最好的^^
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;
}
好吧,你的代碼在歲月交叉時不起作用因為jan 1st可能是一年的星期一和下一個的星期二。 如果我這樣做,我會先檢查一下
a)它們是每個月的一周中的同一天(使用DateTime.DayOfWeek)
b)它們是每個月的同一周*使用擴展方法DayOfMonth(參見鏈接)* 計算.NET *中的星期幾 *
(你說你已經知道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;
如果您需要檢查條件a和b以及添加更改if語句,如下所示
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;
}
記住要使用get week of month擴展方法,你必須在.NET中包含計算星期幾中的代碼我確信在文本編輯器中有這樣的拼寫錯誤。
好吧,這是我最初想到如何解決這個問題。
首先,定義一個函數,它將日期轉換為與它們應該出現的順序相對應的序數值。
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]
從這里開始迭代列表並確保整數按順序出現而沒有間隙或停頓只是一個小問題 - 也就是說,每個項目/整數正好比前一個更多。
我可能會誤解你想要做什么,但我認為這會有效,假設你不必處理古代日期。 查看轉換為“總月數”的日期是否存在任何差距
int totalMonths = date.Year * 12 + (date.Month - 1);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.