簡體   English   中英

DateTime.TryParse->“鬼滴答聲”

[英]DateTime.TryParse -> “Ghost ticks”

我有以下單元測試,該測試在我們的一位開發人員的計算機上失敗(他在結果變量中獲得了一些滴答聲,而datetime變量為零滴答聲),但在其他所有機器上都運行良好。

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

我認為這可能與他使用的某些Windows日期時間設置有關。 從理論上講, ToNullableDateTime(string)應該創建一個新的區域性信息,該信息對用戶計算機是中立的。 GetCultureInfo應該調用new CultureInfo(name, false) 我唯一能想到的是,有一個緩存的區域性信息,它在s_NameCachedCultures包含某種與用戶機相關的修改日期時間,該日期時間在GetCultureInfoHelperhttp://referencesource.microsoft.com/#mscorlib/系統/全球化/cultureinfo.cs,5fe58d4ecbba7689 )。

我知道,如果您使用與Windows計算機相同的區域性進行調用,則CreateSpecificCulture方法可以返回用戶修改的日期時間。 但是我一直認為,無論如何, GetDateTime都將返回未修改的日期時間。

所以有兩個問題:

  • 修改后的CultureInfo可能存儲在內部緩存中?
  • 如果是這樣,那么通過手動調用new CultrueInfo("xy", false)來獲取未修改的CultureInfo的唯一方法是嗎?

當你做

字符串值= 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;
    }

我監督了一些細節。 該問題與GetCultureInfo返回修改后的日期時間無關,該問題已經以dateTime.ToString(); 它使用Thread.CurrentThread.CultureInfo (等於Windows區域性,可以修改)。 開發人員機器將DateTime(1, 1, 1) 01.01.01 00:00:00 DateTime(1, 1, 1)01.01.01 00:00:00 在任何其他機器上,輸出為01.01.0001 00:00:00 因此,使用的是年份的縮寫版本,在TryParse方法中,它似乎被解釋為“本世紀的第一年”(快速旁節點:因此,年齡超過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歲以上的人的縮寫出生日期將導致該功能中的日期。但這偏離了問題的上下文。

我認為實際問題沒有真正的解決方案(除了告訴開發人員不要在UI字符串之外使用本地CultureInfos,並且永遠不要使用縮寫日期作為輸入)。

我們的代碼本身沒有這些問題,因為我們對所有內部內容都使用CultureInfo.InvariantCulture 我只是考慮單元測試..我認為測試本身是正確的。 它表明,該功能實際上對縮略日期不起作用。 如果可以識別出帶有縮寫年份的日期時間字符串,我將更改ToNullableDateTime()的行為以引發異常。

我想,很可能CultureInfo在緩存中。 這在單元測試( AreSame )中非常容易檢查。

代替DateTime(1, 1, 1) DateTime(1, 2, 3) ,請使用DateTime(1, 2, 3)並檢查幻影刻度。 在字符串中的某處找到它們嗎? 您是否在兩台結果不同的機器上嘗試了相同的區域性(硬編碼的區域性名稱)?

還要檢查差異是否是您所在地區的UTC偏移量。

您可能可以使用TryParseExact

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM