问题
I have a list of DateTimes and I need to 'invert' (for lack of a better word) this list.
public class Available
{
public Available(DateTime startDate, DateTime endDate)
{
if (!startDate.Day.Equals(endDate.Day))
throw new Exception("The start and end days are not equal.");
this.StartDate = startDate;
this.EndDate = endDate;
}
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
List<Available> availableTimes = new List<Available>()
{
new Available(new DateTime(2015, 3, 16, 08, 00, 00), new DateTime(2015, 3, 16, 10, 00, 00)),
new Available(new DateTime(2015, 3, 16, 12, 00, 00), new DateTime(2015, 3, 16, 14, 00, 00)),
new Available(new DateTime(2015, 3, 16, 15, 00, 00), new DateTime(2015, 3, 16, 16, 00, 00)),
};
I need to transform this list of available times to a list of blocked times for that particular day but I'm kind of stuck. In this case I need:
2015-3-16 00:00 - 2015-3-16 08:00,
2015-3-16 10:00 - 2015-3-16 12:00,
2015-3-16 14:00 - 2015-3-16 15:00,
2015-3-16 16:00 - 2015-3-16 23:59
Any bright ideas?
回答1:
Try this, renamed your class to DateTimeRange as it has dual meanings now including blocked, and also had to make the assumption that 23:59 was midnight -1 tick.
using System;
using System.Collections.Generic;
using System.Linq;
public class DateTimeRange
{
public DateTimeRange(DateTime startDate, DateTime endDate)
{
if (!startDate.Day.Equals(endDate.Day))
throw new Exception("The start and end days are not equal.");
this.StartDate = startDate;
this.EndDate = endDate;
}
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
public class Program
{
static List<DateTimeRange> availableTimes = new List<DateTimeRange>()
{
new DateTimeRange(new DateTime(2015, 3, 16, 08, 00, 00), new DateTime(2015, 3, 16, 10, 00, 00)),
new DateTimeRange(new DateTime(2015, 3, 16, 12, 00, 00), new DateTime(2015, 3, 16, 14, 00, 00)),
new DateTimeRange(new DateTime(2015, 3, 16, 15, 00, 00), new DateTime(2015, 3, 16, 16, 00, 00)),
};
private static IEnumerable<DateTimeRange> GetBlockedTimes(IEnumerable<DateTimeRange> ranges)
{
var min = ranges.Select(r => r.StartDate).Min().Date;
var max = ranges.Select(r => r.EndDate).Max().AddDays(1).Date.AddTicks(-1);
foreach(var range in ranges.OrderBy(r => r.StartDate))
{
yield return new DateTimeRange(min, range.StartDate);
min = range.EndDate;
}
yield return new DateTimeRange(min, max);
}
public static void Main()
{
foreach(var item in GetBlockedTimes(availableTimes))
{
Console.WriteLine(item.StartDate + " - " + item.EndDate);
}
}
}
回答2:
If your list is always "sorted" and without overlapping times, the algorithm shouldn't be too hard. "Global start/end time" refers to the values 2015-3-16 00:00
and 2015-3-16 23:59
of your example.
start = global start time
for each item in list:
yield new (start, item.start)
start = item.end
yield new (start, global end time)
Translating it into a C# method is left as an exercise.
回答3:
You should beware the first and end time range for marginal cases.
List<DateTimeRange> availableTimes = new List<DateTimeRange>()
{
new DateTimeRange(new DateTime(2015, 3, 16, 00, 00, 00), new DateTime(2015, 3, 16, 1, 00, 00)),
new DateTimeRange(new DateTime(2015, 3, 16, 08, 00, 00), new DateTime(2015, 3, 16, 10, 00, 00)),
new DateTimeRange(new DateTime(2015, 3, 16, 12, 00, 00), new DateTime(2015, 3, 16, 14, 00, 00)),
new DateTimeRange(new DateTime(2015, 3, 16, 15, 00, 00), new DateTime(2015, 3, 16, 16, 00, 00)),
new DateTimeRange(new DateTime(2015, 3, 16, 19, 00, 00), new DateTime(2015, 3, 16, 23, 59, 59)),
};
var gap = GetGapsForDay(availableTimes);
public IEnumerable<DateTimeRange> GetGapsForDay(List<DateTimeRange> ranges)
{
var start = ranges.First().StartDate.Date;
var end = ranges.First().StartDate.Date.AddDays(1).AddMinutes(-1);
foreach(var item in ranges.OrderBy(i => i.StartDate))
{
if(start < item.StartDate)
yield return new DateTimeRange(start, item.StartDate);
start = item.EndDate;
}
if (ranges.Max(i => i.EndDate) < end)
yield return new DateTimeRange(start, end);
}
回答4:
Heinzi's algorithm turned into C#. Assumes the available intervals don't overlap and are sorted.
DateTime firstDay = availableTimes[0].StartDate;
DateTime previousTime = new DateTime(firstDay.Year, firstDay.Month, firstDay.Day, 0, 0, 0);
List<Available> unavailableTimes = new List<Available>();
foreach (Available available in availableTimes)
{
unavailableTimes.Add(new Available(previousTime, available.StartDate));
previousTime = available.EndDate;
}
var dateTime = previousTime.Date;
DateTime endDay = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 23, 59, 59);
unavailableTimes.Add(new Available(previousTime, endDay));
回答5:
You have to concat your DateTime
to single dimension array:
var minBorder = new DateTime(2015, 3, 16, 00, 00, 00);
var maxBorder = new DateTime(2015, 3, 16, 23, 59, 00);
var times = new List<DateTime>();
times.Add(minBorder);
foreach (var at in availableTimes) {
times.Add(at.StartDate);
times.Add(at.EndDate);
}
times.Add(maxBorder);
result:
and then take intervals you need like this:
public static IList<Available> Invert(IList<DateTime> input) {
var result = new List<Available>();
for (var i = 0; i < input.Count; i += 2) {
result.Add(
new Available(input[i], input[i + 1])
);
}
return result;
}
回答6:
If you just want to order the list items by date, with link, this is very simple :
availableTimes = availableTimes.OrderByDescending(x => x.startDate).ToList();
来源:https://stackoverflow.com/questions/29073718/c-sharp-invert-list-of-datetime