Cache.Add 绝对过期 - 是否基于 UTC?
Cache.Add 的示例使用DateTime.Now.Add
来计算过期时间,即它传递:
DateTime.Now.AddSeconds(60)
作为 absoluteExpiration
参数的值。
我认为相对于 DateTime.UtcNow
计算它会更正确[因为如果夏令时在现在和到期点之间的中间时间开始,则不会有歧义]。
在引入 DateTimeKind
之前,我猜测缓存管理中存在一些丑陋的黑客行为,以使其在时间不是 UTC 时间时执行适当的操作。
在 .NET 2.0 及更高版本中,我猜测它应该正确处理计算为 DateTime.UtcNow.AddSeconds(60)
的 DateTime
,因为它具有 DateTime .Kind
用作其推理中的输入。
多年来,我一直自信地使用 DateTime.UtcNow
作为基础,但在没有任何内容指出文档有任何内容的情况下,无法提出一个理由表明这绝对是正确的做法4年多以来一直具有高度误导性。
问题?
- 尽管进行了大量的狂欢和谷歌搜索,但我无法从 MS 找到任何关于此问题的权威讨论 - 有人能找到与此相关的内容吗?
- 为什么使用 UtcNow 不会更正确和/或更安全?
(是的,我可以仔细阅读来源和/或反射源,但我正在寻找完整的详细内幕!)
The examples for Cache.Add uses DateTime.Now.Add
to compute the expiration, i.e. it passes:
DateTime.Now.AddSeconds(60)
as the value of the absoluteExpiration
parameter.
I'd have thought that computing it relative to DateTime.UtcNow
would be more correct [as there is no ambiguity if Daylight Savings Time starts in the intervening time between now and the expiration point].
Before the introduction of DateTimeKind
, I'd have guessed that there's some ugly hacks in the cache management to make it do something appropriate if the time was not a UTC time.
In .NET 2.0 and later, I'm guessing that it should handle a DateTime
calculated as DateTime.UtcNow.AddSeconds(60)
correctly given that it has DateTime.Kind
to use as an input in its inferences.
I've been confidently using DateTime.UtcNow
as the base for years, but wasnt able to come up with a rationale that this is definitely the correct thing to do in the absence of anything pointing out the documentation has been highly misleading for 4+ years.
The questions?
- Despite much bingage and googling I wasnt able to find any authoritative discussion on this from MS - can anyone locate something regarding this?
- Is there any reason why using UtcNow wouldnt be more correct and/or safe?
(Yes, I could peruse the source and/or the Reflector'd source, but am looking for a full blow-by-blow lowdown!)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我不久前在 Microsoft Connect 上报告了此错误,但它是已关闭,因为无法修复。
如果您指定本地时间的绝对过期时间,那么在 .NET 2.0 中仍然会遇到问题。
在夏令时结束的一小时内,您的当地时间是不明确的,因此您可能会得到意想不到的结果,即绝对到期时间可能比预期长一小时。
在欧洲,夏令时于 2009 年 10 月 25 日 02:00 结束。下面的示例说明,如果您在 01:59 将一个项目放入缓存且过期时间为 2 分钟,则该项目将在缓存中保留一小时,并且两分钟。
.NET 2.0 或更高版本的解决方法是指定 UTC 中的绝对过期时间,如 Ruben 指出的那样。
Microsoft 也许应该建议在示例中使用 UTC 来表示绝对过期,但我猜这可能会造成混淆,因为此建议仅对 .NET 2.0 及更高版本有效。
编辑
来自评论:
仅当您在夏令时结束时的一个不明确的小时内将本地时间的 AbsoluteExpiration 时间插入到缓存中时,才会出现此问题。
例如,如果您的本地时区是中欧(冬季为 GMT+1,夏季为 GMT+2),并且您在 2009 年 10 月 25 日 01:59:00 执行以下代码:
则该项目将保留在缓存一小时零两分钟,而不是您通常期望的两分钟。对于某些时间要求较高的应用程序(例如股票行情、航班起飞板)来说,这可能是一个问题。
这里发生的是(假设欧洲时间,但原则对于任何时区都是相同的):
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 = 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 或更高版本):
从注释中:
不,你不是。您的分析是正确的,如果您的应用程序对时间要求严格并且在 DST 结束时的这段时间内运行,那么您使用 DateTime.UtcNow 是正确的。
鲁本的回答中的声明是:
不正确,您就可以安全使用。
I reported this bug on Microsoft Connect some time ago, but it's been closed as won't fix.
You still have a problem in .NET 2.0 if you specify your absolute expiration in local time.
During one hour at the end of daylight savings time, your local time is ambiguous, so you can get unexpected results, i.e. the absolute expiration can be one hour longer than expected.
In Europe, daylight savings time ended at 02:00 on 25 October 2009. The sample below illustrates that if you placed an item in the cache at 01:59 with an expiration of 2 minutes, it would remain in the cache for one hour and two minutes.
The workaround for .NET 2.0 or later is to specify the absolute expiration time in UTC as pointed out by Ruben.
Microsoft should perhaps be recommending to use UTC in the examples for absolute expiration, but I guess there is the potential for confusion since this recommendation is only valid for .NET 2.0 and later.
EDIT
From the comments:
The problem will only happen if you insert an item in the cache with an AbsoluteExpiration time in local time during that one ambiguous hour at the end of daylight savings time.
So for example, if your local time zone is Central European (GMT+1 in winter, GMT+2 in summer), and you execute the following code at 01:59:00 on 25 October 2009:
then the item will remain in the cache for one hour and two minutes, rather than the two minutes you would normally expect. This can be a problem for some highly time-critical applications (e.g. stock ticker, airline departures board).
What's happening here is (assuming European time, but the principle is the same for any time zone):
DateTime.Now = 2009-10-25 01:59:00 local. local=GMT+2, so UTC = 2009-10-24 23:59:00
.AddMinutes(2) = 2009-10-25 02:01:00 local. local = GMT+1, so UTC = 2009-11-25 01:01:00
Cache.Add internally converts the expiration time to UTC (2009-11-25 01:01:00) so the expiration is one hour and two minutes ahead of the current UTC time (23:59:00).
If you use DateTime.UtcNow in place of DateTime.Now, the cache expiration will be two minutes (.NET 2.0 or later):
From the comments:
No you're not. Your analysis is spot on and if your application is time-critical and runs during that period at the end of DST, you're right to be using DateTime.UtcNow.
The statement in Ruben's answer that:
is incorrect.
Cache.Add
在存储之前将过期日期转换为 UTC。来自Reflector(我省略了大部分参数以使其更易于阅读):
在
CacheExpires.FlushExpiredItems
中,utcAbsoluteExpiration
与DateTime.UtcNow
进行比较代码>.正如 Joe 在他的回答中指出的,当缓存项的添加和过期跨越夏令时结束时,会导致意外行为。Cache.Add
converts the expiration date to UTC before storing it.From Reflector (I've omitted most of the parameters to make it easier to read):
In
CacheExpires.FlushExpiredItems
,utcAbsoluteExpiration
is compared toDateTime.UtcNow
. As Joe notes in his answer, this causes unexpected behavior when a cache item's addition and expiration span the end of daylight saving time.[高度衍生自 Jeff Sternal 的答案,我在不完整的付款中+1:D]
似乎在 1.1 中(没有看 1.0,但我认为它类似),在没有
DateTime 的情况下.Kind
并发布了具有DateTime.Now
相对时间的示例,他们感觉立即调用ToUniversalTime()
很舒服。因此...
在 1.x 中,如果您 [API 用户] 使用
DateTime.UtcNow
(并且在调用期间对 DST 开始很敏感),您最终会陷入混乱Cache.Add
)在 2.0 [和 3.x] 中,只要
Kind
即可安全使用设置您提供的时间[如果您从DateTime.Now
或UtcNow
获取时间,通常会是这样]。 [有关完整理由,请参阅 Joe 的评论和回答] 绝对更喜欢UtcNow
作为 1 小时 DST 切换的歧义。这个例子保持原样,这样反对 1.x 的人们就不会被误导[人们可以继续假装 DST 是一个古怪的边缘情况 :P]。 [同上,正如 Joe 所指出的] 但这是一个非常有争议的立场,因为这可能会导致内容在缓存中停留额外一个小时。
我仍然很有兴趣听到更多细节,包括来自任何小马的任何细节。
编辑:我从 Joe 的评论中意识到,我没有明确指出这样一个事实:如果使用 2.0 或更高版本,那么使用
UtcNow
肯定更正确。在夏令时“土拨鼠时间”期间,该项目有被额外缓存一小时的风险。我还认为 MS 文档应该指出这一事实(附带条件是他们需要提及这不适用于 1.1 [无论页面是否标记为 2.0+ 特定]。谢谢乔。编辑:Noda Time 将有一个简洁的包装使这个万无一失:D
[Highly derivative of insights from Jeff Sternal's answer, which I've +1'd in incomplete payment :D]
It seems that in 1.1 (didnt look at 1.0 but I assume its similar), in the absence of a
DateTime.Kind
and having published examples with aDateTime.Now
-relative time, they feel comfortable immediately callingToUniversalTime()
immediately.Hence...
in 1.x, you'll end up with a mess if you [the API user] use
DateTime.UtcNow
(and there's a sensitivity to DST starting during the call toCache.Add
)in 2.0 [and 3.x], you're safe to use either as long as the
Kind
on the time you supply is set [which it normally would be if you got the time fromDateTime.Now
orUtcNow
]. [See Joe's comments and answer for full rationale] Definitely preferUtcNow
as ambiguity for 1 hour of DST switchover.The example stays as-is so people working against 1.x dont get misled [and people can go on pretending that DST is a wacky edge case :P]. [Ditto, as pointed out by Joe] But this is a very debatable stance as this can result in stuff staying in the cache for an extra hour.
I'm still very interested to hear more details, including any from any Ponies out there.
EDIT: I realise from Joe's comments that I didn't explicitly call out the fact that it is definitely more correct to use
UtcNow
if using 2.0 or later as one is exposed to risk of the item being cached for an extra hour during the DST 'groundhog hour'. I also think the MS doc should point this fact out (with the proviso that they need to mention that this does not to apply to 1.1 [regardless of whether the page is marked 2.0+ specific]. Thanks Joe.EDIT: Noda Time will have a neat wrapper to make this foolproof :D