2009-11-06 8 views
29

Los ejemplos para Cache.Add utiliza DateTime.Now.Add para calcular la caducidad, es decir, que pasa a:Cache.Add expiración absoluta - ¿UTC basado o no?

DateTime.Now.AddSeconds(60) 

como el valor del parámetro absoluteExpiration.

Hubiera pensado que calcularlo en relación con DateTime.UtcNow sería más correcto [ya que no hay ambigüedad si el horario de verano se inicia en el intervalo entre ahora y el punto de expiración].

Antes de la introducción de DateTimeKind, habría supuesto que hay algunos hacks feos en la administración de la caché para que haga algo apropiado si la hora no era una hora UTC.

En .NET 2.0 y posterior, supongo que debe manejar un DateTime calcula como DateTime.UtcNow.AddSeconds(60) correctamente dado que tiene DateTime.Kind utilizar como insumo en sus inferencias.

He estado utilizando con confianza DateTime.UtcNow como la base durante años, pero no he podido encontrar un fundamento de que esto es definitivamente lo correcto en ausencia de algo que indique que la documentación ha sido muy engañosa para 4 + años.

Las preguntas?

  1. A pesar de mucho bingage y Google no pude encontrar ninguna discusión autorizada sobre esto de MS - ¿alguien puede encontrar algo al respecto?
  2. ¿Hay alguna razón por la cual usar UtcNow no sería más correcto y/o seguro?

(Sí, pude leer la fuente y/o la fuente Reflector'd, pero estoy buscando un Lowdown completa golpe por golpe!)

Respuesta

20

I reported this bug on Microsoft Connect hace algún tiempo, pero ha sido cerrado como no solucionará

usted todavía tiene un problema en .NET 2.0 si se especifica su expiración absoluta en hora local.

Durante una hora al final del horario de verano, su hora local es un mbiguous, por lo que puede obtener resultados inesperados, es decir, la expiración absoluta puede ser una hora más de lo esperado.

En Europa, el horario de verano finalizaba a las 02:00 del 25 de octubre de 2009. El ejemplo siguiente ilustra que si colocaba un elemento en la memoria caché a las 01:59 con una caducidad de 2 minutos, permanecería en la memoria caché por una hora y dos minutos.

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()); 

La solución para .NET 2.0 o posterior es especificar el tiempo de expiración absoluta en UTC como señaló Ruben.

Microsoft tal vez debería recomendar el uso de UTC en los ejemplos de caducidad absoluta, pero creo que existe la posibilidad de confusión, ya que esta recomendación solo es válida para .NET 2.0 y posterior.

EDITAR

De los comentarios:

embargo, la exposición sólo se produce si la conversión sucede durante la superposición. La conversión simple que realmente toma el lugar es cuando aloja el artículo con Caché.Agregue

El problema solo ocurrirá si inserta un elemento en la memoria caché con un tiempo de expiración absoluta en hora local durante esa hora ambigua al final del horario de verano.

Así, por ejemplo, si su zona horaria local es Central Europeo (GMT + 1 en invierno, GMT + 2 en verano), y se ejecuta el código siguiente en 01:59:00 el 25 de octubre de 2009:

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

luego el artículo permanecerá en la memoria caché durante una hora y dos minutos, en lugar de los dos minutos que normalmente esperaría. Esto puede ser un problema para algunas aplicaciones altamente críticas (por ejemplo, el ticker de valores, el tablero de salidas de aerolíneas).

lo que está sucediendo aquí es (suponiendo hora de Europa, pero el principio es el mismo para cualquier zona horaria):

  • DateTime.Now = 2009-10-25 01:59:00 local. local = GMT + 2, por lo que UTC = 2009-10-24 23:59:00

  • .AddMinutes (2) = 2009-10-25 02:01:00 local. local = GMT + 1, por lo que UTC = 2009-11-25 01:01:00

  • Cache.Add convierte internamente la hora de caducidad en UTC (2009-11-25 01:01:00) por lo que la caducidad es una hora y dos minutos antes de la hora UTC actual (23:59:00).

Si utiliza DateTime.UtcNow en lugar de DateTime.Now, la caducidad de la caché será de dos minutos (.NET 2.0 o posterior):

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

De los comentarios:

¿O me falta algo?

No, no lo eres. Su análisis es perfecto y si su aplicación es de tiempo crítico y se ejecuta durante ese período al final del horario de verano, está en lo correcto al utilizar DateTime.UtcNow.

La declaración en la respuesta de Ruben que:

usted es seguro de usar, ya sea mientras la Clase en el momento en que la oferta es establecer

es incorrecto.

+0

@Joe: estoy de acuerdo en que el caso que ha presentado arriba es correcto. Pero la exposición solo ocurre si la conversión ocurre durante la superposición. La única conversión que realmente tiene lugar es cuando aloja el elemento con Cache.Add. No estoy seguro de qué puntos le atribuye a Jeff sobre la mejor política: especificar un vencimiento basado en Utc o cómo los documentos deberían cubrirlo. ¿No es ese el punto que hice en mi respuesta [a mí mismo]? ¿O me estoy perdiendo algo? (Entiendo el problema de raíz, por lo que mencioné la pregunta en primer lugar) –

