Подтвердить что ты не робот

Cache.Add абсолютное истечение - UTC или нет?

Примеры для 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+ лет.

Вопросы?

  • Несмотря на многоголосый и гуглинг, я не смог найти авторитетного обсуждения этого вопроса с MS - может ли кто-нибудь найти что-то в этом отношении?
  • Есть ли причина, по которой использование UtcNow не будет более правильным и/или безопасным?

(Да, я мог бы изучить источник и/или источник Reflector'd, но я ищу полную пошаговую выдувку!)

4b9b3361

Ответ 1

I сообщила об этой ошибке в Microsoft Connect некоторое время назад, но она была закрыта, так как не будет исправлена.

У вас все еще есть проблема в .NET 2.0, если вы укажете свое абсолютное истечение в локальное время.

В течение одного часа в конце летнего времени ваше местное время неоднозначно, поэтому вы можете получить неожиданные результаты, т.е. абсолютное истечение может быть на один час больше ожидаемого.

В Европе время перехода на летнее время закончилось в 02:00 25 октября 2009 года. В приведенном ниже примере показано, что если вы поместили элемент в кеш в 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 или новее - указать абсолютное время истечения в UTC, как указано в Ruben.

Возможно, Microsoft должна рекомендовать использовать UTC в примерах для абсолютного истечения срока действия, но я думаю, что существует путаница, поскольку эта рекомендация действительна только для .NET 2.0 и более поздних версий.

ИЗМЕНИТЬ

Из комментариев:

Но экспозиция возникает только в том случае, если преобразование происходит во время перекрытия. Единственное преобразование, фактически принимающее место, когда вы размещаете элемент с помощью Cache.Add

Проблема возникает, если вы вставляете элемент в кеш с временем AbsoluteExpiration по местному времени в течение этого одного неоднозначного часа в конце летнего времени.

Например, если ваш локальный часовой пояс - среднеевропейский (GMT + 1 зимой, GMT + 2 летом), и вы выполняете следующий код в 01:59:00 25 октября 2009 года:

DateTime absoluteExpiration = DateTime.Now.AddMinutes(2);
Cache.Add(... absoluteExpiration ...)

то элемент останется в кеше на один час и две минуты, а не на две минуты, которые вы обычно ожидаете. Это может быть проблемой для некоторых критически важных приложений (например, биржевой тикер, плата отправления авиакомпаний).

Что происходит здесь (при условии европейского времени, но принцип тот же для любого часового пояса):

  • DateTime.Now = 2009-10-25 01:59:00 локальный. 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 или новее):

DateTime absoluteExpiration = DateTime.UtcNow.AddMinutes(2);
Cache.Add(... absoluteExpiration ...)

Из комментариев:

Или я что-то не хватает?

Нет, нет. Ваш анализ на месте, и если ваше приложение критически важно и работает в течение этого периода в конце DST, вы правы, чтобы использовать DateTime.UtcNow.

В заявлении в Ruben ответьте, что:

вы можете использовать его либо до тех пор, пока тип "Добрый" во время поставки установлен

неверно.

Ответ 2

Cache.Add преобразует дату истечения срока в UTC перед ее сохранением.

Из рефлектора (я пропустил большинство параметров, чтобы упростить его чтение):

public object Add(... DateTime absoluteExpiration ...)
{
    DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
    return this._cacheInternal.DoInsert(... utcAbsoluteExpiration ...);
}

В CacheExpires.FlushExpiredItems, utcAbsoluteExpiration сравнивается с DateTime.UtcNow. Как отмечает Joe в своем ответе, это приводит к неожиданному поведению, когда добавление и истечение срока действия кеша заканчивается в конце летнего времени.

Ответ 3

[Высокое производное от понимания ответа Джеффа Сирна, которое у меня было + 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]. [См. Комментарии и ответы Джо для полного обоснования] Определенно предпочитайте UtcNow как двусмысленность в течение 1 часа переключения DST.

  • Пример остается таким, так что люди, работающие против 1.x, не вводят в заблуждение [и люди могут продолжать делать вид, что DST - это дурацкий случай: P]. [Точно так же, как отметил Джо]. Но это очень спорная позиция, так как это может привести к тому, что материал останется в кеше на дополнительный час.

Мне все еще очень интересно услышать больше деталей, в том числе от любых пони.

EDIT: Я понимаю из комментариев Джо, что я явно не указал на то, что определенно правильнее использовать UtcNow при использовании версии 2.0 или более поздней версии, поскольку каждый подвергается риску кэширования элемента для дополнительного час во время ДНР "час сурка". Я также думаю, что MS-документ должен указывать этот факт (при условии, что они должны упомянуть, что это не относится к 1.1 [независимо от того, помечена ли страница с особым значением 2.0+). Спасибо Джо.

EDIT: у Noda Time будет аккуратная обертка, чтобы сделать это надежным: D