2013-03-11 13 views
5

Tüm tarihlerin "Ayın İlk Paztası" olduğu bir ayın dışında bir tarihler listesi var. Bazı durumlarda aylar eksik olduğundan, tüm tarihlerin birbirini takip edenC# 'da, bir DateTime dizisindeki boşlukları bulmanın en iyi yolu nedir?

olup olmadığını belirlemek için bir işlev yazmam gerekiyor. Örneğin, bu tarihler listesiyse, tüm öğeler "İlk Cuma günü ay "ve boşluklar yok. Aşağıdaki bu örnek doğruya dönecektir. onlar da hepsi "Ayın ilk Cuma" olsa bile, çünkü aşağıda bu örnek yanlış dönecekti

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); 

, onun Mart 2013 öğeyi eksik.
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); 

yüzden (IsThisListConsecutive için doğru mantığı anlamaya çalışıyorum) yöntemi: İşte

benim ilk denemede oldu: (Zaten tüm tarihler haftanın aynı aynı gün olduğuna açık biliyoruz Not ayın hafta bu yüzden arıyorum tek şey eksik bir yuva) liste bir yıldan arta haçlar exept eğer işler yukarıda

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

Bu kodudur. Bu işlevi oluşturmak için daha iyi bir yol hakkında tavsiyede bulunmak istedim ve bu çizginin yıllar geçtikçe tarihlerle başa çıkabilmek için nasıl yeniden yazılabileceğini öğrenmek istedim.

+1

Kodunuzdaki 'orderSlots' nereden geliyor? Ayrıca "ardışık" kelimesini garip bir şekilde kullandığınızı düşünüyorum. – poke

+0

@poke - Siparişi verilen kodun türünü siparişe göre sabitledim. – leora

+0

Btw'ye baktığımda karşılaşmak için "ardışık" ile karşılaştırıldığında kullanmak için daha iyi bir kelime düşünebilirsiniz. "date" ve "date1" nin Çarşamba günü değil, date2 've date3'ün Perşembe olması amaçlanıyor mu? – poke

cevap

2

Not: Bu tamamen denenmemiş ve tarih kontrolleri muhtemelen oldukça kötü veya biraz gereksiz, ama bu şu an ^^

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

Neden IEnumerable 'arayüzünü foreach döngüsü yerine kullanmayı tercih ettiniz? –

+1

@NathanAnderson Güzel soru. İlk unsuru ayrı ayrı seçmek istedim ama “bool first = true” olayı yapmak ya da 'weekday' ve' previous' nullable yapmak gibi hissetmedim… – poke

+0

Mantıklı. Netleştirdiğiniz için teşekkürler. –

2

TimeSpan yapısına bakmanızı öneririm. Operatör aşırı yükü sayesinde, iki tarihin ayrılmasıyla bir TimeSpan alabilir ve ardından iki tarih arasındaki farkı ifade eden bir TimeSpan alabilirsiniz.

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

+0

, Timespan'ın kullanılması ile ilgili tarihlerin çıkarılması karşılaştırma mantığına yardımcı olmamanızdır (doğrudan ay hariç değil) – leora

+0

Modül işlecini kullandıysanız ve Biraz mantık, 30, 60 ya da günler arası (ama yine de birkaç ay arayla) olan tarihler için yeterince uygun olabilir. –

+0

@NathanAnderson Bütün aylar aynı uzunluktadır ve hafta içi (özellikle ilk/son) ay günleriyle aynı hizaya gelmez. Tarihler çok zor. –

0

elimden ile gelebilir en iyisi Ne yapmaya çalıştığınızı yanlış yorumlamak, ama eski tarihlerle uğraşmak zorunda kalmadığınızı varsayarak bunun işe yarayacağını düşünüyorum. tarihleri ​​herhangi bir boşluk "toplam aylar"

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

