我遇到了解决这个问题的逻辑方法的问题。 我有一个日期范围列表,比如说:
01/01/15 - 11/01/15
02/01/15 - 04/01/15
09/01/15 - 13/01/15
18/01/15 - 20/01/15
我需要做的是计算所有这些日期范围内的总夜数。
所以对于这个例子,总数应该是14晚:
01/01/15 - 11/01/15 // 10 nights
02/01/15 - 04/01/15 // Ignored as nights are covered in 1-11
09/01/15 - 13/01/15 // 2 nights as 11th and 12th nights haven't been covered
18/01/15 - 20/01/15 // 2 nights
我可以很容易地计算出使用最小 - 最大日期的总夜数,但忽略了缺少的日期(示例中为14-17),这是我无法弄清楚的。
有没有办法找到失踪的总天数来帮助解决这个问题?
答案 0 :(得分:3)
以下是使用HashSet的方法:
public static int CountDays(IEnumerable<TimeRange> periods)
{
var usedDays = new HashSet<DateTime>();
foreach (var period in periods)
for (var day = period.Start; day < period.End; day += TimeSpan.FromDays(1))
usedDays.Add(day);
return usedDays.Count;
}
这假定您的日期范围是半开的间隔(即开始日期被视为范围的一部分,但结束日期不是)。
这是一个完整的演示程序。答案是14
:
using System;
using System.Collections.Generic;
namespace ConsoleApplication2
{
public sealed class TimeRange
{
public DateTime Start { get; private set; }
public DateTime End { get; private set; }
public TimeRange(string start, string end)
{
Start = DateTime.Parse(start);
End = DateTime.Parse(end);
}
}
internal class Program
{
public static void Main()
{
var periods = new []
{
new TimeRange("01/01/15", "11/01/15"),
new TimeRange("02/01/15", "04/01/15"),
new TimeRange("09/01/15", "13/01/15"),
new TimeRange("18/01/15", "20/01/15")
};
Console.WriteLine(CountDays(periods));
}
public static int CountDays(IEnumerable<TimeRange> periods)
{
var usedDays = new HashSet<DateTime>();
foreach (var period in periods)
for (var day = period.Start; day < period.End; day += TimeSpan.FromDays(1))
usedDays.Add(day);
return usedDays.Count;
}
}
}
注意:对于大日期范围,这不是非常有效!如果要考虑大的日期范围,将重叠范围组合到单个范围的方法会更好。
[编辑]修复了使用半开间隔而非封闭间隔的代码。
答案 1 :(得分:1)
这样的事情应该有效:
internal class Range
{
internal DateTime From, To;
public Range(string aFrom, string aTo)
{
From = DateTime.ParseExact(aFrom, "dd/mm/yy", CultureInfo.InvariantCulture);
To = DateTime.ParseExact(aTo, "dd/mm/yy", CultureInfo.InvariantCulture);
}
}
public static int ComputeNights(IEnumerable<Range> ranges)
{
var vSet = new HashSet<DateTime>();
foreach (var range in ranges)
for (var i = range.From; i < range.To; i = i.AddDays(1)) vSet.Add(i)
return vSet.Count;
}
运行示例的代码:
var vRanges = new List<Range>
{
new Range("01/01/15", "11/01/15"),
new Range("02/01/15", "04/01/15"),
new Range("09/01/15", "13/01/15"),
new Range("18/01/15", "20/01/15"),
};
var v = ComputeNights(vRanges);
v评估为14
答案 2 :(得分:1)
你可以像这样计算:
var laterDateTime = Convert.ToDateTime("11/01/15 ");
var earlierDateTime = Convert.ToDateTime("01/01/15");
TimeSpan dates = laterDateTime - earlierDateTime;
int nights = dates.Days - 1;
您可以将所有内容转换为DateTime。 然后,您可以使用 - 运算符减去两个DateTimes。 您的结果将是一种结构TimeSpan。
TimeSpan hat a Days属性。从中减去1并且你收到了夜晚。
2天之间是1晚 3天之间是2晚 4天之间是3晚我相信你可以做其余的事。
答案 3 :(得分:1)
假设有两个晚上,例如两晚1月1日和1月3日,这应该工作。如果您已经有DateTime
个值而不是字符串,则可以删除解析位。基本上,我使用DateTime.Subtract()
来计算两个日期之间的天数(即夜晚)。
namespace DateTest1
{
using System;
using System.Collections.Generic;
using System.Globalization;
class Program
{
static void Main(string[] args)
{
var intervals = new List<Tuple<string, string>>
{
new Tuple<string, string>("01/01/15", "11/01/15"),
new Tuple<string, string>("02/01/15", "04/01/15"),
new Tuple<string, string>("09/01/15", "13/01/15"),
new Tuple<string, string>("18/01/15", "20/01/15")
};
var totalNights = 0;
foreach (var interval in intervals)
{
var dateFrom = DateTime.ParseExact(interval.Item1, "dd/MM/yy", CultureInfo.InvariantCulture);
var dateTo = DateTime.ParseExact(interval.Item2, "dd/MM/yy", CultureInfo.InvariantCulture);
var nights = dateTo.Subtract(dateFrom).Days;
Console.WriteLine("{0} - {1}: {2} nights", interval.Item1, interval.Item2, nights);
totalNights += nights;
}
Console.WriteLine("Total nights: {0}", totalNights);
}
}
}
01/01/15 - 11/01/15: 10 nights
02/01/15 - 04/01/15: 2 nights
09/01/15 - 13/01/15: 4 nights
18/01/15 - 20/01/15: 2 nights
Total nights: 18
Press any key to continue . . .
答案 4 :(得分:1)
我认为这个解决方案会更快,然后循环使用内部循环的范围来插入列表中的天数。该解决方案不需要额外的空间。它是O(1)并且它只通过范围一次,所以它的复杂性是O(n)。但它假定您的范围按startdate
排序。如果没有,您可以随时轻松订购:
var p = new[]
{
new Tuple<DateTime, DateTime>(DateTime.ParseExact("01/01/15", "dd/MM/yy", CultureInfo.InvariantCulture), DateTime.ParseExact("11/01/15", "dd/MM/yy", CultureInfo.InvariantCulture)),
new Tuple<DateTime, DateTime>(DateTime.ParseExact("02/01/15", "dd/MM/yy", CultureInfo.InvariantCulture), DateTime.ParseExact("04/01/15", "dd/MM/yy", CultureInfo.InvariantCulture)),
new Tuple<DateTime, DateTime>(DateTime.ParseExact("09/01/15", "dd/MM/yy", CultureInfo.InvariantCulture), DateTime.ParseExact("13/01/15", "dd/MM/yy", CultureInfo.InvariantCulture)),
new Tuple<DateTime, DateTime>(DateTime.ParseExact("18/01/15", "dd/MM/yy", CultureInfo.InvariantCulture), DateTime.ParseExact("20/01/15", "dd/MM/yy", CultureInfo.InvariantCulture))
};
int days = (p[0].Item2 - p[0].Item1).Days;
var endDate = p[0].Item2;
for(int i = 1; i < p.Length; i++)
{
if(p[i].Item2 > endDate)
{
days += (p[i].Item2 - (p[i].Item1 > endDate ? p[i].Item1 : endDate)).Days;
endDate = p[i].Item2;
}
}
答案 5 :(得分:0)
如果您没有足够的答案,可以使用Linq和Aggregate
。返回14晚。
List<Tuple<DateTime, DateTime>> dates = new List<Tuple<DateTime, DateTime>>
{
Tuple.Create(new DateTime(2015, 1,1), new DateTime(2015, 1,11)),
Tuple.Create(new DateTime(2015, 1,2), new DateTime(2015, 1,4)),
Tuple.Create(new DateTime(2015, 1,9), new DateTime(2015, 1,13)),
Tuple.Create(new DateTime(2015, 1,18), new DateTime(2015, 1,20))
};
var availableDates =
dates.Aggregate<Tuple<DateTime, DateTime>,
IEnumerable<DateTime>,
IEnumerable<DateTime>>
(new List<DateTime>(),
(allDates, nextRange) => allDates.Concat(Enumerable.Range(0, (nextRange.Item2 - nextRange.Item1).Days)
.Select(e => nextRange.Item1.AddDays(e))),
allDates => allDates);
var numDays =
availableDates.Aggregate<DateTime,
Tuple<DateTime, int>,
int>
(Tuple.Create(DateTime.MinValue, 0),
(acc, nextDate) =>
{
int daysSoFar = acc.Item2;
if ((nextDate - acc.Item1).Days == 1)
{
daysSoFar++;
}
return Tuple.Create(nextDate, daysSoFar);
},
acc => acc.Item2);