2012-04-13 16 views
5

Se ho un periodo di tempo, diciamo DateFrom e DateTo e ho un elenco di date, Queste date saranno le date di divisione. Per esempio:Dividere un periodo di tempo in più periodi di tempo

DateTime dateFrom = new DateTime(2012, 1, 1); 
DateTime dateTo = new DateTime(2012, 12, 31); 

List<DateTime> splitDates = new List<DateTime> 
    { 
     new DateTime(2012,2,1), 
     new DateTime(2012,5,1), 
     new DateTime(2012,7,1), 
     new DateTime(2012,11,1), 
    }; 

List<Tuple<DateTime, DateTime>> periods = SplitDatePeriod(dateFrom, dateTo, splitDates); 

Voglio che il risultato sia un elenco di periodi, così per l'esempio precedente, il risultato dovrebbe essere:

(01/01/2012 - 01/02/2012) 
(02/02/2012 - 01/05/2012) 
(02/05/2012 - 01/07/2012) 
(02/07/2012 - 01/11/2012) 
(02/11/2012 - 31/12/2012) 

Ho già scritto un metodo per farlo:

List<Tuple<DateTime, DateTime>> SplitDatePeriod(DateTime dateFrom, DateTime dateTo, List<DateTime> splitDates) 
{ 
    var resultDates = new List<Tuple<DateTime, DateTime>>(); 

    // sort split dates 
    List<DateTime> _splitDates = splitDates.OrderBy(d => d.Date).ToList(); 

    DateTime _curDate = dateFrom.Date; 
    for (int i = 0; i <= _splitDates.Count; ++i) 
    { 
     DateTime d = (i < _splitDates.Count) ? _splitDates[i] : dateTo; 

     // skip dates out of range 
     if (d.Date < dateFrom.Date || d.Date > dateTo.Date) 
      continue; 

     resultDates.Add(Tuple.Create(_curDate, d)); 

     _curDate = d.AddDays(1); 
    } 
    return resultDates; 
} 

La questione
sembra così brutto, c'è modo più accurato e più breve di fare questo? usando Linq forse?

+5

si adatta meglio al [Code Review] (http://codereview.stackexchange.com/) –

+0

come 'TimeSpan'? –

+3

@RitchMelton Credo che 'TimeSpan' non abbia nulla da fare qui ... –

risposta

4

questo è quello che funziona e si prende cura di alcuni casi limite anche:

var realDates = splitDates 
    .Where(d => d > dateFrom && d < dateTo) 
    .Concat(new List<DateTime>() {dateFrom.AddDays(-1), dateTo}) 
    .Select(d => d.Date) 
    .Distinct() 
    .OrderBy(d => d) 
    .ToList(); 

// now we have    (start - 1) -- split1 -- split2 -- split3 -- end 
// we zip it against   split1 -- split2 -- split3 -- end 
// and produce  start,split1 -- split1+1,split2 -- split2+1,split3 -- split3+1,end 

realDates.Zip(realDates.Skip(1), (a, b) => Tuple.Create(a.AddDays(1), b)); 
+0

Mentre per lo più tutte le risposte sono corrette, il tuo è il migliore ei commenti sono buoni .. –

+0

Ho corretto la prima riga un po 'per escludere i duplicati. Se due o più date di divisione sono duplicate, il risultato è sbagliato –

+1

Che cosa succede con gli addDays? Inoltre, la documentazione di Enumerable.Distinct dice che non dovremmo fare affidamento su di esso preservando l'ordine. –

0

Mentre LB è corretta e questo appartiene probabilmente su Code Review, mi sentivo come prendere una crepa in questo:

dato il vostro primo codice di blocco, il seguente codice farà quello che stai chiedendo:

// List of all dates in order that are valid 
var dateSegments = new [] { dateFrom, dateTo } 
    .Concat(splitDates.Where(x => x > dateFrom && x < dateTo)) 
    .OrderBy(x => x) 
    .ToArray(); 

List<Tuple<DateTime, DateTime>> results = new List<Tuple<DateTime, DateTime>>(); 
for(var i = 0; i < dateSegments.Length - 1; i++) 
{ 
    results.Add(new Tuple<DateTime, DateTime>(dateSegments[i], dateSegments[i+1])); 
} 
0

si può fare in questo modo:

012.
List<DateTime> split = 
    splitDates.Where(d => d >= dateFrom && d <= dateTo).ToList(); 

List<Tuple<DateTime, DateTime>> periods = 
    Enumerable.Range(0, split.Count + 1) 
    .Select(i => new Tuple<DateTime, DateTime>(
    i == 0 ? dateFrom : split[i - 1].AddDays(1), 
    i == split.Count ? dateTo : split[i] 
)) 
    .ToList(); 
0

Se mettete tutte le date in un unico elenco, allora questo dovrebbe funzionare:

var dates = new List<DateTime> 
     { 
      new DateTime(2012, 1, 1), 
      new DateTime(2012, 2, 1), 
      new DateTime(2012, 5, 1), 
      new DateTime(2012, 7, 1), 
      new DateTime(2012, 11, 1), 
      new DateTime(2012, 12, 31) 
     }; 

var z = dates.Zip(dates.Skip(1), (f, s) => Tuple.Create(f.Equals(dates[0]) ? f : f.AddDays(1), s)); 
0
List<DateTime> splitDates = GetSplitDates(); 
DateTime dateFrom = GetDateFrom(); 
DateTime dateTo = GetDateTo(); 

List<DateTime> edges = splitDates 
    .Where(d => dateFrom < d && d < dateTo) 
    .Concat(new List<DateTime>() {dateFrom, dateTo}) 
    .Distinct() 
    .OrderBy(d => d) 
    .ToList(); 

//must be at least one edge since we added at least one unique date to this. 
DateTime currentEdge = edges.First(); 

List<Tuple<DateTime, DateTime>> resultItems = new List<Tuple<DateTime, DateTime>>(); 

foreach(DateTime nextEdge in edges.Skip(1)) 
{ 
    resultItems.Add(Tuple.Create(currentEdge, nextEdge)); 
    currentEdge = nextEdge; 
} 

return resultItems; 
0

mi hanno reso semplice per avere le date tra il DateRange fornito.

Object Model

public class DateObjectClass 
{ 
    public DateTime startDate { get; set; } 
    public DateTime endDate { get; set; } 
} 

Azione:

public List<DateObjectClass> SplitDateRangeByDates(DateTime start,DateTime end) 
    { 
     List<DateObjectClass> datesCollection = new List<DateObjectClass>(); 
     DateTime startOfThisPeriod = start; 
     while (startOfThisPeriod < end) 
     { 
      DateTime endOfThisPeriod =new DateTime(startOfThisPeriod.Year,startOfThisPeriod.Month,startOfThisPeriod.Day,23,59,59); 
      endOfThisPeriod = endOfThisPeriod < end ? endOfThisPeriod : end; 
      datesCollection.Add(new DateObjectClass() { startDate= startOfThisPeriod ,endDate =endOfThisPeriod}); 
      startOfThisPeriod = endOfThisPeriod; 
      startOfThisPeriod = startOfThisPeriod.AddSeconds(1); 
     } 

     return datesCollection; 
    } 
Problemi correlati