DateTime.TryParse -> "Ghost ticks"
本文关键字:quot Ghost ticks gt TryParse DateTime | 更新日期: 2023-09-27 18:03:43
我有下面的单元测试,它在我们的一个开发人员的机器上失败了(他在result变量中得到一些刻度,而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)
。我唯一能想到的是,有一个缓存的文化信息,它包含GetCultureInfoHelper
中检查的s_NameCachedCultures
中与用户机器相关的修改日期时间(http://referencesource.microsoft.com/#mscorlib/system/globalization/cultureinfo.cs,5fe58d4ecbba7689)。
我知道,CreateSpecificCulture
方法可以返回一个用户修改的datetime,如果你用与windows机器相同的区域性调用它。但我一直认为,GetDateTime
在任何情况下都会返回一个未修改的日期时间。
所以有两个问题:
- 是否有可能,修改后的
CultureInfo
存储在内部缓存中? - 如果是这样,只有通过手动调用
new CultrueInfo("xy", false)
才能获得未修改的CultureInfo
吗?
当你这么做的时候
string value = dateTime.ToString();
this将使用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返回一个修改的日期时间无关,这个问题已经开始于使用Thread.CurrentThread.CultureInfo
的dateTime.ToString();
(它等于windows区域性,可以修改)。开发人员机器将DateTime(1, 1, 1)
转换为01.01.01 00:00:00
。在任何其他机器上,输出都是01.01.0001 00:00:00
。因此使用了年份的缩写版本,在TryParse
方法中,这似乎被解释为"当前世纪的第1年"(快速补充:所以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字符串之外使用本地cultureinfo和永远不要在输入中使用缩写日期)。
我们的代码本身没有这样的问题,因为我们使用CultureInfo.InvariantCulture
来处理所有内部的东西。我只是想到了单元测试…我认为测试本身是正确的。它表明,这个函数实际上不能正确地处理缩写日期。我将更改ToNullableDateTime()
的行为,如果识别出具有缩写年份的日期时间字符串,则抛出异常。
我猜,最有可能的CultureInfo
是在缓存中。这在单元测试(AreSame
)中是非常容易检查的。
用DateTime(1, 2, 3)
代替DateTime(1, 1, 1)
并检查幽灵刻度。它们在字符串的某个地方找到了吗?您是否在两台产生不同结果的机器上尝试了相同的区域性(硬编码的区域性名称)?
还要检查差异是否为您的区域性中与UTC的偏移量。
您可以使用TryParseExact