c# - interval - datetime utc
Cómo especificar que los objetos DateTime recuperados de EntityFramework deben ser DateTimeKind.UTC (4)
Esta pregunta ya tiene una respuesta aquí:
- Entity Framework DateTime y UTC 11 respuestas
Tengo el programa C # donde todos los objetos DateTimeKind.UTC
son DateTimeKind.UTC
. Al guardar los objetos en la base de datos, almacena el UTC como se esperaba. Sin embargo, al recuperarlos, son DateTimeKind.Unspecified
. ¿Hay alguna manera de decirle a Entity Framework (Code First) cuando crea objetos DateTime
en C # para usar siempre DateTimeKind.UTC
?
No, no hay. Y en realidad es DateTimeKind.Unspecified
.
Sin embargo, si le preocupa soportar múltiples zonas horarias, debería considerar usar DateTimeOffset . Es como un DateTime regular, excepto que no representa una "perspectiva" de tiempo, representa una vista absoluta, en la cual 3PM (UTC - 3) es igual a 4PM (UTC - 2). DateTimeOffset contiene DateTime y la zona horaria, y es compatible tanto con EntityFramework como con SQL Server.
Puede hacer que su contexto de datos arregle todos los valores relevantes a medida que avanza. Lo siguiente lo hace con un caché de propiedades para los tipos de entidades, para evitar tener que examinar el tipo cada vez:
public class YourContext : DbContext
{
private static readonly List<PropertyInfo> EmptyPropsList = new List<PropertyInfo>();
private static readonly Hashtable PropsCache = new Hashtable(); // Spec promises safe for single-reader, multiple writer.
// Spec for Dictionary makes no such promise, and while
// it should be okay in this case, play it safe.
private static List<PropertyInfo> GetDateProperties(Type type)
{
List<PropertyInfo> list = new List<PropertyInfo>();
foreach(PropertyInfo prop in type.GetProperties())
{
Type valType = prop.PropertyType;
if(valType == typeof(DateTime) || valType == typeof(DateTime?))
list.Add(prop);
}
if(list.Count == 0)
return EmptyPropsList; // Don''t waste memory on lots of empty lists.
list.TrimExcess();
return list;
}
private static void FixDates(object sender, ObjectMaterializedEventArgs evArg)
{
object entity = evArg.Entity;
if(entity != null)
{
Type eType = entity.GetType();
List<PropertyInfo> rules = (List<PropertyInfo>)PropsCache[eType];
if(rules == null)
lock(PropsCache)
PropsCache[eType] = rules = GetPropertyRules(eType); // Don''t bother double-checking. Over-write is safe.
foreach(var rule in rules)
{
var info = rule.PropertyInfo;
object curVal = info.GetValue(entity);
if(curVal != null)
info.SetValue(entity, DateTime.SpecifyKind((DateTime)curVal, rule.Kind));
}
}
}
public YourContext()
{
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += FixDates;
/* rest of constructor logic here */
}
/* rest of context class here */
}
Esto también se puede combinar con atributos para permitir que uno configure el DateTimeKind
debería tener cada propiedad, almacenando un conjunto de reglas sobre cada propiedad, en lugar de solo PropertyInfo
, y buscando el atributo en GetDateProperties
.
Mi solución, primero usando código: declare las propiedades DateTime de esta manera:
private DateTime _DateTimeProperty;
public DateTime DateTimeProperty
{
get
{
return _DateTimeProperty;
}
set
{
_DateTimeProperty = value.ToKindUtc();
}
}
También puede crear la propiedad como:
private DateTime? _DateTimeProperty;
public DateTime? DateTimeProperty
{
get
{
return _DateTimeProperty;
}
set
{
_DateTimeProperty = value.ToKindUtc();
}
}
ToKindUtc()
es una extensión para cambiar DateTimeKind.Unspecified
ToUniversalTime()
to DateTimeKind.Utc
o llamar a ToUniversalTime()
si kind es DateTimeKind.Local
Aquí el código para las extensiones:
public static class DateTimeExtensions
{
public static DateTime ToKindUtc(this DateTime value)
{
return KindUtc(value);
}
public static DateTime? ToKindUtc(this DateTime? value)
{
return KindUtc(value);
}
public static DateTime ToKindLocal(this DateTime value)
{
return KindLocal(value);
}
public static DateTime? ToKindLocal(this DateTime? value)
{
return KindLocal(value);
}
public static DateTime SpecifyKind(this DateTime value, DateTimeKind kind)
{
if (value.Kind != kind)
{
return DateTime.SpecifyKind(value, kind);
}
return value;
}
public static DateTime? SpecifyKind(this DateTime? value, DateTimeKind kind)
{
if (value.HasValue)
{
return DateTime.SpecifyKind(value.Value, kind);
}
return value;
}
public static DateTime KindUtc(DateTime value)
{
if (value.Kind == DateTimeKind.Unspecified)
{
return DateTime.SpecifyKind(value, DateTimeKind.Utc);
}
else if (value.Kind == DateTimeKind.Local)
{
return value.ToUniversalTime();
}
return value;
}
public static DateTime? KindUtc(DateTime? value)
{
if (value.HasValue)
{
return KindUtc(value.Value);
}
return value;
}
public static DateTime KindLocal(DateTime value)
{
if (value.Kind == DateTimeKind.Unspecified)
{
return DateTime.SpecifyKind(value, DateTimeKind.Local);
}
else if (value.Kind == DateTimeKind.Utc)
{
return value.ToLocalTime();
}
return value;
}
public static DateTime? KindLocal(DateTime? value)
{
if (value.HasValue)
{
return KindLocal(value.Value);
}
return value;
}
}
Recuerde incluir en el archivo del modelo.
using TheNameSpaceWhereClassIsDeclared;
El método de propiedad set se invoca cuando se lee desde la base de datos con EF o cuando se asigna en el método de edición de un controlador MVC.
Advertencia: si está en formularios web, si edita las fechas en la zona horaria local, DEBE convertir la fecha a UTC antes de enviarla al servidor.
Eche un vistazo a la respuesta de michael.aird aquí: https://.com/a/9386364/279590 Imprime el tipo de fecha UTC durante la carga, con un evento en ObjectMaterialized.