2011-03-09 38 views
21

在我的应用程序中,我通过Web服务检索域对象。在Web服务数据中,我知道所有日期值都是UTC,但Web服务不会将其xs:dateTime值格式化为UTC日期。 (换句话说,字母Z不会附加到每个日期的末尾以指示UTC。)如何使用反射设置DateTime.Kind对象的所有日期时间属性

我无法更改此时Web服务的行为方式,但作为解决方法,我创建了一个方法,我致电紧接在来自Web服务的对象被反序列化之后。

private void ExplicitlyMarkDateTimesAsUtc<T>(T obj) where T : class 
    { 
     Type t = obj.GetType(); 

     // Loop through the properties. 
     PropertyInfo[] props = t.GetProperties(); 
     for (int i = 0; i < props.Length; i++) 
     { 
      PropertyInfo p = props[i]; 
      // If a property is DateTime or DateTime?, set DateTimeKind to DateTimeKind.Utc. 
      if (p.PropertyType == typeof(DateTime)) 
      { 
       DateTime date = (DateTime)p.GetValue(obj, null); 
       date = DateTime.SpecifyKind(date, DateTimeKind.Utc); 
       p.SetValue(obj, date, null); 
      } 
      // Same check for nullable DateTime. 
      else if (p.PropertyType == typeof(Nullable<DateTime>)) 
      { 
       DateTime? date = (DateTime?)p.GetValue(obj, null); 
       DateTime? newDate = DateTime.SpecifyKind(date.Value, DateTimeKind.Utc); 
       p.SetValue(obj, newDate, null); 
      } 
     } 
    } 

的方法,采用了一个对象,并通过其循环特性,发现要么DateTimeNullable<DateTime>,然后(应该)显式地将DateTime.Kind属性为每个属性值的集,以DateTimeKind.Utc属性。

该代码不会抛出任何异常,但obj永远不会更改其DateTime属性。在调试器p.SetValue(obj, date, null);被调用,但obj永远不会被修改。

为什么不更改应用于obj

+1

你如何确定obj没有更新? – 2011-03-09 22:34:05

+0

@Stefan我可以在调试器中看到它。其属性值不会被修改。 – RunnerRick 2011-03-09 22:35:49

+0

尝试改变值不只是种类,也许加起来看看它是否改变。改变种类不应改变价值。 – Aliostad 2011-03-09 22:38:02

回答

28

工作正常,当我尝试它。要小心,你只是在改变那种,而不是时间。如果date.HasValue为false,则不能正确处理空日期,所以不能使用date.Value。确保没有默默地捕获异常并绕过其余的属性分配。修复:

  // Same check for nullable DateTime. 
      else if (p.PropertyType == typeof(Nullable<DateTime>)) { 
       DateTime? date = (DateTime?)p.GetValue(obj, null); 
       if (date.HasValue) { 
        DateTime? newDate = DateTime.SpecifyKind(date.Value, DateTimeKind.Utc); 
        p.SetValue(obj, newDate, null); 
       } 
      } 
+0

