将两个 DateTime 对象相加

Adding two DateTime objects together

有没有比这个更好的方法将一个 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)

编辑

经过密集的头脑风暴后,似乎没有什么不同的方法,但为了方便起见,可以将其装箱在额外的 class 和 operator+ 中,就像 JonSkeet 的回答

将两个 DateTime 值相加没有意义。如果你想表示“11 年、2 个月、5 天、10 小时、10 分钟和 11 秒”,那么你应该表示它。那是 not 与 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,但根据您的示例,我假设您需要月和年。)

如果您不想使用 Noda Time,我建议您自己伪造一个 Period-like class。这很容易做到 - 例如:

// 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 日,具体取决于是否是闰年。

我在此处列出的简单方法(遍历中间值)有其缺点,因为在不需要时截断可能会发生两次(增加年份然后增加月份) - 例如,"February 29th + 1 year + 1 month" 在逻辑上可能是 "March 29th" 但它实际上最终会是 "March 28th" 因为截断到 2 月 28 日将在添加月份之前发生。

尝试找出一种 "right" 方法来进行历法运算是 fiendishly difficult,特别是在某些情况下,人们可能不同意 "right" 的答案是什么。在上面的代码中,我选择了简单性和可预测性 - 根据您的实际要求,您可能需要更复杂的东西。

你有一个 DateTime 代表一个时间点。并且你想给它加上years/months/days/hours/minutes/seconds个数。

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

避免它并不能解决你的痛苦,但它可以让它变得易于管理。

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

现在,将 LA 到 NY 到 London 的 "vector" 添加到 London 是有意义的——因为旅行矢量是一个矢量,而不是一个点。而point+vector就是一个点。

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

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

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

你应该支持:

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

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

然而,这只让你成功了一半。

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

这是基础。

有"what does it mean to be 1 month after January 31st"的问题,可以回答,但任何对该问题的合理回答都不能解决通勤问题。

你计划的构造函数——你在其中输入年、月、日、小时、分钟、秒的数量——因此它的含义不明确。

所以一个健壮的解决方案不应该有那个构造函数。

一个解决方案是创建您显式添加的 YearsMonthsDaysHoursMinutesSeconds 类型一起。它们相加的顺序是它们应用于您添加它的 DateTime 的顺序。通勤和 "simplification" 被避免,直到 DateTime 上的最终应用 - 所以 +1 year, +2 days, -1 month, -1 year, -2 days, +1 month 不是 零转换。

有一个与 DateTime-DateTime 相关的问题 -- 它应该 return a CalendarVector v 这样 lhs = rhs + v,但是有多个这样的向量.球坐标也会出现同样的问题——你是指绕地球的短距离还是长距离?在某些情况下这无关紧要——但随后您将结果减半以找到中间点。另外,当您接近 "far side of the world".

时,您会遇到不连续性

所以我的建议是在 DateTime 对象上维护一个转换列表。 1 year 是一个转换,包括将 year 字段加 1,然后修复其他字段以使其一致。这些转换支持否定。加法是一次应用一个,从左到右。否定也可以颠倒应用程序的顺序,并且相邻的转换 "of the same kind" 可以组合(因此 +1 个月 -1 个月成为恒等转换,而不是基于下个月月末的钳位操作),或者不(所以 x = x+1 month,那么下一行的 x = x-1 monthx = x + 1 month - 1 month) 相同。

另一种方法是坚持要求用户提供在这些异常情况下应该做什么的策略(这种情况……一直都在发生),因为这个问题非常棘手 "solves" 问题充其量只能突出问题,迫使客户端程序员思考并做出决定。

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

DateTime result = new DateTime(first.Ticks + second.Ticks);

最简单的是:

firstDateTime.AddTicks(secondDateTime.Ticks);