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

5

如果我有一个时间段,比如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 可能可以吗?


5
更适合进行代码审查。 - L.B
3
我认为TimeSpan在这里没有任何作用... - user915331
6个回答

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

我稍微修改了第一行,以排除重复项。如果两个或更多的拆分日期是重复的,则结果是错误的。 - user915331
1
AddDays是什么意思?此外,Enumerable.Distinct文档说我们不应该依赖于它保持顺序。 - Amy B
感谢您的Distinct注释,已将其上移。AddDays是因为每个元组中的第一个元素比前一个元组中的第二个元素大1天。也就是说,时间段的顺序没有重叠。 - yamen

1
你可以这样做:
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

虽然L.B是正确的,这可能属于代码审查,但我想试着解决这个问题:

根据您的第一个代码块,以下代码将实现您要求的功能:

// 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
如果您将所有日期放入一个列表中,那么这应该可以工作:
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
我已经简化了获取所提供日期范围内的日期的过程。
模型对象
 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;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接