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

时间:2014-04-02 11:25:12

标签: c# .net datetime-format

我有以下代码,它有效。

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”格式。我想知道如何在不抛出格式异常的情况下对其进行解析,以及在调用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);
        }
    }
}

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

5 个答案:

答案 0 :(得分:3)

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.12014...,Jan..,..1甚至5Jan2014字符串(最后一个不使用任何分隔符,但会检查数字结束的位置,因此它已成功分为5Jan2014令牌。

如果我们有不明确的字符串01/04,那么来自文化的信息将用于解决日/月的顺序。订单摘自DateTimeFormatInfo.MonthDayPattern。例如,对于en-US,它是MMMM dd,对于en-GB,它是dd MMMM。内部private static bool GetMonthDayOrder(string pattern, DateTimeFormatInfo dtfi, out int order)类中有System.DateTimeParse方法,用于提取订单。如果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
      A.D. 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

答案 1 :(得分:1)

Datetime.GetDateTimeFormats()方法未按格式列出日期" 2009.7.28 05:23:15"这可能是因为默认的cultureInfo。

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

因此,对于其工作原理的逻辑假设是,如果没有提供任何特定的文化,DateTime.Parse()会在所有可能的文化中运行字符串,并且只有在没有任何文化时才会返回异常。 ;字符串匹配。

编辑:

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

  

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

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

  

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

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

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

希望它有所帮助。荣誉,一个非常有趣的观察。

答案 2 :(得分:0)

可能的方式

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

答案 3 :(得分:0)

尝试使用类DateTimeFormatInfo为您的文化提供信息abount datetime格式。

答案 4 :(得分:0)

更新: - 试试这个:

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