Convert.ToDateTime在下午日期/时间值上引发FormatException异常

11

我们有一个应用程序解析以下格式的日期/时间值:

2009-10-10 09:19:12.124
2009-10-10 12:13:14.852
2009-10-10 13:00:00
2009-10-10 15:23:32.022

有一个特定的服务器突然(今天)无法解析13:00:00或之后的任何时间。这个客户端有五台服务器,只有一台出现了问题。我们有其他几十个客户端,总共数百台服务器没有问题。

System.FormatException: String was not recognized as a valid DateTime.
at System.DateTimeParse.Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
at System.DateTime.Parse(String s, IFormatProvider provider)
at System.Convert.ToDateTime(String value, IFormatProvider provider)
at System.String.System.IConvertible.ToDateTime(IFormatProvider provider)
at System.Convert.ToDateTime(Object value)

我运行了一个测试,使用DateTime.Parse(s, CultureInfo.CurrentCulture)和DateTime.Parse(s, CultureInfo.InvariantCulture),只有在使用CurrentCulture时才出现问题。然而,CurrentCulture与所有其他服务器一样都是"en-US",并且在区域设置或语言设置中没有任何不同之处。

有人以前见过这种情况吗?建议我可以研究什么?

编辑:非常感谢目前为止的回答。然而,我正在寻找关于哪些配置可能导致此问题突然更改行为并停止工作的建议,尽管它已经工作了多年并在数百个其他服务器上工作。我已经为下一个版本进行了更改,但我正在寻找一种配置更改来在当前安装的中间修复此问题。

8个回答

7
如果你确定日期时间的格式,建议使用DateTime.ParseExact而不是简单地使用DateTime.Parse。(同时像其他人一样,指定固定区域文化信息,以消除更多的差异。)这会给你更多的控制:
using System;
using System.Globalization;

class Test
{
    static void Main()
    {
        Parse("2009-10-10 09:19:12.124");
        Parse("2009-10-10 12:13:14.852");
        Parse("2009-10-10 13:00:00");
        Parse("2009-10-10 15:23:32.022");
    }

    static readonly string ShortFormat = "yyyy-MM-dd HH:mm:ss";
    static readonly string LongFormat = "yyyy-MM-dd HH:mm:ss.fff";

    static readonly string[] Formats = { ShortFormat, LongFormat };

    static void Parse(string text)
    {
        // Adjust styles as per requirements
        DateTime result = DateTime.ParseExact(text, Formats, 
                                              CultureInfo.InvariantCulture,
                                              DateTimeStyles.AssumeUniversal);
        Console.WriteLine(result);
    }
}

3
我们有一个运行在Windows 2003服务器上的C# .NET 2.0 Web服务。该服务已经运行了两年以上。最近我们在该应用程序中遇到了错误。“String was not recognized as a valid DateTime”是错误信息。另一个Web服务返回一个日期,该函数应该返回这样一个值:
"5/10/2010 12:00:00 AM"
当发生错误时,函数返回:
"5/10/2010 12:00:00 "
错误返回结果末尾带有空格,且没有AM。
这个问题在接下来的几个星期里出现过几次并消失了。检查Windows日志后,我们发现错误的开始和结束时间与应用池回收的时间(默认情况下每29小时)相符(误差为一到两分钟)。该问题只出现在生产Web服务器上,而不是开发或测试服务器上。我们尝试更换服务器,一个月内没有任何错误。现在错误又开始了。我们手动重置了应用池,问题立即消失。我们不希望把这看作是一个可接受的解决方案,因为应用池会定期回收。我们不知道是否需要应用池回收,但据我们所知,定期回收可以提高应用程序性能。
Web应用程序已经使用ParseExact(),但格式字符串看起来像这样:
"yyyy-MM-ddHH:mm:ss.0Z"
而不是Jon Skeet在此线程中建议的
"yyyy-MM-dd HH:mm:ss"
我不知道这会有多大的差异。我们还检查了文化设置。
显然,问题始于应用池回收,但我不知道在回收期间发生了什么。大多数情况下,回收没有负面影响,当出现错误时,下一个回收总是能够停止错误。

我们还没有解决这个问题。自从我写下这句话以来,这个问题已经再次出现了几次,重新启动暂时可以解决它。我们正在考虑改变循环的时间,确保每周在同一时间进行并且我们做好准备,但是我们仍然不知道真正的问题所在。我们还与Microsoft合作过,他们似乎比我们更无法解决这个问题。 - Brian Begley
这方面有任何更新吗?我自己也遇到了同样的问题,并在发生时对进程进行了内存转储。我在那里看到的是,将日期时间存储为通用对象,然后将其作为日期时间取回时会丢失区域设置信息(当然非常随机...似乎无法再现) - mpeterson

3

我们也遇到了同样的问题。只需回收应用程序池即可。


