当给定的文化不知道格式时,Convert.ToDateTime()如何解析给定的字符串

我有以下代码,它的工作原理。

string testDateStr = "2009.7.28 05:23:15"; DateTime testDateObj = Convert.ToDateTime(testDateStr, CultureInfo.GetCultureInfo("fr-FR")); 

我检查了我的文化的有效格式:

 string[] validFormats = testDateObj.GetDateTimeFormats(CultureInfo.GetCultureInfo("fr-FR")); 

并且它们都不符合“2009.7.28 05:23:15”格式。 我想知道如何在不抛出格式exception的情况下对其进行解析,以及在调用Convert.ToDateTime()时执行何种隐藏解析。

更新:在LakshmiNarayanan的回答后我尝试了以下内容。

 foreach(var culture in CultureInfo.GetCultures(CultureTypes.AllCultures)) { foreach(var format in testDateObj.GetDateTimeFormats(culture)) { if (format == testDateStr) { Console.WriteLine(culture.DisplayName); } } } 

有些文化实际上包含了我的字符串所在的格式,但仍然无法解释为什么当我们要求它使用特定文化进行转换时它不会抛出exception,而该文化不知道字符串所在的格式。

Convert.ToDateTime方法在内部使用DateTime.Parse方法,该方法基于内部复杂的Lex方法。 传递的字符串有一堆规则。 它被分成令牌并分析每个令牌。 分析非常复杂,我只会展示几条规则。

如果令牌由数字组成并且长度为3~8,则此令牌将成为年份,这就是为什么可以解析01.2014.01字符串,这将产生01 Jan 2014结果。 请注意,您还可以解析01 2014 0101\n2014\n01等字符串,并给出相同的结果。 您可以使用空格或,或者分隔标记. 符号。

如果token是月份的名称,则它将是月份(表或标记是在内部DateTimeFormatInfo.CreateTokenHashTable方法中构建的)。 所以你在FebFebruary哪个位置并不重要。 您可以同样解析2014 1 Jan2014.Jan.1 2014...,Jan..,..1甚至5Jan2014字符串(最后一个不使用任何分隔符,但它检查数字结束的位置,所以它被成功分为5Jan2014代币)。

