2012-12-03 20 views
6

Poszukuję metody podziału zakresu dat na serię zakresów dat według wielkości fragmentów dni. Planuję używać tego do buforowania wywołań do usługi, która jeśli zakres dat jest zbyt duży, błędy usługi.Podział zakresu dat na fragmenty zakresu dat

Oto, do czego do tej pory doszedłem. Wygląda na to, że działa, ale nie jestem pewien, czy zakończy się poprawnie. Wygląda to na coś, co prawdopodobnie zostało zrobione kilka razy wcześniej, ale nie mogę go znaleźć.

public IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize) 
{ 
    var newStart = start; 
    var newEnd = start.AddDays(dayChunkSize); 

    while (true) 
    { 
     yield return new Tuple<DateTime, DateTime>(newStart, newEnd); 

     if (newEnd == end) 
      yield break; 

     newStart = newStart.AddDays(dayChunkSize); 
     newEnd = (newEnd.AddDays(dayChunkSize) > end ? end : newEnd.AddDays(dayChunkSize)); 
    } 
} 

Szukam sugestii poprawy, lub "Dude, użyj tej funkcji do tego!"

Odpowiedz

16

Myślę, że Twój kod kończy się niepowodzeniem, gdy różnica między początkiem i końcem jest mniejsza niż dayChunkSize. patrz poniżej:

var singleRange = SplitDateRange(DateTime.Now, DateTime.Now.AddDays(7), dayChunkSize: 15).ToList(); 
Debug.Assert(singleRange.Count == 1); 

Proponowane rozwiązanie:

public static IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize) 
    { 
     DateTime chunkEnd; 
     while ((chunkEnd = start.AddDays(dayChunkSize)) < end) 
     { 
      yield return Tuple.Create(start, chunkEnd); 
      start = chunkEnd; 
     } 
     yield return Tuple.Create(start, end); 
    } 
+0

Dzięki za wskazanie błędu, Kochaj czysty wygląd swojego rozwiązania. – Khan

+0

Ładny kod, dzięki! –

+0

Bardzo ładna odpowiedź. Dziękuję Ci! Dodałbym jako dodatkowy - jeśli nie chcesz "nakładania się", zmodyfikuj wiersz "zwrot plonu Tuple.Create (start, chunkEnd);" być "chunkEnd.AddDays (-1)". Zapewni to, że daty rozpoczęcia/zakończenia dla każdego wiersza się nie pokrywają. –

1

Twój kod wygląda dobrze dla mnie. I naprawdę nie podoba mi się pomysł while(true)
ale inne rozwiązanie byłoby użyć enumerable.Range:

public static IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize) 
     { 
      return Enumerable 
        .Range(0, (Convert.ToInt32((end - start).TotalDays)/dayChunkSize +1)) 
        .Select(x => Tuple.Create(start.AddDays(dayChunkSize * (x)), start.AddDays(dayChunkSize * (x + 1)) > end 
                       ? end : start.AddDays(dayChunkSize * (x + 1)))); 
     } 

lub też, będzie również działać:

public static IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize) 
     { 

      var dateCount = (end - start).TotalDays/5; 
      for (int i = 0; i < dateCount; i++) 
      { 
       yield return Tuple.Create(start.AddDays(dayChunkSize * i) 
             , start.AddDays(dayChunkSize * (i + 1)) > end 
             ? end : start.AddDays(dayChunkSize * (i + 1))); 
      } 

     } 

ja nie mam żadnych obiekty dla dowolnej implementacji. Są praktycznie identyczne.

+0

Skuliłem a ja wpisując while (prawda)). Dziękuję – Khan

2

Istnieje kilka problemów z rozwiązania:

  • testy newEnd == end nigdy nie może być prawdą, więc while mógł pętla zawsze (teraz widzę, że warunek ten powinny zawsze być wyzwalane, ale nie było to oczywiste przy pierwszym odczycie kodu, ale nadal jest trochę niebezpieczna)
  • AddDays jest wywoływana trzy razy dla każdej iteracji (mniejszy problem z wydajnością)

Oto alternatywa:

public IEnumerable<Tuple<DateTime, DateTime>> SplitDateRange(DateTime start, DateTime end, int dayChunkSize) 
{ 
    DateTime startOfThisPeriod = start; 
    while (startOfThisPeriod < end) 
    { 
     DateTime endOfThisPeriod = startOfThisPeriod.AddDays(dayChunkSize); 
     endOfThisPeriod = endOfThisPeriod < end ? endOfThisPeriod : end; 
     yield return Tuple.Create(startOfThisPeriod, endOfThisPeriod); 
     startOfThisPeriod = endOfThisPeriod; 
    } 
} 

Zauważ, że ten obcina ostatni okres do końca na end jak podano w kodzie w pytaniu. Jeśli nie jest to konieczne, można pominąć drugą linię while, upraszczając metodę. Ponadto, startOfThisPeriod nie jest bezwzględnie konieczne, ale czułem, że było to wyraźniejsze niż ponowne użycie start.

+0

Dobry punkt z AddDays. – Khan

Powiązane problemy