2015-07-20 50 views
1

我有以下單元測試,它在我們的開發人員(他在結果變量中得到一些滴答聲,而日期時間變量在零滴答)的機器上失敗,但在其他機器上運行良好。DateTime.TryParse - >「Ghost ticks」

[TestMethod] 
    public void DateTimeStringDateTimeMinCurrentCultureToNullableDateTimeSuccessTest() 
    { 
     var dateTime = new DateTime(1, 1, 1); 
     string value = dateTime.ToString(); 
     var result = value.ToNullableDateTime(); 
     Assert.AreEqual(dateTime, result); 
    } 

這裏所使用的擴展方法:

/// <summary> 
    /// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null. 
    /// Uses the current culture. 
    /// </summary> 
    public static DateTime? ToNullableDateTime(this string s) 
    { 
     //Don't use CultureInfo.CurrentCulture to override user changes of the cultureinfo. 
     return s.ToNullableDateTime(CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.Name)); 
    } 

    /// <summary> 
    /// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null. 
    /// </summary> 
    public static DateTime? ToNullableDateTime(this string s, CultureInfo cultureInfo) 
    { 
     if (String.IsNullOrEmpty(s)) return null; 
     DateTime i; 
     if (DateTime.TryParse(s, cultureInfo, DateTimeStyles.None, out i)) return i; 
     return null; 
    } 

我想,這可能與他使用了一些窗口日期時間設置。理論上,ToNullableDateTime(string)應該創建一個新的文化信息,這是用戶機器中立。 GetCultureInfo應調用new CultureInfo(name, false)。我唯一可以想到的是,有一個緩存的文化信息,其中包含s_NameCachedCultures中的某種與用戶計算機相關的修改日期時間,該日期時間在GetCultureInfoHelperhttp://referencesource.microsoft.com/#mscorlib/system/globalization/cultureinfo.cs,5fe58d4ecbba7689)中檢查。

我知道,CreateSpecificCulture方法可以返回用戶修改的日期時間,如果你用與Windows機器相同的文化來調用它。但我一直認爲,在任何情況下,GetDateTime都會返回一個未修改的日期時間。

因此,有兩個問題:

  • 難道是可能的,即修改CultureInfo被存儲在內部緩存?
  • 如果是這樣,是通過手動呼叫new CultrueInfo("xy", false)獲得未修改的CultureInfo的唯一方法?
+0

爲什麼選擇在擴展方法的短重載中使用'CultureInfo.GetCultureInfo'?該[文檔](https://msdn.microsoft.com/en-us/library/yck8b540.aspx)說,喜歡它比普通實例構造函數('new CultureInfo(...)')更快的原因是因爲的緩存。 –

+0

瞭解's'和'CultureInfo.CurrentCulture.Name'在測試失敗的機器上以及測試通過的機器上會有所幫助。 –

回答

1

我監督了一點細節。該問題與GetCultureInfo返回修改的日期時間無關,問題已在dateTime.ToString();開始,該問題使用Thread.CurrentThread.CultureInfo(相當於windows文化,可以修改)。開發者機器將DateTime(1, 1, 1)轉換爲01.01.01 00:00:00。在任何其他機器上,輸出是01.01.0001 00:00:00。因此,使用了年份的縮寫版本,這似乎被解釋爲TryParse方法中的「當前世紀的第一年」(快速sidenode:因此,超過100年的用戶不可能使用縮寫年版)。 它實際上是一個有趣的現象..

 for (int i = 0; i < 100; i++) 
     { 
      var year = 1900 + i; 
      DateTime date = new DateTime(year, 1, 1); 
      var parsedDate = DateTime.ParseExact(date.ToString("yy"), "yy", CultureInfo.InvariantCulture); 
      Console.WriteLine("{0}: {1}", year, parsedDate.ToString("yyyy")); 
     } 

導致:

[...] 
1928: 2028 
1929: 2029 
1930: 1930 
1931: 1931 
[...] 

所以,縮寫出生日期的人年長86將導致該功能的日期..但這個從移開問題的上下文..

我認爲沒有真正的解決方案的實際問題(旁邊告訴開發人員不使用本地CultureInfos以外的用戶界面字符串,從來沒有使用縮短的日期輸入)。

我們的代碼本身沒有這樣的問題,因爲我們對所有內部東西都使用CultureInfo.InvariantCulture。我只考慮單元測試..我認爲測試本身是正確的。它表明,該函數實際上不適用於縮寫日期。如果識別出具有縮寫年份的日期時間字符串,我將更改ToNullableDateTime()的行爲以引發異常。

0

我想,最有可能的是CultureInfo是在緩存中。這將很容易檢查單元測試(AreSame)。

而不是DateTime(1, 1, 1)使用DateTime(1, 2, 3)並檢查ghost ticks。他們是否在某處找到了字符串?你在兩臺有不同結果的機器上嘗試過相同的文化(硬編碼文化名稱)嗎?

另外檢查差異是否是您的文化中UTC的偏移量。您可能會使用TryParseExact

3

當你

字符串值=則DateTime.ToString();

這將使用CultureInfo.CurrentCulture。然後嘗試使用...解析此字符串...

CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.Name);

因此,您正在使用文化來解析與您創建字符串的字符串不同的字符串。當然,會有這種情況沒有通過。

我建議這個問題是大多數人的機器

Assert.AreEqual(CultureInfo.CurrentCulture, CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.Name)); 

會通過,但有問題的機器上它沒有也不做你的字符串。我建議你可能想使用CultureInfo.InvariantCulture。所以...

[TestMethod] 
    public void DateTimeStringDateTimeMinCurrentCultureToNullableDateTimeSuccessTest() 
    { 
     DateTime dateTime = new DateTime(1, 1, 1); 
     string value = dateTime.ToStringInvariant(); 
     var result = value.ToNullableDateTime(); 
     Assert.AreEqual(dateTime, result); 
    } 


    public static string ToStringInvariant(this DateTime? date) 
    { 
     if (date.HasValue) 
      return date.Value.ToStringInvariant(); 

     return null; 
    } 

    public static string ToStringInvariant(this DateTime date) 
    { 
     return date.ToString(CultureInfo.InvariantCulture); 

    } 
    /// <summary> 
    /// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null. 
    /// Uses the current culture. 
    /// </summary> 
    public static DateTime? ToNullableDateTime(this string s) 
    { 
     //Don't use CultureInfo.CurrentCulture to override user changes of the cultureinfo. 
     return s.ToNullableDateTime(CultureInfo.InvariantCulture); 
    } 

    /// <summary> 
    /// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null. 
    /// </summary> 
    public static DateTime? ToNullableDateTime(this string s, CultureInfo cultureInfo) 
    { 
     if (String.IsNullOrEmpty(s)) return null; 
     DateTime i; 
     if (DateTime.TryParse(s, cultureInfo, DateTimeStyles.None, out i)) return i; 
     return null; 
    }