2012-12-03 13 views
6

Sto cercando un metodo per dividere un intervallo di date in una serie di intervalli di date in base alla dimensione dei giorni. Sto pensando di usarlo per bufferizzare le chiamate a un servizio che, se l'intervallo di date è troppo grande, gli errori di servizio.Dividi intervallo di date in blocchi dell'intervallo di date

Questo è quello che ho inventato finora. Sembra funzionare, ma non sono sicuro che uscirà correttamente. Questo sembra qualcosa che è stato probabilmente fatto diverse volte prima, ma non riesco a trovarlo.

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

Sto cercando suggerimenti di miglioramento o "Amico, usa questa funzione esistente per questo!"

risposta

16

Penso che il codice non funzioni quando la differenza tra inizio e fine è inferiore a dayChunkSize. Vedere questo:

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

Soluzione proposta:

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

Grazie per aver segnalato l'errore, adoro l'aspetto pulito della soluzione. – Khan

+0

Bel codice, grazie! –

+0

Risposta molto bella. Grazie! Vorrei aggiungere come ulteriore - se non si desidera "sovrapporre" modificare la riga "resa return Tuple.Create (start, chunkEnd);" essere "chunkEnd.AddDays (-1)". Ciò assicurerà che le date di inizio/fine di ogni riga non si sovrappongano. –

1

Il tuo codice sembra buono per me. Non mi piace molto l'idea di while(true)
Ma altra soluzione sarebbe quella di utilizzare 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)))); 
     } 

o anche, questo funziona anche:

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

     } 

non ho alcun oggetti per qualsiasi implementazione. Sono praticamente identici.

+0

Rabbrividii mentre stavo scrivendo il while (true);). Grazie – Khan

2

Ci sono un paio di problemi con la vostra soluzione:

  • il test newEnd == end non può mai essere vero, in modo che il while potrebbe ciclo per sempre (I ora vedere che questa condizione dovrebbe sempre essere attivato, ma non era ovvio in prima lettura del codice:
  • AddDays viene chiamato tre volte per ogni iterazione (problema di prestazioni minori)

Ecco un'alternativa:

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

Si noti che questo tronca l'ultimo periodo per terminare il end come indicato nel codice nella questione. Se ciò non è necessario, la seconda riga di while potrebbe essere omessa, semplificando il metodo. Inoltre, startOfThisPeriod non è strettamente necessario, ma ho ritenuto che fosse più chiaro del riutilizzo di start.

+0

Buon punto con gli addDays. – Khan

Problemi correlati