是的,但是...为什么呢?我们偶尔也会遇到相同的问题。我们的Web服务将一个C#的DateTime对象转换为一个字符串,并传递给一个期望一个smalldatetime类型的SQL Server数据库过程。由于某些原因,有时候这会将时间向前或向后移动12小时,因此发送今天的日期会被识别为中午十二点(12:00:00 PM),而不是半夜十二点(12:00:00 AM)。 - Paul Williams

2
解决方案非常简单,使用 CultureInfo.InvariantCulture。对于示例数据来说,这是唯一明智的做法。
我知道这并没有解释区别,但你的软件真的不应该依赖于“默认”文化(除了可能在用户界面中)。

1
即使使用不变文化,我也不会依赖于默认格式——明确指定格式,这样就清楚了你的期望。 - Jon Skeet

1

我刚刚找到了一些关于这个问题的原因的信息。我们的C#客户端将业务日期(仅日期)作为字符串值发送到Web服务。Web服务将此字符串作为SqlParameter传递给SqlCommand,再传递给接受业务日期datetime参数的存储过程。

一个客户的系统突然出现了问题。我使用SQL Server Profiler观察RPC调用,并注意到日期参数突然有了时间组件。这应该是一个仅包含日期的值--即10/27/2012 12:00:00 AM。

我修改了存储过程以添加WAITFOR DELAY '00:01:00'。然后,在Web服务调用此存储过程时,我获取了内存转储。

检查内存转储,我发现客户端已经传递了正确的日期字符串“2012-10-25 00:00:00”在参数数据集中。但是,当Web服务调用存储过程时,它却搞砸了这些日期的转换。我在转储中找到了SqlCommand,并检查了与存储过程日期参数对应的SqlParameter。该值为“10/25/2012 12:00:00 PM”。

查看所有的CultureInfo对象,我发现“en-US”的本地设置1033有一个长时间字符串“h:mm:ss tt”。AMDesignator是“AM”,但PMDesignator是一个空字符串!我通过运行以下命令验证了这是C#中错误日期的原因:

// clear the PM designator
System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.PMDesignator = ""
// this will yield "12/25/2012 12:00:00 " (note the extra space at the end)
Convert.ToDateTime("10/25/2012").ToString()

结果是“2012年10月25日12:00:00”,这与Brian Begley在他的回答中所说的相对应。使用不变格式可以得到非常相似的结果:
// clear the PM designator
System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.PMDesignator = ""
// this will yield "10/25/2012 12:00:00"
Convert.ToDateTime("10/25/2012").ToString(System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat)
// restore the PM designator to "PM"
System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.PMDesignator = "PM"
// this will yield "10/25/2012 00:00:00"
Convert.ToDateTime("10/25/2012").ToString(System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat)

我还没有确定是什么导致了CultureInfo的损坏。

请注意微软已经意识到这个问题。链接的文章说它只适用于Windows Server 2003,并且有一个热补丁可用。


0

听起来好像是文化对象没有理解军事时间表示法。老实说,我不确定该怎么做,但这可能会给你一个起点。


0

CultureInfo.DateTimeFormat 属性的值是什么?根据 MSDN 的说法

用户可以通过控制面板中的区域和语言选项部分选择覆盖与 Windows 当前文化相关联的某些值。例如,用户可能选择以不同的格式显示日期或使用与默认文化不同的货币。
如果 UseUserOverride 为 true 并且指定的文化匹配 Windows 的当前文化,则 CultureInfo 使用这些覆盖,包括由 DateTimeFormat 属性返回的 DateTimeFormatInfo 实例的属性和 NumberFormat 属性返回的 NumberFormatInfo 实例的属性的用户设置。如果用户设置与与 CultureInfo 相关联的文化不兼容,例如,如果所选日历不是 OptionalCalendars 中的一个,则方法的结果和属性的值是未定义的。
DateTimeFormat 属性和 NumberFormat 属性的值直到应用程序访问该属性时才计算。如果用户可以在应用程序运行时更改当前文化为新文化,然后应用程序访问 DateTimeFormat 或 NumberFormat 属性,则应用程序检索新文化的默认值,而不是原始文化的覆盖。为了保留原始当前文化的覆盖,应用程序应在更改当前文化之前访问 DateTimeFormat 和 NumberFormat 属性。

可能是某个用户设置覆盖了这个设置吗?


0

这只是一个猜测,但也许以下事件序列可能导致了问题:

  1. 管理员进入所有服务器的控制面板国际设置,并将时间格式从“HH:mm:ss”更改为“hh:mm:ss tt”。
  2. 管理员仅在其中一台服务器上重新启动应用程序池(或IIS服务或机器)。

现在,所有在被重新启动的服务器上的新ASP.NET工作线程都将看到更改后的时间格式,并且它们的DateTime.Parse方法将失败。然而,如果其他服务器仍在使用原始工作线程,则它们尚未检测到当前区域设置DateTimeFormatInfo的更改。

这是一个非常不太可能的情况,但你又遇到了一个非常不太可能的情况。您可以尝试在所有服务器上重新启动相关应用程序池,看看是否有任何变化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接