繁体   English   中英

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

[英]How Convert.ToDateTime() parses a given string when the given culture does not know the 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);
        }
    }
}

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

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 内部System.DateTimeParse类中有private static bool GetMonthDayOrder(string pattern, DateTimeFormatInfo dtfi, out int order)方法,用于提取顺序。 如果order变量取值为6 ,则为MM/dd ,如果取值为7 ,则为dd/MM 请注意,它不会尝试对01/31进行一些启发式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

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

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

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

编辑:

通过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必须解析所有文化中的所有验证。

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

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

可能的方法

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM