Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to specify that DateTime objects retrieved from EntityFramework should be DateTimeKind.UTC [duplicate]

I have C# program where all DateTime objects are DateTimeKind.UTC. When saving the objects to the database it stores UTC as expected. However, when retrieving them, they are DateTimeKind.Unspecified. Is there a way to tell Entity Framework (Code First) when creating DateTime objects in C# to always use DateTimeKind.UTC?

like image 251
William Avatar asked Apr 07 '15 16:04

William


People also ask

How do you convert DateTime to UTC?

To convert the time in a non-local time zone to UTC, use the TimeZoneInfo. ConvertTimeToUtc(DateTime, TimeZoneInfo) method. To convert a time whose offset from UTC is known, use the ToUniversalTime method. If the date and time instance value is an ambiguous time, this method assumes that it is a standard time.

How do I change the date format in Entity Framework?

Entity Framework DateTime format when editing entry Id... and ... Date.... It's defined as so:...public class Timetable { public int Id { get; set; } [Required, Column(TypeName = "Date"), DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")] public DateTime Date { get; set; } } ... I then scaffolded some basic...

What does DateTime SpecifyKind do?

The SpecifyKind method creates a new DateTime object using the specified kind parameter and the original time value. The returned DateTime value does not represent the same instant in time as the value parameter, and SpecifyKind is not a time zone conversion method.

What is DateTimeOffset C#?

The DateTimeOffset structure includes a DateTime value, together with an Offset property that defines the difference between the current DateTimeOffset instance's date and time and Coordinated Universal Time (UTC).


4 Answers

No, there's not. And it's actually DateTimeKind.Unspecified.

However, if you are concerned about supporting multiple timezones, you should consider using DateTimeOffset. It's like a regular DateTime, except that it does not represent a "perspective" of time, it represents an absolute view, in which 3PM (UTC - 3) equals 4PM (UTC - 2). DateTimeOffset contains both the DateTime and the time zone and it's supported by both EntityFramework and SQL Server.

like image 77
André Pena Avatar answered Oct 18 '22 22:10

André Pena


You can have your datacontext fix up all the relevant values as it goes. The following does so with a cache of properties for entity types, so as to avoid having to examine the type each time:

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 */
}

This can also be combined with attributes so as to allow one to set the DateTimeKind each property should have, by storing a set of rules about each property, rather than just the PropertyInfo, and looking for the attribute in GetDateProperties.

like image 39
Jon Hanna Avatar answered Oct 18 '22 22:10

Jon Hanna


My solution, using code first: Declare the DateTime properties in this way:

private DateTime _DateTimeProperty;
public DateTime DateTimeProperty
{
    get
    {
        return _DateTimeProperty;
    }
    set
    {
        _DateTimeProperty = value.ToKindUtc();
    }
}

Also can create the property as:

private DateTime? _DateTimeProperty;
public DateTime? DateTimeProperty
{
    get
    {
        return _DateTimeProperty;
    }
    set
    {
        _DateTimeProperty = value.ToKindUtc();
    }
}

ToKindUtc() is a extension to change DateTimeKind.Unspecified to DateTimeKind.Utc or call ToUniversalTime() if kind is DateTimeKind.Local Here the code for the extensions:

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;
    }
}

Remember to include in the model's file.

using TheNameSpaceWhereClassIsDeclared;

The set method of property is called when reading from datatabase with EF, or when assigned in a MVC controller's edit method.

Warning, if in web forms, if you edit dates in local timezone, you MUST convert the date to UTC before send to server.

like image 3
FRL Avatar answered Oct 18 '22 21:10

FRL


Have a look on the michael.aird answer here: https://stackoverflow.com/a/9386364/279590 It stamp the date UTC kind during loading, with an event on ObjectMaterialized.

like image 1
Olivier de Rivoyre Avatar answered Oct 18 '22 21:10

Olivier de Rivoyre