[英]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
包含某种与用户机相关的修改日期时间,该日期时间在GetCultureInfoHelper
( http://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.