tamam dönüştürülür olup olmadığını görmek, yıllar Ocak 1st çünkü üzerinde çapraz kod çalışmıyor bir yıl üzerinde bir Pazartesi ve sonraki bir Salı olabilir. Bunu yapıyor olsaydım, ilk önce bu

'u kontrol ederler. A) her ayın haftanın aynı günüdür (DateTime'ı kullanırlar).DayOfWeek)

b) * kullanım uzatma yöntemi DayOfMonth (* Calculate week of month in .NET *

(zaten olmak için & b biliyor söyledi) bağlantısı bakınız her ay ayın aynı haftada gerçek bu yüzden onlar ay üst üste olup olmadığını belirlemek zorunda)

c) üçüncü durumuna gitmeyi sağlayan

//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; 

Bir & b koşullarını kontrol yanı sen Calculate week of month in .NET I kod eklemek zorunda

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

ay uzatma yönteminin olsun hafta kullanmayı unutmayın aşağıdaki gibi eğer ifadeyi değiştirmek eklemeniz gerekiyorsa Bir metin editöründe yaptığım gibi yazım hataları var.

1

İşte bu sorunu yaklaşım nasıl benim ilk düşünce olduğunu. İlk olarak, tarihlerin 'un görüntülenmesi sırasına karşılık gelen sıra değerlerine dönüşecek bir işlevi tanımlamak gerekir.

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)); 

Ardından, verilen tarihlerde, biz gibi sıralı diziler ile bitirmek: sadece listeyi yinelemek için önemsiz bir konudur Buradan

[24156 + 0, 24156 + 1, 24156 + 2, 24156 + 3] 

Ve

[24156 + 0, 24156 + 1, /* !!!! */ 24156 + 3] 

ve yani, her ürünün/tam sayı, önceki daha tam olarak bir fazla - tamsayılardır boşluklar veya tezgahları olmadan sırayla meydana sağlanması.

+0

'd.Month' Ocak için 1 değerini döndürür, bu nedenle 1'i çıkarmanız gerekir. Aksi takdirde, Aralık 2012 == Ocak 2013. –

+0

@ default.kramer Whoops! Düzeltildi, teşekkürler. –

3

Bunu uygulamak için, bir dizi alan ve her öğeyi önceki öğeyle oluşturan bir çiftler dizisi döndüren basit bir yardımcı yöntemle başlayacağız.

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

iki tarih aynı ay içinde olup olmadığını belirlemek için de bu basit yöntemi kullanacağız: bu kadar eğer kullanma

public static bool AreSameMonth(DateTime first, DateTime second) 
{ 
    return first.Year == second.Year 
     && first.Month == second.Month; 
} 

, kolayca her tarihinin ayını kapıp görebilirsiniz önceki aydan ay. Eğer tüm çiftler için doğruysa, ardı ardına aylar var.

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

@pst Sağ, sabit. Oh, ve 'Zip' kullanmak, diziyi iki kez yinelemekle sonuçlanırdı, eğer source.Zip (source.Skip (1)' gibi bir şey yaptıysanız, çift yinelemekten kaçınmak (ve tembellik devam ettirmek) için kullanamazsınız. Zip: – Servy

+0

Hmm, Zip ile bu durumu düşünmüyordum .. Sanırım bir Listede geçiyorum ve genellikle bunu düşünmüyorum. (Ben genellikle bu yönde etkili bir programcı değilim, ama dinliyorum) ReSharpers "çifte değerlendirme" uyarıları ve ToList'i, * temposu * gerektirmediği birçok durumda yayar.) –

+0

@pst Eğer içinde geçirdiğiniz şeyin genel boyutunu bildiğiniz özel bir durumla uğraşırken iyi, ama veri kümesinin boyutunu bilmediğiniz (veya veri kümesinin boyutunu bilmediğiniz internette yabancılar için kod yazarken) daha genel yarar yöntemleri yazarken yapmanız en iyisidir. mümkün olduğunca az sayıda varsayım olarak. – Servy

İlgili konular