2011-03-09 49 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。我花了相當多的時間在這方面,並且很高興能夠得到一個解決方案,不過很抱歉。

相關問題