简体   繁体   English

DateTime.TryParse->“鬼滴答声”

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

i've the following unit test, which fails on the machine of one of our developers (He get some ticks in the result variable, while the datetime variable is at zero ticks), but runs well on every other machine. 我有以下单元测试,该测试在我们的一位开发人员的计算机上失败(他在结果变量中获得了一些滴答声,而datetime变量为零滴答声),但在其他所有机器上都运行良好。

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

Here's the used extension method: 这是使用的扩展方法:

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

I think this might be related to some windows date time settings he uses. 我认为这可能与他使用的某些Windows日期时间设置有关。 In theory the ToNullableDateTime(string) should create a new culture info, which is user machine neutral. 从理论上讲, ToNullableDateTime(string)应该创建一个新的区域性信息,该信息对用户计算机是中立的。 GetCultureInfo should call new CultureInfo(name, false) . GetCultureInfo应该调用new CultureInfo(name, false) The only thing I could come up with, is that there's a cached culture info, which contains some kind of user machine related modified datetime in the s_NameCachedCultures which is checked in the GetCultureInfoHelper ( http://referencesource.microsoft.com/#mscorlib/system/globalization/cultureinfo.cs,5fe58d4ecbba7689 ). 我唯一能想到的是,有一个缓存的区域性信息,它在s_NameCachedCultures包含某种与用户机相关的修改日期时间,该日期时间在GetCultureInfoHelperhttp://referencesource.microsoft.com/#mscorlib/系统/全球化/cultureinfo.cs,5fe58d4ecbba7689 )。

I know, that the CreateSpecificCulture method can return a user modified datetime, if you call it with the same culture than the windows machine. 我知道,如果您使用与Windows计算机相同的区域性进行调用,则CreateSpecificCulture方法可以返回用户修改的日期时间。 But I always thought, the GetDateTime would return a unmodified datetime in any case. 但是我一直认为,无论如何, GetDateTime都将返回未修改的日期时间。

So there are two questions: 所以有两个问题:

  • Could it be possible, that a modified CultureInfo be stored in the internal cache? 修改后的CultureInfo可能存储在内部缓存中?
  • If so, is the only way to get a unmodified CultureInfo by manually calling new CultrueInfo("xy", false) ? 如果是这样,那么通过手动调用new CultrueInfo("xy", false)来获取未修改的CultureInfo的唯一方法是吗?

When you do 当你做

string value = dateTime.ToString(); 字符串值= dateTime.ToString();

this will use CultureInfo.CurrentCulture. 这将使用CultureInfo.CurrentCulture。 You then try and parse this string using... 然后,您尝试使用...解析此字符串。

CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.Name); CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.Name);

So you are specifically using a culture to parse the string that is different to the one you've created the string with. 因此,您正在专门使用一种文化来解析与创建该字符串所使用的字符串不同的字符串。 Of course there will be instances where this doesn't pass. 当然,在某些情况下这不会通过。

I'd suggest the issue is on most peoples machines 我建议这个问题是大多数人的机器上

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

would pass but on the machine in question it doesn't and neither do your strings. 会通过,但是在有问题的机器上不会通过,您的字符串也不会通过。

I'd suggest you probably want to use CultureInfo.InvariantCulture . 我建议您可能要使用CultureInfo.InvariantCulture So... 所以...

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

I've overseen a little detail. 我监督了一些细节。 The problem wasn't related to the GetCultureInfo returning a modified datetime, the problem already startet with the dateTime.ToString(); 该问题与GetCultureInfo返回修改后的日期时间无关,该问题已经以dateTime.ToString(); which uses the Thread.CurrentThread.CultureInfo (which equals to the windows culture, which can be modified). 它使用Thread.CurrentThread.CultureInfo (等于Windows区域性,可以修改)。 The developers machine translates the DateTime(1, 1, 1) to 01.01.01 00:00:00 . 开发人员机器将DateTime(1, 1, 1) 01.01.01 00:00:00 DateTime(1, 1, 1)01.01.01 00:00:00 On any other machine the output is 01.01.0001 00:00:00 . 在任何其他机器上,输出为01.01.0001 00:00:00 So an abbreviated version of the year is used, which seems to be interpreted as the "year 1 of the current century" in the TryParse method (quick sidenode: So it's not possible for users older than 100 years to tell their birthdate with the abbreviated year version). 因此,使用的是年份的缩写版本,在TryParse方法中,它似乎被解释为“本世纪的第一年”(快速旁节点:因此,年龄超过100岁的用户无法使用缩写名称来表示他们的生日年版本)。 It's actually an interesting behaviour.. 这实际上是一个有趣的行为。

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

leads to: 导致:

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

So, the abbreviated birth date for anyone older 86 would lead to a date in the feature.. but this moves away from the questions context .. 因此,年龄在86岁以上的人的缩写出生日期将导致该功能中的日期。但这偏离了问题的上下文。

I think there's no real solution the actual problem (beside telling developers to not use local CultureInfos outside of UI strings and never use abbreviated dates for inputs). 我认为实际问题没有真正的解决方案(除了告诉开发人员不要在UI字符串之外使用本地CultureInfos,并且永远不要使用缩写日期作为输入)。

We don't have such problems in our code itself, because we use CultureInfo.InvariantCulture for all internal stuff. 我们的代码本身没有这些问题,因为我们对所有内部内容都使用CultureInfo.InvariantCulture I just think about the unit test .. I think the test itself is correct. 我只是考虑单元测试..我认为测试本身是正确的。 It shows, that the function actually doesn't work correct with abbreviated dates. 它表明,该功能实际上对缩略日期不起作用。 I will change the behaviour of the ToNullableDateTime() to throw an exception, if a datetime string with a abbreviated year is recognized. 如果可以识别出带有缩写年份的日期时间字符串,我将更改ToNullableDateTime()的行为以引发异常。

I guess, most probably the CultureInfo is in a cache. 我想,很可能CultureInfo在缓存中。 This would be very easy to check in a unit test ( AreSame ). 这在单元测试( AreSame )中非常容易检查。

Instead of DateTime(1, 1, 1) use DateTime(1, 2, 3) and check the ghost ticks. 代替DateTime(1, 1, 1) DateTime(1, 2, 3) ,请使用DateTime(1, 2, 3)并检查幻影刻度。 Are they found in the string somewhere? 在字符串中的某处找到它们吗? Did you try the same culture (hard-coded culture name) on two machines which had different results? 您是否在两台结果不同的机器上尝试了相同的区域性(硬编码的区域性名称)?

Also check if the difference is the offset to UTC in your culture. 还要检查差异是否是您所在地区的UTC偏移量。

You probably can use TryParseExact . 您可能可以使用TryParseExact

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM