有没有更好的方法将一个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的回答中一样
答案 0 :(得分:36)
将两个DateTime
值一起添加是没有意义的。如果你想表示" 11年,2个月,5天,10个小时,10分钟和11秒"那么你应该代表那个。那个不与0011-02-05T10:10:11相同。特别是,您永远无法添加" 2个月和30天"例如。同样,您永远无法添加一年,因为您在一个日期内的月值和日期值不能为0。
现在没有BCL类型来表示" 11年[...]"但你可以很容易地创建自己的。作为替代方案,您可以使用具有Period
的{{3}}项目来实现此目的:
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
类课程。这很容易做到 - 例如:
// 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日,具体取决于它是否是闰年。
我在这里列出的简单方法,通过中间值,有其缺点,因为截断可能发生两次(增加年数,然后增加几个月),当它不需要时 - 例如,&# 34; 2月29日+ 1年+ 1个月"可能逻辑上是" 3月29日"但它实际上最终将成为" 3月28日"因为截至2月28日将在月份加入之前发生。
试图找出一个"对"做日历算术的方法是Noda Time,特别是在某些情况下,人们可能不同意"对"答案是。在上面的代码中,我选择了简单性和可预测性 - 根据您的实际需求,您可能需要更复杂的代码。
答案 1 :(得分:1)
您有一个代表某个时间点的DateTime
。并且您希望为其添加若干年/月/日/小时/分钟/秒。
DataTime
的变化不是一个点,它是一个向量(点之间的差异)。将一个误认为另一个很容易,因为它们通常具有相似的结构。但是,这种类型错误会导致很多痛苦。
避免它不能解决你的痛苦,但它使它易于管理。
将两个DateTime
加在一起就是一起添加两个点。有点像将洛杉矶的位置添加到纽约。
现在,将洛杉矶的“矢量”添加到纽约到伦敦是有意义的 - 因为旅行矢量是一个矢量,而不是一个点。点+向量只是一个点。
所以这意味着你需要创建一个时间向量类型。一个简单的时间跨度是一个选项,但可能不合适:因为你关心的是月,年和日,而不是纳秒或绝对持续时间。
我将复制矢量名CalendarVector
,因为它代表日历上的移动,而不是时间本身。
一个简单的第一步是创建每个子类型时间的元组 - 年,月,日等 - 然后以任意顺序将它们添加到原始DateTime
并重载{{1 }}
你应该支持:
operator+
理想。 DateTime = DateTime + CalendarVector
CalendarVector = CalendarVector + CalendarVector
CalendarVector = CalendarVector - CalendarVector
CalendarVector = int * CalendarVector
CalendarVector = - CalendarVector
DateTime = DateTime - CalendarVector
CalendarVector = DateTime - DateTime
重载是可选的,但可能不需要。
然而,这只会让你走到一半。
剩下的最大问题是CalendarVector添加无法通勤。将1个月添加到DateTime,然后添加1天,与添加1天然后添加1个月不同。
这是根本性的。
存在“1月31日1个月后意味着什么”的问题,可以回答,但对该问题的任何合理答案都不能解决通勤问题。
你计划好的构造函数 - 你在那里喂它的年数,月数,日数,小时数,分钟数 - 因此含糊不清。
因此,强大的解决方案不应该具有该构造函数。
解决方案是创建您明确添加的CalendarVector + DateTime
,Years
,Months
,Days
,Hours
和Minutes
类型。它们一起添加的顺序是它们应用于您添加到它的Seconds
的顺序。在DateTime
上进行最终申请之前,可以避免通勤和“简化”,因此DateTime
不零转换。
+1 year, +2 days, -1 month, -1 year, -2 days, +1 month
存在相关问题 - 它应该返回DateTime-DateTime
CalendarVector
,v
,但有多个此类向量。球面坐标可能会出现同样的问题 - 你的意思是围绕地球的短路还是长路?在某些情况下并不重要 - 但是你将结果减半以找到中间点。此外,当您接近“世界的另一端”时,您会遇到不连续性。
所以我的建议是维护lhs = rhs + v
对象的转换列表。 DateTime
是一个转换,包括向year字段添加1,然后修复其他字段以使它们保持一致。这些转变支持否定。增加是从左到右一次应用它们。否定也可能会颠倒应用程序的顺序,并且“相同类型”的相邻转换可能会合并(因此+1个月-1个月成为身份转换,而不是基于下个月月末的夹紧操作),或者不是(所以1 year
,然后下一行的x = x+1 month
与x = x-1 month
相同。
另一种方法是坚持要求用户提供在这些特殊情况下(在所有时间内发生)应该做什么的策略,因为这个问题非常棘手,以至于“解决”问题的库可以最好突出问题并强迫客户程序员思考它们并做出决定。
答案 2 :(得分:1)
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);