一起添加两个DateTime对象

有没有更好的方法将一个DateTime对象添加到另一个,而不是:

DateTime first = new DateTime(2000, 1, 1); DateTime second = new DateTime(11, 2, 5, 10, 10, 11); DateTime result = first.AddYears(second.Year); DateTime result = first.AddMonths(second.Month); ... 

等等…

在这个例子中我想得到DateTime(2011, 3, 6, 10, 10, 11)

编辑

经过密集的头脑风暴之后,似乎没有什么不同的方式,但是为了方便它可以装在额外的类和操作员里面,就像在JonSkeet的回答中一样

将两个DateTime值一起添加是没有意义的。 如果你想代表“11年,2个月,5天,10个小时,10分钟和11秒”,那么你应该代表那个。 这与0011-02-05T10:10:11 一样。 特别是,你永远无法添加“2个月和30天”。 同样,您永远无法添加一年,因为您在一个日期内的月和日值不能为0。

现在没有BCL类型代表“11年[…]”的想法,但你可以合理地轻松创建自己的类型。 作为替代方案,您可以使用我的Noda Time项目,其中包含Period用于此目的:

 var localDateTime = new LocalDate(2000, 1, 10).AtMidnight(); var period = new PeriodBuilder { Years = 11, Months = 2, Days = 5, Hours = 10, Minutes = 10, Seconds = 11 }.Build(); var result = localDateTime + period; 

与此处提供的其他一些答案相反,您不能TimeSpan用于此目的。 TimeSpan没有任何月份和年份的概念,因为它们的长度不同,而TimeSpan表示固定数量的刻度。 (如果你最大的单位是几天,那么你可以使用TimeSpan ,但考虑到你的例子,我认为你需要数月TimeSpan年。)

如果您不想使用Noda Time,我建议您自己伪造一个类似Period的课程。 这很容易做到 – 例如:

 // Untested and quickly hacked up. Lots more API you'd probably // want, string conversions, properties etc. public sealed class Period { private readonly int years, months, days, hours, minutes, seconds; public Period(int years, int months, int days, int hours, int minutes, int seconds) { this.years = years; this.months = months; this.days = days; this.hours = hours; this.minutes = minutes; this.seconds = seconds; } public static DateTime operator+(DateTime lhs, Period rhs) { // Note: order of operations is important here. // Consider January 1st + (1 month and 30 days)... // what do you want the result to be? return lhs.AddYears(rhs.years) .AddMonths(rhs.months) .AddDays(rhs.days) .AddHours(rhs.hours) .AddMinutes(rhs.minutes) .AddSeconds(rhs.seconds); } } 

用法:

 DateTime first = new DateTime(2000, 1, 1); Period second = new Period(11, 2, 5, 10, 10, 11); DateTime result = first + second; 

您需要了解DateTime.Add将如何处理不可能的情况 – 例如,在1月31日添加一个月将给您2月28日/ 29日,具体取决于它是否是闰年。

我在这里列出的简单方法,通过中间值,有其缺点,因为截断可能发生两次(增加年数,然后增加几个月),当它不需要时 – 例如,“2月29日+ 1年+ 1个月“可能逻辑上是”3月29日“,但它实际上最终会变成”3月28日“,截至2月28日将在月份加入之前发生。

试图找出一种“正确”的方法来进行日历算法是非常困难的 ,特别是在某些情况下,人们可能不同意“正确”的答案是什么。 在上面的代码中,我选择了简单性和可预测性 – 根据您的实际需求,您可能需要更复杂的东西。

你有一个DateTime代表一个时间点。 并且您希望为其添加若干年/月/日/小时/分钟/秒。

DataTime的变化不是一个点,它是一个向量(点之间的差异)。 将一个误认为另一个很容易,因为它们通常具有相似的结构。 但是,这种类型错误会导致很多痛苦。

避免它不会解决你的痛苦,但它使它易于管理。

一起添加两个DateTime就是一起添加两个点。 有点像将洛杉矶的位置添加到纽约。

现在,将洛杉矶的“向量”添加到纽约到伦敦是有道理的 – 因为旅行向量是一个向量,而不是一个点。 点+向量只是一个点。

所以这意味着你需要创建一个时间向量类型。 一个简单的时间跨度是一个选项,但可能不合适:因为你关心的是月,年和日,而不是纳秒或绝对持续时间。

我将复制CalendarVector的矢量名称,因为它表示日历上的移动,而不是时间本身。

一个简单的第一步是创建每个子类型的时间的元组 – 年,月,日等 – 然后使用重载的operator+以任意顺序将它们添加到原始DateTime

你应该支持:

 DateTime = DateTime + CalendarVector CalendarVector = CalendarVector + CalendarVector CalendarVector = CalendarVector - CalendarVector CalendarVector = int * CalendarVector CalendarVector = - CalendarVector DateTime = DateTime - CalendarVector CalendarVector = DateTime - DateTime 

理想的。 CalendarVector + DateTime重载是可选的,但可能不需要。

但是,这只能让你走到一半。

剩下的最大问题是CalendarVector添加不会通勤 。 将1个月添加到DateTime,然后添加1天,与添加1天然后添加1个月不同。

这是至关重要的。

存在“在1月31日之后1个月意味着什么”的问题,可以回答,但是对该问题的任何合理答案都不能解决通勤问题。

您计划的构造函数 – 您在其中提供的年数,月数,日数,小时数,分钟数 – 在其含义上是模糊的。

因此,强大的解决方案不应该具有该构造函数。

解决方案是创建明确添加的YearsMonthsDaysHoursMinutesSeconds类型。 它们一起添加的顺序是它们应用于您添加到的DateTime的顺序。 直到日期时间的最终申请,才能避免通勤和“简化” – 所以+1 year, +2 days, -1 month, -1 year, -2 days, +1 month 不是零转换。

DateTime-DateTime存在一个相关问题 – 它应该返回一个CalendarVector v ,使得lhs = rhs + v ,但是有多个这样的向量。 球面坐标可能会出现同样的问题 – 你的意思是围绕地球的短路还是漫长的路? 在某些情况下并不重要 – 但是你将结果减半以找到中间点。 此外,当您接近“世界的另一端”时,您会遇到不连续性。

所以我的建议是维护DateTime对象的转换列表。 1 year是一个转换,包括在年份字段中添加1,然后修复其他字段以使它们保持一致。 这些转变支持否定。 增加是从左到右一次应用它们。 否定也可能会颠倒应用程序的顺序,并且“相同类型”的相邻转换可能会合并(因此+1个月-1个月成为身份转换,而不是基于下个月月末的夹紧操作),或者不是(所以x = x+1 month ,然后下一行x = x-1 monthx = x + 1 month - 1 month )。

另一种方法是坚持用户提供在这些特殊情况下应该做什么的策略(这种情况一直发生),因为这个问题非常棘手,以至于“解决”问题的库最多可以突出显示问题并迫使客户程序员思考它们并做出决定。