Cache.Add绝对到期 - 是否基于UTC?

时间:2009-11-06 16:06:39

标签: .net asp.net datetime utc system.web.caching

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年多。

问题?

  1. 尽管有很多bingage和谷歌搜索我无法从MS找到任何权威的讨论 - 任何人都可以找到关于此的东西?
  2. 为什么使用UtcNow更正确和/或更安全?
  3. (是的,我可以仔细阅读源代码和/或反射器的来源,但我正在寻找一个完整的逐个低点!)

3 个答案:

答案 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中,utcAbsoluteExpirationDateTime.UtcNow进行比较。作为Joe notes in his answer,当缓存项的添加和到期跨越夏令时结束时,这会导致意外行为。

答案 2 :(得分:3)

[来自Jeff Sternal的答案的见解的高度衍生,我在不完整的付款中得到+1:D]

似乎在1.1(没看过1.0,但我认为它相似),在没有DateTime.Kind并且发布了DateTime.Now相对时间的例子的情况下,他们立即感到舒服ToUniversalTime()立刻。{/ p>

...因此

    在1.x中的
  1. 如果[API用户]使用DateTime.UtcNow,你将会陷入混乱(并且在调用Cache.Add期间对DST敏感)

  2. 在2.0 [和3.x]中,只要你提供的时间Kind被设置,你就可以安全使用[如果你有时间的话通常可以DateTime.NowUtcNow]。 [请参阅Joe的评论并回答完整的理由]绝对更喜欢UtcNow作为DST切换1小时的歧义。

  3. 这个例子保持原样,因此反对1.x的人不会被误导[并且人们可以继续假装DST是一个古怪的边缘案例:P]。 [Ditto,正如Joe所指出的那样]但这是一个非常值得商榷的立场,因为这会导致一些东西停留在缓存中一小时。

  4. 我仍然非常有兴趣听到更多细节,包括那里的任何小马。

    编辑:我从Joe的评论中意识到,如果使用2.0或更高版本,我没有明确地说明 使用UtcNow肯定更正确的事实在DST'土拨鼠时段'期间项目被缓存一小时的风险。我也认为MS doc应该指出这个事实(附带条件是他们需要提到这不适用于1.1 [无论页面是否标记为特定的2.0+]。感谢Joe。

    编辑:Noda Time将有一个整洁的包装,使这个万无一失:D