在尝试将分钟/秒舍入到最接近的小时(在这种情况下忽略分/秒部分)时,对于以下行为有点好奇。
我尝试了两种方法,并使用BenchMarkDotNet进行基准测试。
private DateTime testData = DateTime.Now;
[Benchmark]
public DateTime CeilingUsingNewOperator() => new DateTime(testData.Year,testData.Month,testData.Day,testData.Hour + 1,0,0);
[Benchmark]
public DateTime CeilingUsingAddOperator() => testData.AddHours(1).AddMinutes(-testData.Minute).AddSeconds(-testData.Second);
以下是结果。
+-------------------------+-----------+----------+----------+
| Method | Mean | Error | StdDev |
+-------------------------+-----------+----------+----------+
| CeilingUsingNewOperator | 207.99 ns | 3.465 ns | 3.072 ns |
| CeilingUsingAddOperator | 108.74 ns | 1.429 ns | 1.337 ns |
+-------------------------+-----------+----------+----------+
我很好奇为什么New Operator会慢一些,如果我假设,每次我们在第二种方法中调用AddX方法时,我们都会得到一个Datetime。
有人可以解释一下吗?
答案 0 :(得分:7)
可以使用进一步的基准来验证这一点,但我强烈怀疑这是因为AddHours
,AddMinutes
和AddSeconds
方法都可以在不执行任何复杂的日期/时间算法的情况下工作。他们只需要:
DateTime
DateTime
值您还使用Minute
和Second
属性,但可以在不计算月/日/年值的情况下计算它们。
将其与需要的“单一构造函数”调用进行比较:
计算年,月和日的代码需要“理解”公历 - 为了提取信息,要执行的计算要复杂得多。有些可以缓存(例如每年开始时的刻度数)但是在内存访问方面你会失去一些参考位置。
我希望大多数有效的方法是直接计算滴答的方法,只需简单算术:
long originalTicks = testData.Ticks;
long hoursSinceEpoch = originalTicks / TimeSpan.TicksPerHour;
long newTicks = hoursSinceEpoch * TimeSpan.TicksPerHour;
return new DateTime(newTicks, testData.Kind);
一个重要的缺点:我认为当DateTimeKind
为Local
时,围绕夏令时边界会有问题,因为实际上有四种种而不是三种我们通常使用(Utc
,Local
,Unspecified
)。 Local
实际上被分成两部分,以“知道”当前一个选项和后一个选项之间的值不明确时,正在表示哪个日期/时间。如果你没有使用Local
类,那应该没问题。