这是一个很好的检查来添加。但是,它并没有解决我的问题。 (当我调试我的代码时,它是在第一个if区块内的 – RunnerRick 2011-03-09 23:13:22

+0

我99%确定代码是好的,如果必须的话,不要信任调试器 – 2011-03-09 23:20:43

+0

它对我来说也很好,它不会当我在UI中使用对象时,时区偏移是错误的,当我在UI中使用它们之前对对象进行预处理时,时区问题是固定的,因此我可以在对象之前对它们进行预处理用户界面,但我试图避免在所有地方重复相同的代码。 – RunnerRick 2011-03-09 23:31:03

1

请参阅http://derreckdean.wordpress.com/2013/04/24/converting-all-datetime-properties-of-an-object-graph-to-local-time-from-utc/的博客文章。我用这个代码到一个WCF响应对象图转换为具有所有本地时间:

/// <summary> 
/// Since all dates in the DB are stored as UTC, this converts dates to the local time using the Windows time zone settings. 
/// </summary> 
public static class AllDateTimesAsUTC 
{ 

    /// <summary> 
    /// Specifies that an object's dates are coming in as UTC. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="obj"></param> 
    /// <returns></returns> 
    public static T AllDatesAreUTC<T>(this T obj) 
    { 
     if (obj == null) 
     { 
      return default(T); 
     } 
     IterateDateTimeProperties(obj); 
     return obj; 
    } 

    private static void IterateDateTimeProperties(object obj) 
    { 
     if (obj == null) 
     { 
      return; 
     } 
     var properties = obj.GetType().GetProperties(); 
     //Set all DaetTimeKinds to Utc 
     foreach (var prop in properties) 
     { 
      var t = prop.PropertyType; 
      if (t == typeof(DateTime) || t == typeof(DateTime?)) 
      { 
       SpecifyUtcKind(prop, obj); 
      } 
      else if (t.IsEnumerable()) 
      { 
       var vals = prop.GetValue(obj, null); 
       if (vals != null) 
       { 
        foreach (object o in (IEnumerable)vals) 
        { 
         IterateDateTimeProperties(o); 
        } 
       } 
      } 
      else 
      { 
       var val = prop.GetValue(obj, null); 
       if (val != null) 
       { 
        IterateDateTimeProperties(val); 
       } 
      } 
     } 
     //properties.ForEach(property => SpecifyUtcKind(property, obj)); 
     return; // obj; 
    } 

    private static void SpecifyUtcKind(PropertyInfo property, object value) 
    { 
     //Get the datetime value 
     var datetime = property.GetValue(value, null); 
     DateTime output; 

     //set DateTimeKind to Utc 
     if (property.PropertyType == typeof(DateTime)) 
     { 
      output = DateTime.SpecifyKind((DateTime)datetime, DateTimeKind.Utc); 
     } 

     else if (property.PropertyType == typeof(DateTime?)) 
     { 
      var nullable = (DateTime?)datetime; 
      if (!nullable.HasValue) return; 
      output = (DateTime)DateTime.SpecifyKind(nullable.Value, DateTimeKind.Utc); 
     } 
     else 
     { 
      return; 
     } 

     Debug.WriteLine("  ***** Converted date from {0} to {1}.", datetime, output); 
     datetime = output.ToLocalTime(); 

     //And set the Utc DateTime value 
     property.SetValue(value, datetime, null); 
    } 
    internal static Type[] ConvertibleTypes = {typeof(bool), typeof(byte), typeof(char), 
typeof(DateTime), typeof(decimal), typeof(double), typeof(float), typeof(int), 
typeof(long), typeof(sbyte), typeof(short), typeof(string), typeof(uint), 
typeof(ulong), typeof(ushort)}; 

    /// <summary> 
    /// Returns true if this Type matches any of a set of Types. 
    /// </summary> 
    /// <param name="types">The Types to compare this Type to.</param> 
    public static bool In(this Type type, params Type[] types) 
    { 
     foreach (Type t in types) if (t.IsAssignableFrom(type)) return true; return false; 
    } 

    /// <summary> 
    /// Returns true if this Type is one of the types accepted by Convert.ToString() 
    /// (other than object). 
    /// </summary> 
    public static bool IsConvertible(this Type t) { return t.In(ConvertibleTypes); } 

    /// <summary> 
    /// Gets whether this type is enumerable. 
    /// </summary> 
    public static bool IsEnumerable(this Type t) 
    { 
     return typeof(IEnumerable).IsAssignableFrom(t); 
    } 

    /// <summary> 
    /// Returns true if this property's getter is public, has no arguments, and has no 
    /// generic type parameters. 
    /// </summary> 
    public static bool SimpleGetter(this PropertyInfo info) 
    { 
     MethodInfo method = info.GetGetMethod(false); 
     return method != null && method.GetParameters().Length == 0 && 
      method.GetGenericArguments().Length == 0; 
    } 

} 

(某些代码来自其他SO职位)

要使用:从任何呼叫.AllDatesAreUTC()目的。它将走图并进行本地时间转换。

void svc_ZOut_GetZOutSummaryCompleted(object sender, ZOut_GetZOutSummaryCompletedEventArgs e) 
    { 
     svc.ZOut_GetZOutSummaryCompleted -= new EventHandler<ZOut_GetZOutSummaryCompletedEventArgs>(svc_ZOut_GetZOutSummaryCompleted); 
     svc = null; 
     var whenDone = (Action<bool, ZOutResult>)e.UserState; 
     if (e.Error != null) 
     { 
      FireOnExceptionRaised(e.Error); 
      whenDone(false, null); 
     } 
     else 
     { 
      var res = e.Result.AllDatesAreUTC(); 
      FireOnSessionReceived(res.IsError, res.Session); 
      if (res.IsError == true) 
      { 
       whenDone(false, null); 
      } 
      else 
      { 
       whenDone(true, res.Result); 
      } 
     } 
    } 

通过修改SpecifyUtcKind方法,您可以更改行为以标记UTC的时间,而无需更改时间本身。

编辑:我不建议在循环引用的对象图上使用这个,根据注释中的对话。

+0

这引发了一个stackoverflow异常。可能是因为你通过对象图递归,遍历每个属性,包括字符串和基元。 – 2014-03-04 17:05:16

+0

你的对象图有多大,你有没有引用同一图中其他对象的对象?这个代码不是为了处理这个问题;我只需要它来处理不相互链接的简单图形,这肯定会导致'StackOverflowException'。 – 2014-03-04 17:55:15

+0

也许就是这样。我的图有一个属性女巫是一个分页列表,该集合中的每个项目是一个带有导航属性的EF对象。无法弄清楚我的生活如何遍历图表,忽略原语(除其他外),只是为了找到日期时间。 – 2014-03-04 17:59:59

0

我知道这事很好,但希望它可以帮助某人。我试图在原帖中做与RickRunner一样的东西,并且提出了非常相似的代码。我遇到了类似的问题,虽然对我来说obj.Kind设置正常,如果属性是常规的DateTime类型;但是对于可以为空的DateTime属性,不管我做了什么,Kind都不会被修改。最后,我发现,如果我设置的属性设置为null,然后回一个DateTime,它正确地重置种类:

// Same check for nullable DateTime. 
else if (p.PropertyType == typeof(Nullable<DateTime>)) { 
    DateTime? date = (DateTime?)p.GetValue(obj, null); 
    if (date.HasValue) { 
     DateTime? newDate = DateTime.SpecifyKind(date.Value, DateTimeKind.Utc); 
     p.SetValue(obj, null, null); 
     p.SetValue(obj, newDate, null); 
    } 
} 

这是丑陋的,我没有挖得太深尝试和数字为什么SetValue没有在第一个地方正确设置Kind。我花了相当多的时间在这方面,并且很高兴能够得到一个解决方案,不过很抱歉。

相关问题