+0

@Joe: +1 re el enlace al elemento Conectar: ​​acepto que es un error de doc –

+0

@Joe - has atribuido incorrectamente algunas cosas a mi respuesta, así que he editado su respuesta para arreglarlas (aparte de eso, buen descubrimiento). –

6

Cache.Add convierte la fecha de caducidad a UTC antes de guardar eso.

De Reflector (he omitido la mayor parte de los parámetros para que sea más fácil de leer):

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

En CacheExpires.FlushExpiredItems, utcAbsoluteExpiration se compara con DateTime.UtcNow. Como Joe notes in his answer, esto causa un comportamiento inesperado cuando la adición y la caducidad de un elemento de caché se extienden al final del horario de verano.

+0

¡Buen trabajo! Supongo que está en 3.5SP1. ¿Alguna idea sobre 2.0 raw, 1.0? ¿'DateTimeUtil.ConvertToUniversalTime' hace algo más que hacer una simple llamada a' DateTime.ToUniversalTime' cuando 'Kind == DateTimeKind.Local'? Sí, debería cargar Reflector. Cargando ... –

+0

Gracias. De hecho, tengo un interés permanente en el funcionamiento interno del cahce: http://stackoverflow.com/questions/1434284/when-does-asp-net-remove-expired-cache-items. :) Ese fue en realidad el marco 2.0. ¡No he mirado a los demás, pero espero los resultados de su investigación! –

+0

Ah, 'DateTime.ToUniversalTime' y sus amigos tienen una pila de hacks siempre y cuando su brazo lo arregle silenciosamente (en 1.x sin' DateTime.Kind', estoy seguro de que fue más interesante - IIRC siempre se ajustó y por lo tanto, ¿tenía que ser más cuidadoso al llamarlo?). –

3

[altamente derivada de puntos de vista de la respuesta de Jeff esternal, que he hecho +1 en el pago incompleto: D]

Parece que en el apartado 1.1 (aspecto aún no ha a 1,0 pero supongo que su similar), en la ausencia de un DateTime.Kind y de haber publicado ejemplos con un tiempo relativo DateTime.Now, se sienten cómodos llamando inmediatamente al ToUniversalTime().

Por lo tanto ...

  1. en 1.x, que va a terminar con un lío si [el usuario API] utiliza DateTime.UtcNow (y hay una sensibilidad al horario de verano a partir durante la llamada a Cache.Add)

  2. en 2.0 [3.x], que está es seguro de usar, siempre y cuando el Kind en el momento de su suministro esté configurado [lo que normalmente sería si tuviera el tiempo de DateTime.Now o UtcNow]. [Consulte los comentarios y la respuesta de Joe para obtener una explicación completa] Definitivamente, prefiera UtcNow como ambigüedad para 1 hora de cambio de horario de verano.

  3. El ejemplo se queda como está, por lo que las personas que trabajan en contra de 1.x no se engañan [y la gente puede seguir pretendiendo que el horario de verano es un caso de borde raro: P]. [Ídem, como lo señaló Joe] Pero esta es una postura muy discutible, ya que puede dar lugar a que las cosas permanezcan en el caché durante una hora más.

Todavía estoy muy interesado en escuchar más detalles, incluidos los de cualquier Ponis que haya.

EDIT: Me doy cuenta de los comentarios de Joe que no llamar explícitamente el hecho de que es definitivamente más correcto utilizar UtcNow si se utiliza 2.0 o posterior, que uno está expuesto al riesgo de que el artículo que se está almacenado en caché para una hora extra durante el horario de verano 'hora de la marmota'. También creo que el MS debe señalar este hecho (con la condición de que deben mencionar que esto no se aplica a 1.1 [independientemente de si la página está marcada 2.0+ específica]. Gracias Joe.

EDITAR: Noda tiempo tendrá un contenedor limpio para hacer esta prueba de tontos:. D

+0

Interesante, tendré que mirar más de cerca a 1.1 cuando tenga algo de tiempo este fin de semana. –

+1

"n 2.0 ...puede usar "- no. La hora local es ambigua durante una hora al año al finalizar el horario de verano, por lo que si especifica una hora local durante esta hora, puede tardar una hora. Consulte mi respuesta. – Joe

+0

@ Joe: AIUI, el único problema es cuando coloca una entrada de expiración absoluta con una hora local en 1: 59: 59.999 y Cache.Add [indirectamente] hace la conversión después de que se ha producido la conmutación. En todos los demás casos, conoce el DateTimeKind del el tiempo que especifica y usa esto para convertir (o no) el tiempo a Utc para su uso interno. ¿Usted miró el código? –