在" Myy"中解析DateTime格式

时间:2015-05-21 20:01:03

标签: c# parsing datetime

我需要在" Myy"中解析DateTime。格式,所以:

  • 第一个数字是一个月没有前导零(1到12)和
  • 第二个数字是两位数的年份。

示例:

115 -> January 2015
1016 -> October 2016

DateTime.ParseExact用于" Myy"作为一种格式,DateTime在月份没有前导零时抛出异常。

此代码抛出异常:

var date = DateTime.ParseExact("115", 
   "Myy", 
   CultureInfo.InvariantCulture); // throws FormatException

虽然这很好用:

var date = DateTime.ParseExact("1016", 
    "Myy", 
    CultureInfo.InvariantCulture); // works fine

MSDN Documentation明确定义了格式说明符:

  
      
  • " M" - 月份,从1到12。
  •   
  • " MM" - 月份,从01到12。
  •   
  • " YY" - 年份,从00到99。
  •   

是否有任何格式可以解决上述情况,即" Myy"日期时间格式,其中月份没有前导零?

修改

准确地说:问题是关于在ParseExact中使用格式而不是如何使用字符串操作来解析它本身。

3 个答案:

答案 0 :(得分:12)

这是因为DateTime解析器从左向右读取而没有回溯。

由于它试图读取一个月,它开始取前两位数并用它来解析月份。然后它试图解析年份,但只剩下一个数字,所以它失败了。如果不引入分离字符,根本无法解决这个问题:

DateTime.ParseExact("1 15", "M yy", CultureInfo.InvariantCulture)

如果你不能这样做,请先阅读右边的内容,然后单独拆分年份(使用字符串操作)。或者只是在开头添加零并将其解析为MMyy

string s = "115";
if (s.Length < 4)
    s = "0" + s;
Console.WriteLine(DateTime.ParseExact(s, "MMyy", CultureInfo.InvariantCulture));

研究!

因为ispiro要求提供资源:解析由DateTimeParse类型完成。与我们相关的是ParseDigits方法:

internal static bool ParseDigits(ref __DTString str, int digitLen, out int result) {
    if (digitLen == 1) {
        // 1 really means 1 or 2 for this call
        return ParseDigits(ref str, 1, 2, out result);
    }
    else {
        return ParseDigits(ref str, digitLen, digitLen, out result);
    }
}

请注意,在digitLen等于1的情况下,请在那里发表评论。知道其他ParseDigits重载中的第一个数字是minDigitLen而另一个是maxDigitLen。所以基本上,对于digitLen传递1,该函数也将接受最大长度2(这使得可以使用单个M来匹配2位数月份)

现在,实际完成工作的other overload包含此循环:

while (tokenLength < maxDigitLen) {
    if (!str.GetNextDigit()) {
        str.Index--;
        break;
    }
    result = result * 10 + str.GetDigit();
    tokenLength++;
}

正如您所看到的,该方法不断从字符串中取出更多数字,直到它超过最大数字长度。该方法的其余部分只是错误检查和填充。

最后,让我们看一下DoStrictParse中的实际解析。在那里,我们有following loop

// Scan every character in format and match the pattern in str.
while (format.GetNext()) {
    // We trim inner spaces here, so that we will not eat trailing spaces when
    // AllowTrailingWhite is not used.
    if (parseInfo.fAllowInnerWhite) {
        str.SkipWhiteSpaces();
    }
    if (!ParseByFormat(ref str, ref format, ref parseInfo, dtfi, ref result)) {
       return (false);
    }
}

基本上,这会循环遍历格式字符串中的字符,然后尝试使用该格式匹配字符串从左到右ParseByFormat执行额外的逻辑来捕获重复的格式(例如yy而不仅仅是y)并使用该信息分支到不同的格式。对于我们这几个月,这是the relevant part

if (tokenLen <= 2) {
    if (!ParseDigits(ref str, tokenLen, out tempMonth)) {
        if (!parseInfo.fCustomNumberParser ||
            !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempMonth)) {
            result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
            return (false);
        }
    }
}

所以在这里我们关闭ParseDigits的圆圈,其中1的标记长度为M。但正如我们上面所看到的,如果可以,它仍将匹配两位数;并且没有验证它匹配的两位数字是否对一个月有意义。所以130也不匹配2030年1月。它将在第13个月匹配,并在之后失败。

答案 1 :(得分:1)

来自MSDN

  

如果format是自定义格式模式,不包含日期或   时间分隔符(例如&#34; yyyyMMdd HHmm&#34;),使用不变的文化   提供者参数和每种自定义格式的最宽格式   符。例如,如果要以格式指定小时数   模式,指定更宽的形式,&#34; HH&#34;而不是更窄的形式,   &#34; H&#34;

换句话说,它很可能无法以您想要的方式解决。

答案 2 :(得分:0)

太简单了?

      string date = "115";
      if (date.Count()==3)
      {
         date = "0" + date;
      }