From e6ece788b62065c4b6f5b76a5ba9b32dd732a00b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 2 Mar 2023 22:27:33 -0500 Subject: [PATCH] Trim some overheads for "r"/"o" DateTime parsing We were doing some unnecessary work on these fast paths. I also noticed and cleaned up an unnecessary delegate involved in Hebrew number parsing. --- .../src/System/Globalization/DateTimeParse.cs | 83 +++++++------------ 1 file changed, 31 insertions(+), 52 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs index b5e5d2fd31cc80..725ad9b9aba0aa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs @@ -12,10 +12,6 @@ internal static class DateTimeParse { internal const int MaxDateTimeNumberDigits = 8; - internal delegate bool MatchNumberDelegate(ref __DTString str, int digitLen, out int result); - - private static readonly MatchNumberDelegate s_hebrewNumberParser = new MatchNumberDelegate(MatchHebrewDigits); - internal static DateTime ParseExact(ReadOnlySpan s, ReadOnlySpan format, DateTimeFormatInfo dtfi, DateTimeStyles style) { DateTimeResult result = default; // The buffer to store the parsing result. @@ -3070,7 +3066,7 @@ private static bool ParseISO8601(scoped ref DateTimeRawInfo raw, ref __DTString // //////////////////////////////////////////////////////////////////////// - internal static bool MatchHebrewDigits(ref __DTString str, int digitLen, out int number) + internal static bool MatchHebrewDigits(ref __DTString str, out int number) { number = 0; @@ -3790,12 +3786,21 @@ private static string ExpandPredefinedFormat(ReadOnlySpan format, scoped r case 's': // Sortable format (in local time) case 'o': case 'O': // Round Trip Format - ConfigureFormatOS(ref dtfi, ref parseInfo); + parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); + dtfi = DateTimeFormatInfo.InvariantInfo; break; + case 'r': case 'R': // RFC 1123 Standard. (in Universal time) - ConfigureFormatR(ref dtfi, ref parseInfo, ref result); + parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); + dtfi = DateTimeFormatInfo.InvariantInfo; + + if ((result.flags & ParseFlags.CaptureOffset) != 0) + { + result.flags |= ParseFlags.Rfc1123Pattern; + } break; + case 'u': // Universal time format in sortable format. parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); dtfi = DateTimeFormatInfo.InvariantInfo; @@ -3805,6 +3810,7 @@ private static string ExpandPredefinedFormat(ReadOnlySpan format, scoped r result.flags |= ParseFlags.UtcSortPattern; } break; + case 'U': // Universal time format with culture-dependent format. parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); result.flags |= ParseFlags.TimeZoneUsed; @@ -3842,22 +3848,6 @@ private static bool ParseJapaneseEraStart(ref __DTString str, DateTimeFormatInfo return true; } - private static void ConfigureFormatR(scoped ref DateTimeFormatInfo dtfi, scoped ref ParsingInfo parseInfo, scoped ref DateTimeResult result) - { - parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); - dtfi = DateTimeFormatInfo.InvariantInfo; - if ((result.flags & ParseFlags.CaptureOffset) != 0) - { - result.flags |= ParseFlags.Rfc1123Pattern; - } - } - - private static void ConfigureFormatOS(scoped ref DateTimeFormatInfo dtfi, scoped ref ParsingInfo parseInfo) - { - parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); - dtfi = DateTimeFormatInfo.InvariantInfo; - } - // Given a specified format character, parse and update the parsing result. // private static bool ParseByFormat( @@ -3896,9 +3886,9 @@ private static bool ParseByFormat( } parseResult = ParseDigits(ref str, tokenLen, out tempYear); } - if (!parseResult && parseInfo.fCustomNumberParser) + if (!parseResult && parseInfo.fUseHebrewNumberParser) { - parseResult = parseInfo.parseNumberDelegate(ref str, tokenLen, out tempYear); + parseResult = MatchHebrewDigits(ref str, out tempYear); } if (!parseResult) { @@ -3916,8 +3906,8 @@ private static bool ParseByFormat( { if (!ParseDigits(ref str, tokenLen, out tempMonth)) { - if (!parseInfo.fCustomNumberParser || - !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempMonth)) + if (!parseInfo.fUseHebrewNumberParser || + !MatchHebrewDigits(ref str, out tempMonth)) { result.SetBadDateTimeFailure(); return false; @@ -3958,8 +3948,8 @@ private static bool ParseByFormat( if (!ParseDigits(ref str, tokenLen, out tempDay)) { - if (!parseInfo.fCustomNumberParser || - !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempDay)) + if (!parseInfo.fUseHebrewNumberParser || + !MatchHebrewDigits(ref str, out tempDay)) { result.SetBadDateTimeFailure(); return false; @@ -4454,10 +4444,7 @@ private static bool DoStrictParse( DateTimeFormatInfo dtfi, scoped ref DateTimeResult result) { - ParsingInfo parseInfo = default; - parseInfo.Init(); - - parseInfo.calendar = dtfi.Calendar; + var parseInfo = new ParsingInfo(dtfi.Calendar); parseInfo.fAllowInnerWhite = ((styles & DateTimeStyles.AllowInnerWhite) != 0); parseInfo.fAllowTrailingWhite = ((styles & DateTimeStyles.AllowTrailingWhite) != 0); @@ -4468,17 +4455,13 @@ private static bool DoStrictParse( // Fast-paths for common and important formats/configurations. if (styles == DateTimeStyles.None) { - switch (formatParamChar) + switch (formatParamChar | 0x20) { - case 'R': case 'r': - ConfigureFormatR(ref dtfi, ref parseInfo, ref result); - return ParseFormatR(s, ref parseInfo, ref result); + return TryParseFormatR(s, ref result); - case 'O': case 'o': - ConfigureFormatOS(ref dtfi, ref parseInfo); - return ParseFormatO(s, ref result); + return TryParseFormatO(s, ref result); } } @@ -4494,11 +4477,7 @@ private static bool DoStrictParse( result.calendar = parseInfo.calendar; - if (parseInfo.calendar.ID == CalendarId.HEBREW) - { - parseInfo.parseNumberDelegate = s_hebrewNumberParser; - parseInfo.fCustomNumberParser = true; - } + parseInfo.fUseHebrewNumberParser = parseInfo.calendar.ID == CalendarId.HEBREW; // Reset these values to negative one so that we could throw exception // if we have parsed every item twice. @@ -4658,7 +4637,7 @@ private static bool DoStrictParse( return DetermineTimeZoneAdjustments(ref result, styles, bTimeOnly); } - private static bool ParseFormatR(ReadOnlySpan source, scoped ref ParsingInfo parseInfo, scoped ref DateTimeResult result) + private static bool TryParseFormatR(ReadOnlySpan source, scoped ref DateTimeResult result) { // Example: // Tue, 03 Jan 2017 08:08:05 GMT @@ -4836,8 +4815,8 @@ private static bool ParseFormatR(ReadOnlySpan source, scoped ref ParsingIn return false; } - // Validate that the parsed date is valid according to the calendar. - if (!parseInfo.calendar.TryToDateTime(year, month, day, hour, minute, second, 0, 0, out result.parsedDate)) + // Validate that the parsed date is valid. + if (!DateTime.TryCreate(year, month, day, hour, minute, second, 0, out result.parsedDate)) { result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar)); return false; @@ -4853,7 +4832,7 @@ private static bool ParseFormatR(ReadOnlySpan source, scoped ref ParsingIn return true; } - private static bool ParseFormatO(ReadOnlySpan source, scoped ref DateTimeResult result) + private static bool TryParseFormatO(ReadOnlySpan source, scoped ref DateTimeResult result) { // Examples: // 2017-06-12T05:30:45.7680000 (interpreted as local time wrt to current time zone) @@ -6036,11 +6015,11 @@ internal struct ParsingInfo internal bool fUseTwoDigitYear; internal bool fAllowInnerWhite; internal bool fAllowTrailingWhite; - internal bool fCustomNumberParser; - internal DateTimeParse.MatchNumberDelegate parseNumberDelegate; + internal bool fUseHebrewNumberParser; - internal void Init() + public ParsingInfo(Calendar calendar) { + this.calendar = calendar; dayOfWeek = -1; timeMark = DateTimeParse.TM.NotSet; }