将一段时间分成多个时间段

时间:2012-04-13 22:14:03

标签: c#

如果我有时间段,请说DateFromDateTo,我有一个日期列表,这些日期将是分割日期。例如:

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

我希望结果是一个句点列表,所以对于前面的例子,结果应该是:

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

我已经写了一个方法来做到这一点:

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

问题
它看起来很难看,是否有更简洁的方式来做这件事?使用Linq可能吗?

6 个答案:

答案 0 :(得分:4)

这是一个有效的方法,也可以处理一些边缘情况:

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

答案 1 :(得分:0)

虽然L.B是正确的,这可能属于Code Review,但我觉得这样做是为了解决这个问题:

鉴于您的第一个代码块,以下代码将满足您的要求:

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

答案 2 :(得分:0)

你可以这样做:

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

答案 3 :(得分:0)

如果您将所有日期都放在一个列表中,那么这应该有效:

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

答案 4 :(得分: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;

答案 5 :(得分:0)

我已经简化了提供DateRange之间的日期。

模型对象

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

行动:

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