Cache.Add的示例使用DateTime.Now.Add
计算到期日期,即通过:
DateTime.Now.AddSeconds(60)
作为absoluteExpiration
参数的值。
我认为相对于DateTime.UtcNow
计算它会更正确[因为如果夏令时在现在和到期点之间的间隔开始时没有歧义]。
在引入DateTimeKind
之前,我已经猜到缓存管理中存在一些丑陋的黑客攻击,如果时间不是UTC时间,它会做适当的事情。
在.NET 2.0及更高版本中,我猜测它应该正确地处理DateTime
计算为DateTime.UtcNow.AddSeconds(60)
,因为它有DateTime.Kind
用作推理中的输入。
我一直在自信地使用DateTime.UtcNow
作为基础多年,但是没有能够提出这样的理由:在没有任何指出文档高度的情况下,这绝对是正确的事情。误导了4年多。
问题?
(是的,我可以仔细阅读源代码和/或反射器的来源,但我正在寻找一个完整的逐个低点!)
答案 0 :(得分:23)
我前一段时间reported this bug on Microsoft Connect,但它已被关闭,因为无法解决。
如果您在当地时间指定绝对到期时,在.NET 2.0中仍然存在问题。
在夏令时结束的一小时内,您当地的时间不明确,因此您可能会得到意想不到的结果,即绝对到期时间可能比预期长一个小时。
在欧洲,夏令时在2009年10月25日的02:00结束。下面的示例说明如果您在01:59将一个项目放在缓存中并且有2分钟到期,它将保留在缓存中一小时两分钟。
DateTime startTime = new DateTime(2009, 10, 25, 1, 59,0);
DateTime endTime = startTime.AddMinutes(2);
// end time is two minutes after start time
DateTime startUtcTime = startTime.ToUniversalTime();
DateTime endUtcTime = endTime.ToUniversalTime();
// end UTC time is one hour and two minutes after start UTC time
Console.WriteLine("Start UTC time = " + startUtcTime.ToString());
Console.WriteLine("End UTC time = " + endUtcTime.ToString());
.NET 2.0或更高版本的解决方法是按照Ruben的说明指定UTC的绝对到期时间。
微软应该建议在示例中使用UTC进行绝对过期,但我想有可能会产生混淆,因为这个建议仅对.NET 2.0及更高版本有效。
修改强>
来自评论:
但是曝光只会发生 在重叠期间发生转换。 实际采取的单一转换 这个地方就是你提出物品的时候 Cache.Add
只有在夏令时结束时的一个不明确的小时内,以当地时间的AbsoluteExpiration时间插入缓存中的项目时,才会出现此问题。
例如,如果您当地的时区是中欧(冬季GMT + 1,夏季GMT + 2),并且您在2009年10月25日01:59:00执行以下代码:
DateTime absoluteExpiration = DateTime.Now.AddMinutes(2);
Cache.Add(... absoluteExpiration ...)
然后该项目将在缓存中保留一小时两分钟,而不是您通常预期的两分钟。对于一些高度时间关键的应用程序(例如股票代码,航空公司离职板),这可能是一个问题。
这里发生的事情是(假设欧洲时间,但任何时区的原则都是一样的):
DateTime.Now = 2009-10-25 01:59:00 local。 local = GMT + 2,所以UTC = 2009-10-24 23:59:00
.AddMinutes(2)= 2009-10-25 02:01:00 local。 local = GMT + 1,所以UTC = 2009-11-25 01:01:00
Cache.Add在内部将到期时间转换为UTC(2009-11-25 01:01:00),因此到期时间比当前UTC时间(23:59:00)提前一小时两分钟
如果使用DateTime.UtcNow代替DateTime.Now,缓存过期将是两分钟(.NET 2.0或更高版本):
DateTime absoluteExpiration = DateTime.UtcNow.AddMinutes(2);
Cache.Add(... absoluteExpiration ...)
来自评论:
或者我错过了什么?
不,你不是。您的分析是正确的,如果您的应用程序是时间关键的并且在DST结束时的那段时间内运行,那么您使用DateTime.UtcNow是正确的。
鲁本回答的陈述:
只要您提供的种类设置为
,您就可以安全使用
不正确。
答案 1 :(得分:6)
Cache.Add
在存储之前将过期日期转换为UTC。
来自Reflector(我省略了大部分参数以便于阅读):
public object Add(... DateTime absoluteExpiration ...)
{
DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
return this._cacheInternal.DoInsert(... utcAbsoluteExpiration ...);
}
在CacheExpires.FlushExpiredItems
中,utcAbsoluteExpiration
与DateTime.UtcNow
进行比较。作为Joe notes in his answer,当缓存项的添加和到期跨越夏令时结束时,这会导致意外行为。
答案 2 :(得分:3)
[来自Jeff Sternal的答案的见解的高度衍生,我在不完整的付款中得到+1:D]
似乎在1.1(没看过1.0,但我认为它相似),在没有DateTime.Kind
并且发布了DateTime.Now
相对时间的例子的情况下,他们立即感到舒服ToUniversalTime()
立刻。{/ p>
...因此
如果[API用户]使用DateTime.UtcNow
,你将会陷入混乱(并且在调用Cache.Add
期间对DST敏感)
在2.0 [和3.x]中,只要你提供的时间Kind
被设置,你就可以安全使用[如果你有时间的话通常可以DateTime.Now
或UtcNow
]。 [请参阅Joe的评论并回答完整的理由]绝对更喜欢UtcNow
作为DST切换1小时的歧义。
这个例子保持原样,因此反对1.x的人不会被误导[并且人们可以继续假装DST是一个古怪的边缘案例:P]。 [Ditto,正如Joe所指出的那样]但这是一个非常值得商榷的立场,因为这会导致一些东西停留在缓存中一小时。
我仍然非常有兴趣听到更多细节,包括那里的任何小马。
编辑:我从Joe的评论中意识到,如果使用2.0或更高版本,我没有明确地说明 使用UtcNow
肯定更正确的事实在DST'土拨鼠时段'期间项目被缓存一小时的风险。我也认为MS doc应该指出这个事实(附带条件是他们需要提到这不适用于1.1 [无论页面是否标记为特定的2.0+]。感谢Joe。
编辑:Noda Time将有一个整洁的包装,使这个万无一失:D