2013-03-11 46 views
5

我知道所有的日期都是「本月的第一個星期一」,因此我有一個月的日期列表。在某些情況下,我需要編寫一個函數來確定是否所有日期都是連續的在C#中,找到DateTime數組中的空白的最佳方法是什麼?

因此,例如,如果這是日期列表,則函數將返回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; 
    } 

該代碼上述工程exept如果列表從一年跨越到另一個。我想獲得任何建議,以創建這個函數的更好方式,以及如何重寫這條線以處理跨越多年的日期。

+1

代碼中的'orderedSlots'來自哪裏?另外,我認爲你以奇怪的方式使用「連續」這個詞。 – poke 2013-03-11 01:30:59

+0

@poke - 我在orderedSlots的代碼中修正了這個類型。你能想到一個更好的詞用來比較「連續」來看看我在看什麼 – leora 2013-03-11 01:34:10

+0

順便說一句。 「date」和「date1」是星期三,但是「date2」和「date3」是星期四? – poke 2013-03-11 01:34:25

回答

2

注:這是完全未經測試,和日期檢查可能是非常糟糕或者有些多餘,但這是我能想出現在^^

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; 
} 
+0

爲什麼你選擇直接使用'IEnumerable '接口而不是foreach循環? – 2013-03-11 01:56:20

+1

@NathanAnderson好問題。我想單獨挑選第一個元素,但不想做'bool first = true',或者讓'weekday'和'previous'爲空...... – poke 2013-03-11 01:57:44

+0

有道理。感謝澄清。 – 2013-03-11 01:59:55

2

我建議看看TimeSpan結構。由於操作員超載,您可以通過減去兩個日期得到TimeSpan,然後收到表示兩個日期之間差異的TimeSpan

http://msdn.microsoft.com/en-us/library/system.timespan.aspx

+0

使用Timespan的問題是,減去日期無助於比較邏輯(因爲它不是一個直接的月份) – leora 2013-03-11 01:40:15

+0

如果使用模運算符並且一些邏輯可以適應30,60或相隔幾天的日期(但相隔數月)。 – 2013-03-11 01:43:10

+0

@NathanAnderson不是所有的月份都是相同的長度和平日(特別是第一個/最後一個)不一致與月份日期。日期是棘手的。 – 2013-03-11 01:43:47

0

我可以在最好的誤解你正在嘗試做的事情,但我認爲這會起作用,假設你不必處理古老的日期。查看是否有在日期的任何空白轉換爲「總月」

int totalMonths = date.Year * 12 + (date.Month - 1); 
2

沒關係,當歲月跨越監守1月1日你的代碼不工作可能是上一年度週一和下一個星期二。如果我這樣做,我會首先檢查:

a)它們是每個月中一週中的同一天(使用DateTime。星期幾)

二)他們每月的同一個星期每個月* 使用擴展方法DAYOFMONTH(見鏈接) * Calculate week of month in .NET *

(你說你已經知道& 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; 

,如果你需要檢查條件& 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一週,你必須包括在 Calculate week of month in .NET 我的代碼肯定有錯別字,因爲我在文本編輯器中這樣做了。

1

那麼,這裏是我的最初想到我將如何解決這個問題。

首先,定義一個函數將日期轉換爲對應於它們出現的順序的序號值。

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] 

從這裏它只是遍歷列表的小事和確保整數順序沒有間隙或停頓 - 也就是說,每個項目/整數比前一個多一個。

+0

'd.Month' 1月份返回1,因此您需要減去1.否則2012年12月== 2013年1月。 – 2013-03-11 13:49:03

+0

@ default.kramer哎呀!更正,謝謝。 – 2013-03-11 16:25:30

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)); 
} 
+0

@pst正確,固定。噢,如果你做了'source.Zip(source.Skip(1)''',那麼使用'Zip'會導致序列迭代兩次,所以爲了避免雙迭代(並且保持懶惰),你不能使用' Zip'。 – Servy 2013-03-11 17:10:16

+0

嗯,我沒有想過使用Zip的情況..我猜想通過一個List並且通常不會考慮它(我通常不是這方面的高效程序員,但我聽ReSharpers「雙重評估」警告,並在很多情況下發出ToList,在這種情況下,我不需要*懶惰。) – 2013-03-11 17:11:37

+0

@pst處理特定情況時,您知道您傳遞的大小可能是沒問題,但在編寫更多通用的實用方法時,如果您不知道數據集的大小(或者在因特網上爲陌生人編寫代碼時,您不知道數據集的大小),最好是儘可能少的假設。 – Servy 2013-03-11 17:15:23

相關問題