如果我们有一个模糊的字符串01/04 ,那么来自文化的信息将用于解决日/月的顺序。 订单从DateTimeFormatInfo.MonthDayPattern中提取。 例如,对于en-US它是MMMM dd ,对于en-GB它是dd MMMM 。 内部System.DateTimeParse类中有private static bool GetMonthDayOrder(string pattern, DateTimeFormatInfo dtfi, out int order)方法,用于提取顺序。 如果order变量取值为6 ,则为MM/dd ,如果取值为7 ,则为dd/MM 。 请注意,它不会尝试对01/31做一些启发式,只考虑从文化中提取的顺序。 这是测试代码:

 CultureInfo ci = CultureInfo.GetCultureInfo("en-US"); DateTimeFormatInfo dtfi = ci.DateTimeFormat; Assembly coreAssembly = Assembly.ReflectionOnlyLoad("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); Type dateTimeParseType = coreAssembly.GetType("System.DateTimeParse"); MethodInfo getMonthDayOrderMethodInfo = dateTimeParseType.GetMethod("GetMonthDayOrder", BindingFlags.Static | BindingFlags.NonPublic); object[] parameters = new object[] { dtfi.MonthDayPattern, dtfi, null }; getMonthDayOrderMethodInfo.Invoke(null, parameters); int order = (int)parameters[2]; switch (order) { case -1: Console.WriteLine("Cannot extract information"); break; case 6: Console.WriteLine("MM/dd"); break; case 7: Console.WriteLine("dd/MM"); break; } 

还有许多针对AM / PM模式的其他检查,星期几,时间后缀(例如对于韩语语言,考虑后缀,意味着小时)等。

以下代码将生成有关特定于文化的标记的信息:

 DateTimeFormatInfo dti = CultureInfo.InvariantCulture.DateTimeFormat; dynamic hashes = dti.GetType().GetMethod("CreateTokenHashTable", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(dti, null); var tokens = Enumerable.Repeat(new { str = "", type = "", value = "" }, 0).ToList(); foreach (dynamic hash in hashes) if (hash != null) { Type hashType = hash.GetType(); tokens.Add(new { str = (string)hashType.GetField("tokenString", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(hash).ToString(), type = (string)hashType.GetField("tokenType", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(hash).ToString(), value = (string)hashType.GetField("tokenValue", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(hash).ToString() }); } foreach (var token in tokens.Distinct().OrderBy(t => t.type).ThenBy(t => t.value)) Console.WriteLine("{0,10} {1} {2}", token.str, token.type, token.value); 

对于InvariantCulture ,输出为:

  AM 1027 0 PM 1284 1 Sunday DayOfWeekToken 0 Sun DayOfWeekToken 0 Monday DayOfWeekToken 1 Mon DayOfWeekToken 1 Tuesday DayOfWeekToken 2 Tue DayOfWeekToken 2 Wednesday DayOfWeekToken 3 Wed DayOfWeekToken 3 Thu DayOfWeekToken 4 Thursday DayOfWeekToken 4 Friday DayOfWeekToken 5 Fri DayOfWeekToken 5 Sat DayOfWeekToken 6 Saturday DayOfWeekToken 6 AD EraToken 1 AD EraToken 1 , IgnorableSymbol 0 . IgnorableSymbol 0 January MonthToken 1 Jan MonthToken 1 October MonthToken 10 Oct MonthToken 10 November MonthToken 11 Nov MonthToken 11 December MonthToken 12 Dec MonthToken 12 February MonthToken 2 Feb MonthToken 2 March MonthToken 3 Mar MonthToken 3 Apr MonthToken 4 April MonthToken 4 May MonthToken 5 June MonthToken 6 Jun MonthToken 6 Jul MonthToken 7 July MonthToken 7 Aug MonthToken 8 August MonthToken 8 September MonthToken 9 Sep MonthToken 9 / SEP_Date 0 - SEP_DateOrOffset 0日 SEP_DaySuff 0 일 SEP_DaySuff 0时 SEP_HourSuff 0時 SEP_HourSuff 0 T SEP_LocalTimeMark 0分 SEP_MinuteSuff 0月 SEP_MonthSuff 0 월 SEP_MonthSuff 0秒 SEP_SecondSuff 0 : SEP_Time 0 년 SEP_YearSuff 0年 SEP_YearSuff 0 GMT TimeZoneToken 0 Z TimeZoneToken 0 

对于fr-FR文化(注意July包含在列表中以及来自InvariantCulture其他令牌)

  AM 1027 0 PM 1284 1 h DateWordToken 0 dimanche DayOfWeekToken 0 Sun DayOfWeekToken 0 dim. DayOfWeekToken 0 Sunday DayOfWeekToken 0 lundi DayOfWeekToken 1 Monday DayOfWeekToken 1 lun. DayOfWeekToken 1 Mon DayOfWeekToken 1 Tuesday DayOfWeekToken 2 Tue DayOfWeekToken 2 mardi DayOfWeekToken 2 mar. DayOfWeekToken 2 mercredi DayOfWeekToken 3 Wednesday DayOfWeekToken 3 mer. DayOfWeekToken 3 Wed DayOfWeekToken 3 jeudi DayOfWeekToken 4 Thursday DayOfWeekToken 4 Thu DayOfWeekToken 4 jeu. DayOfWeekToken 4 ven. DayOfWeekToken 5 vendredi DayOfWeekToken 5 Friday DayOfWeekToken 5 Fri DayOfWeekToken 5 samedi DayOfWeekToken 6 sam. DayOfWeekToken 6 Sat DayOfWeekToken 6 Saturday DayOfWeekToken 6 ap. J.-C. EraToken 1 , IgnorableSymbol 0 . IgnorableSymbol 0 January MonthToken 1 janv. MonthToken 1 janvier MonthToken 1 Jan MonthToken 1 oct. MonthToken 10 Oct MonthToken 10 octobre MonthToken 10 October MonthToken 10 nov. MonthToken 11 Nov MonthToken 11 novembre MonthToken 11 November MonthToken 11 déc. MonthToken 12 December MonthToken 12 Dec MonthToken 12 décembre MonthToken 12 févr. MonthToken 2 février MonthToken 2 February MonthToken 2 Feb MonthToken 2 mars MonthToken 3 March MonthToken 3 Mar MonthToken 3 Apr MonthToken 4 avr. MonthToken 4 avril MonthToken 4 April MonthToken 4 mai MonthToken 5 May MonthToken 5 June MonthToken 6 juin MonthToken 6 Jun MonthToken 6 July MonthToken 7 juil. MonthToken 7 juillet MonthToken 7 Jul MonthToken 7 Aug MonthToken 8 août MonthToken 8 August MonthToken 8 sept. MonthToken 9 Sep MonthToken 9 septembre MonthToken 9 September MonthToken 9 / SEP_Date 0 - SEP_DateOrOffset 0日 SEP_DaySuff 0 일 SEP_DaySuff 0时 SEP_HourSuff 0時 SEP_HourSuff 0 T SEP_LocalTimeMark 0分 SEP_MinuteSuff 0月 SEP_MonthSuff 0 월 SEP_MonthSuff 0秒 SEP_SecondSuff 0 : SEP_Time 0 년 SEP_YearSuff 0年 SEP_YearSuff 0 GMT TimeZoneToken 0 Z TimeZoneToken 0 

Datetime.GetDateTimeFormats()方法没有以“2009.7.28 05:23:15”格式列出日期,这可能是因为默认的cultureInfo。

但是,如果您检查了GetDateTimeFormats(IFormatProvider)方法的IFormatProvider重载,您可以看到对于文化“fr-FR”,该方法能够使用“点”分隔符成功解析日期。 例如28.07.09 5:23:15

因此,如果没有提供任何特定的文化,DateTime.Parse()在所有可能的文化中运行字符串,并且只有当没有文化的字符串匹配时才返回exception。

编辑:

通过MSDN挖掘, Convert.ToDateTime(stringTime)正在使用当前区域性的DateTimeFormatInfo进行解析。

如果value不为null,则返回值是使用为当前区域性初始化的DateTimeFormatInfo对象中的格式化信息对值调用DateTime.Parse方法的结果。 value参数必须包含DateTimeFormatInfo主题中描述的格式之一的日期和时间表示。

因此,如果未设置特定区域性,则DateTimeFormatInfo对象将引用默认构造函数。 参考MSDN,

此构造函数创建一个DateTimeFormatInfo对象,该对象表示不变文化的日期和时间信息。 若要为特定区域性创建DateTimeFormatInfo对象,请为该区域性创建CultureInfo对象,并检索其CultureInfo.DateTimeFormat属性返回的DateTimeFormatInfo对象。

因此,当没有定义文化时,不变文化是默认的。 因此,Convert.ToDateTime的默认字符串方法引用DateTimeFormatInfo的默认对象,该对象引用不变文化。 这意味着Convert.ToDateTime必须解析所有文化中的所有validation。

因此,我们假设正在检查所有培养变体的validation是正确的。

希望能帮助到你。 荣誉,一个非常有趣的观察。

可能的方法

  dateString = "05/01/1996"; ConvertToDateTime(dateString); dateString = "Tue Apr 28, 2009"; ConvertToDateTime(dateString); dateString = "Wed Apr 28, 2009"; ConvertToDateTime(dateString); dateString = "06 July 2008 7:32:47 AM"; ConvertToDateTime(dateString); dateString = "17:32:47.003"; ConvertToDateTime(dateString); // Convert a string returned by DateTime.ToString("R"). dateString = "Sat, 10 May 2008 14:32:17 GMT"; ConvertToDateTime(dateString); // Convert a string returned by DateTime.ToString("o"). dateString = "2009-05-01T07:54:59.9843750-04:00"; ConvertToDateTime(dateString); int year=2009; int month=7; int day=28; int hr=5; int min=23; int s=15; DateTime testDateObj = Convert.ToDateTime(year, month, day, hr, min, s); 

或简单地说

 DateTime testDateObj = Convert.ToDateTime(2009, 7, 28, 5, 23, 15); 

尝试使用类DateTimeFormatInfo获取信息abount datetime格式我的文化。

更新: – 试试这个:

 string testDateStr = "2009.7.28 05:23:15"; string testDateObj = Convert.ToDateTime(testDateStr).Date.ToString("d"); string[] validFormats = (Convert.ToDateTime(testDateObj)).GetDateTimeFormats(); foreach(string s in validFormats) { lblresult.Text += s; }