以"Myy"格式解析DateTime

Dar*_*iak 7 c# datetime parsing

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

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

例子:

115 -> January 2015
1016 -> October 2016
Run Code Online (Sandbox Code Playgroud)

当使用DateTime.ParseExact"Myy"作为格式时,DateTime当月份没有前导零时抛出异常.

此代码抛出异常:

var date = DateTime.ParseExact("115", 
   "Myy", 
   CultureInfo.InvariantCulture); // throws FormatException
Run Code Online (Sandbox Code Playgroud)

虽然这很好:

var date = DateTime.ParseExact("1016", 
    "Myy", 
    CultureInfo.InvariantCulture); // works fine
Run Code Online (Sandbox Code Playgroud)

MSDN文档明确定义了格式说明符:

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

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

编辑

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

pok*_*oke 12

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

由于它试图读取一个月,它开始取前两位数并用它来解析月份.然后它试图解析年份,但只剩下一个数字,所以它失败了.没有引入分离字符就没有办法解决这个问题:

DateTime.ParseExact("1 15", "M yy", CultureInfo.InvariantCulture)
Run Code Online (Sandbox Code Playgroud)

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

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

研究!

由于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);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,在digitLen等于的情况下评论1.要知道,在其它的第一个数字ParseDigits过载minDigitLen,另一个是maxDigitLen.所以基本上,对于通过digitLen1,该功能也将接受2的最大长度(这使得它可以使用单M相匹配的2位个月).

现在,实际完成工作的另一个重载包含这个循环:

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

如您所见,该方法不断从字符串中获取更多数字,直到超过最大数字长度.该方法的其余部分只是错误检查和东西.

最后,让我们看一下实际的解析DoStrictParse.在那里,我们有以下循环:

// 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上,这会循环遍历格式字符串中的字符,然后尝试使用该格式从左到右匹配字符串.ParseByFormat做了额外的逻辑,捕获重复的格式(yy而不仅仅是y),并使用该信息分支成不同的格式.几个月来,这是相关部分:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • `DateTime解析器从左到右读取而不回溯. - 你有源代码吗?另外 - 如果第一个数字不是"1",那该怎么办 - 它不应该再读了. (2认同)