Collapse AsSpan().Slice(...) into .AsSpan(...) (dotnet/corefx#27867)
[platform/upstream/coreclr.git] / src / mscorlib / shared / System / Globalization / DateTimeParse.cs
index 2825f01..970d176 100644 (file)
@@ -4,6 +4,7 @@
 
 using System.Diagnostics;
 using System.Globalization;
+using System.Runtime.CompilerServices;
 using System.Text;
 
 namespace System
@@ -16,10 +17,10 @@ namespace System
 
         internal static MatchNumberDelegate m_hebrewNumberParser = new MatchNumberDelegate(DateTimeParse.MatchHebrewDigits);
 
-        internal static DateTime ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style)
+        internal static DateTime ParseExact(ReadOnlySpan<char> s, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, DateTimeStyles style)
         {
             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
-            result.Init();
+            result.Init(s);
             if (TryParseExact(s, format, dtfi, style, ref result))
             {
                 return result.parsedDate;
@@ -30,11 +31,11 @@ namespace System
             }
         }
 
-        internal static DateTime ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset)
+        internal static DateTime ParseExact(ReadOnlySpan<char> s, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset)
         {
             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
             offset = TimeSpan.Zero;
-            result.Init();
+            result.Init(s);
             result.flags |= ParseFlags.CaptureOffset;
             if (TryParseExact(s, format, dtfi, style, ref result))
             {
@@ -47,11 +48,11 @@ namespace System
             }
         }
 
-        internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result)
+        internal static bool TryParseExact(ReadOnlySpan<char> s, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result)
         {
             result = DateTime.MinValue;
             DateTimeResult resultData = new DateTimeResult();       // The buffer to store the parsing result.
-            resultData.Init();
+            resultData.Init(s);
             if (TryParseExact(s, format, dtfi, style, ref resultData))
             {
                 result = resultData.parsedDate;
@@ -60,12 +61,12 @@ namespace System
             return false;
         }
 
-        internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset)
+        internal static bool TryParseExact(ReadOnlySpan<char> s, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset)
         {
             result = DateTime.MinValue;
             offset = TimeSpan.Zero;
             DateTimeResult resultData = new DateTimeResult();       // The buffer to store the parsing result.
-            resultData.Init();
+            resultData.Init(s);
             resultData.flags |= ParseFlags.CaptureOffset;
             if (TryParseExact(s, format, dtfi, style, ref resultData))
             {
@@ -76,27 +77,17 @@ namespace System
             return false;
         }
 
-        internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result)
+        internal static bool TryParseExact(ReadOnlySpan<char> s, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result)
         {
-            if (s == null)
-            {
-                result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s));
-                return false;
-            }
-            if (format == null)
-            {
-                result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(format));
-                return false;
-            }
             if (s.Length == 0)
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDateTime));
                 return false;
             }
 
             if (format.Length == 0)
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
+                result.SetBadFormatSpecifierFailure();
                 return false;
             }
 
@@ -105,11 +96,11 @@ namespace System
             return DoStrictParse(s, format, style, dtfi, ref result);
         }
 
-        internal static DateTime ParseExactMultiple(String s, String[] formats,
+        internal static DateTime ParseExactMultiple(ReadOnlySpan<char> s, String[] formats,
                                                 DateTimeFormatInfo dtfi, DateTimeStyles style)
         {
             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
-            result.Init();
+            result.Init(s);
             if (TryParseExactMultiple(s, formats, dtfi, style, ref result))
             {
                 return result.parsedDate;
@@ -121,12 +112,12 @@ namespace System
         }
 
 
-        internal static DateTime ParseExactMultiple(String s, String[] formats,
+        internal static DateTime ParseExactMultiple(ReadOnlySpan<char> s, String[] formats,
                                                 DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset)
         {
             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
             offset = TimeSpan.Zero;
-            result.Init();
+            result.Init(s);
             result.flags |= ParseFlags.CaptureOffset;
             if (TryParseExactMultiple(s, formats, dtfi, style, ref result))
             {
@@ -139,13 +130,13 @@ namespace System
             }
         }
 
-        internal static bool TryParseExactMultiple(String s, String[] formats,
+        internal static bool TryParseExactMultiple(ReadOnlySpan<char> s, String[] formats,
                                                    DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset)
         {
             result = DateTime.MinValue;
             offset = TimeSpan.Zero;
             DateTimeResult resultData = new DateTimeResult();       // The buffer to store the parsing result.
-            resultData.Init();
+            resultData.Init(s);
             resultData.flags |= ParseFlags.CaptureOffset;
             if (TryParseExactMultiple(s, formats, dtfi, style, ref resultData))
             {
@@ -157,12 +148,12 @@ namespace System
         }
 
 
-        internal static bool TryParseExactMultiple(String s, String[] formats,
+        internal static bool TryParseExactMultiple(ReadOnlySpan<char> s, String[] formats,
                                                    DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result)
         {
             result = DateTime.MinValue;
             DateTimeResult resultData = new DateTimeResult();       // The buffer to store the parsing result.
-            resultData.Init();
+            resultData.Init(s);
             if (TryParseExactMultiple(s, formats, dtfi, style, ref resultData))
             {
                 result = resultData.parsedDate;
@@ -171,14 +162,9 @@ namespace System
             return false;
         }
 
-        internal static bool TryParseExactMultiple(String s, String[] formats,
+        internal static bool TryParseExactMultiple(ReadOnlySpan<char> s, String[] formats,
                                                 DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result)
         {
-            if (s == null)
-            {
-                result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s));
-                return false;
-            }
             if (formats == null)
             {
                 result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(formats));
@@ -187,13 +173,13 @@ namespace System
 
             if (s.Length == 0)
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDateTime));
                 return false;
             }
 
             if (formats.Length == 0)
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
+                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_NoFormatSpecifier));
                 return false;
             }
 
@@ -207,13 +193,13 @@ namespace System
             {
                 if (formats[i] == null || formats[i].Length == 0)
                 {
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
+                    result.SetBadFormatSpecifierFailure();
                     return false;
                 }
                 // Create a new result each time to ensure the runs are independent. Carry through
                 // flags from the caller and return the result.
                 DateTimeResult innerResult = new DateTimeResult();       // The buffer to store the parsing result.
-                innerResult.Init();
+                innerResult.Init(s);
                 innerResult.flags = result.flags;
                 if (TryParseExact(s, formats[i], dtfi, style, ref innerResult))
                 {
@@ -222,7 +208,7 @@ namespace System
                     return (true);
                 }
             }
-            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+            result.SetBadDateTimeFailure();
             return (false);
         }
 
@@ -345,7 +331,7 @@ namespace System
         //
         ////////////////////////////////////////////////////////////////////////////
 
-        //          End       NumEnd      NumAmPm     NumSpace    NumDaySep   NumTimesep  MonthEnd    MonthSpace  MonthDSep   NumDateSuff NumTimeSuff     DayOfWeek     YearSpace   YearDateSep YearEnd     TimeZone   Era         UTCTimeMark   
+        //          End       NumEnd      NumAmPm     NumSpace    NumDaySep   NumTimesep  MonthEnd    MonthSpace  MonthDSep   NumDateSuff NumTimeSuff     DayOfWeek     YearSpace   YearDateSep YearEnd     TimeZone   Era         UTCTimeMark
         private static DS[][] dateParsingStates = {
 // DS.BEGIN                                                                             // DS.BEGIN
 new DS[] { DS.BEGIN, DS.ERROR,   DS.TX_N,    DS.N,       DS.D_Nd,    DS.T_Nt,    DS.ERROR,   DS.D_M,     DS.D_M,     DS.D_S,     DS.T_S,         DS.BEGIN,     DS.D_Y,     DS.D_Y,     DS.ERROR,   DS.BEGIN,  DS.BEGIN,    DS.ERROR},
@@ -417,14 +403,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         //
         private static bool MatchWord(ref __DTString str, String target)
         {
-            int length = target.Length;
-            if (length > (str.Value.Length - str.Index))
+            if (target.Length > (str.Value.Length - str.Index))
             {
                 return false;
             }
 
-            if (str.CompareInfo.Compare(str.Value, str.Index, length,
-                                        target, 0, length, CompareOptions.IgnoreCase) != 0)
+            if (str.CompareInfo.Compare(str.Value.Slice(str.Index, target.Length), target, CompareOptions.IgnoreCase) != 0)
             {
                 return (false);
             }
@@ -440,7 +424,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 }
             }
             str.Index = nextCharIndex;
-            if (str.Index < str.len)
+            if (str.Index < str.Length)
             {
                 str.m_current = str.Value[str.Index];
             }
@@ -467,11 +451,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             return (false);
         }
 
-        internal static bool IsDigit(char ch)
-        {
-            return (ch >= '0' && ch <= '9');
-        }
-
+        internal static bool IsDigit(char ch) => (uint)(ch - '0') <= 9;
 
         /*=================================ParseFraction==========================
         **Action: Starting at the str.Index, which should be a decimal symbol.
@@ -588,12 +568,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         // This is the helper function to handle timezone in string in the format like +/-0800
         private static bool HandleTimeZone(ref __DTString str, ref DateTimeResult result)
         {
-            if ((str.Index < str.len - 1))
+            if ((str.Index < str.Length - 1))
             {
                 char nextCh = str.Value[str.Index];
                 // Skip whitespace, but don't update the index unless we find a time zone marker
                 int whitespaceCount = 0;
-                while (Char.IsWhiteSpace(nextCh) && str.Index + whitespaceCount < str.len - 1)
+                while (Char.IsWhiteSpace(nextCh) && str.Index + whitespaceCount < str.Length - 1)
                 {
                     whitespaceCount++;
                     nextCh = str.Value[str.Index + whitespaceCount];
@@ -604,13 +584,13 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     if ((result.flags & ParseFlags.TimeZoneUsed) != 0)
                     {
                         // Should not have two timezone offsets.
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                     result.flags |= ParseFlags.TimeZoneUsed;
                     if (!ParseTimeZone(ref str, ref result.timeZoneOffset))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                 }
@@ -635,17 +615,9 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             str.GetRegularToken(out tokenType, out tokenValue, dtfi);
 
 #if _LOGGING
-            // Builds with _LOGGING defined (x86dbg, amd64chk, etc) support tracing
-            // Set the following internal-only/unsupported environment variables to enable DateTime tracing to the console:
-            //
-            // COMPlus_LogEnable=1
-            // COMPlus_LogToConsole=1
-            // COMPlus_LogLevel=9
-            // COMPlus_ManagedLogFacility=0x00001000
             if (_tracingEnabled)
             {
-                BCLDebug.Trace("DATETIME", "[DATETIME] Lex({0})\tpos:{1}({2}), {3}, DS.{4}", Hex(str.Value),
-                               str.Index, Hex(str.m_current), tokenType, dps);
+                Trace($"Lex({Hex(str.Value)})\tpos:{str.Index}({Hex(str.m_current)}), {tokenType}, DS.{dps}");
             }
 #endif // _LOGGING
 
@@ -656,7 +628,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 case TokenType.YearNumberToken:
                     if (raw.numCount == 3 || tokenValue == -1)
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         LexTraceExit("0010", dps);
                         return false;
                     }
@@ -673,7 +645,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     //  "11:22:33.1234" or "11:22:33-08".
                     if (dps == DS.T_NNt)
                     {
-                        if ((str.Index < str.len - 1))
+                        if ((str.Index < str.Length - 1))
                         {
                             char nextCh = str.Value[str.Index];
                             if (nextCh == '.')
@@ -688,7 +660,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                     if (dps == DS.T_NNt || dps == DS.T_Nt)
                     {
-                        if ((str.Index < str.len - 1))
+                        if ((str.Index < str.Length - 1))
                         {
                             if (false == HandleTimeZone(ref str, ref result))
                             {
@@ -723,7 +695,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                                     }
                                     else
                                     {
-                                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                        result.SetBadDateTimeFailure();
                                         LexTraceExit("0030 (TM.AM/TM.PM Happened more than 1x)", dps);
                                     }
                                     break;
@@ -736,12 +708,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                                 case TokenType.SEP_Time:
                                     if (!raw.hasSameDateAndTimeSeparators)
                                     {
-                                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                        result.SetBadDateTimeFailure();
                                         LexTraceExit("0040 (Invalid separator after number)", dps);
                                         return false;
                                     }
 
-                                    // we have the date and time separators are same and getting a year number, then change the token to YearDateSep as 
+                                    // we have the date and time separators are same and getting a year number, then change the token to YearDateSep as
                                     // we are sure we are not parsing time.
                                     dtok.dtt = DTT.YearDateSep;
                                     break;
@@ -774,7 +746,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                                     break;
                                 default:
                                     // Invalid separator after number number.
-                                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                    result.SetBadDateTimeFailure();
                                     LexTraceExit("0040 (Invalid separator after number)", dps);
                                     return false;
                             }
@@ -784,7 +756,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             LexTraceExit("0050 (success)", dps);
                             return true;
                         }
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         LexTraceExit("0060", dps);
                         return false;
                     }
@@ -807,7 +779,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                                 // Fix AM/PM parsing case, e.g. "1/10 5 AM"
                                 if (dps == DS.D_NN)
                                 {
-                                    if (!ProcessTerminaltState(DS.DX_NN, ref result, ref styles, ref raw, dtfi))
+                                    if (!ProcessTerminalState(DS.DX_NN, ref str, ref result, ref styles, ref raw, dtfi))
                                     {
                                         return false;
                                     }
@@ -817,7 +789,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             }
                             else
                             {
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 break;
                             }
                             if (dps == DS.T_NNt || dps == DS.T_Nt)
@@ -870,9 +842,9 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             {
                                 dtok.num = dtfi.Calendar.ToFourDigitYear(tokenValue);
                             }
-                            catch (ArgumentOutOfRangeException e)
+                            catch (ArgumentOutOfRangeException)
                             {
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), e);
+                                result.SetBadDateTimeFailure();
                                 LexTraceExit("0075 (Calendar.ToFourDigitYear failed)", dps);
                                 return false;
                             }
@@ -896,7 +868,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             break;
                         default:
                             // Invalid separator after number number.
-                            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                            result.SetBadDateTimeFailure();
                             LexTraceExit("0080", dps);
                             return false;
                     }
@@ -934,7 +906,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                                     goto default;
                                 default:
                                     // Invalid separator after number number.
-                                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                    result.SetBadDateTimeFailure();
                                     LexTraceExit("0090", dps);
                                     return false;
                             }
@@ -942,7 +914,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         else
                         {
                             // Invalid separator after number number.
-                            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                            result.SetBadDateTimeFailure();
                             LexTraceExit("0100", dps);
                             return false;
                         }
@@ -983,7 +955,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                                 break;
                             default:
                                 // Invalid separator after number number.
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 LexTraceExit("0110", dps);
                                 return false;
                         }
@@ -1000,7 +972,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                     else
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         LexTraceExit("0120 (DayOfWeek seen more than 1x)", dps);
                         return false;
                     }
@@ -1025,12 +997,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             case TokenType.SEP_Time:
                                 if (!raw.hasSameDateAndTimeSeparators)
                                 {
-                                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                    result.SetBadDateTimeFailure();
                                     LexTraceExit("0130 (Invalid separator after month name)", dps);
                                     return false;
                                 }
 
-                                // we have the date and time separators are same and getting a Month name, then change the token to MonthDatesep as 
+                                // we have the date and time separators are same and getting a Month name, then change the token to MonthDatesep as
                                 // we are sure we are not parsing time.
                                 dtok.dtt = DTT.MonthDatesep;
                                 break;
@@ -1051,7 +1023,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                                 break;
                             default:
                                 //Invalid separator after month name
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 LexTraceExit("0130 (Invalid separator after month name)", dps);
                                 return false;
                         }
@@ -1059,7 +1031,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                     else
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         LexTraceExit("0140 (MonthToken seen more than 1x)", dps);
                         return false;
                     }
@@ -1072,7 +1044,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                     else
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         LexTraceExit("0150 (EraToken seen when result.era already set)", dps);
                         return false;
                     }
@@ -1088,7 +1060,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                     else
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         LexTraceExit("0160 (JapaneseEraToken seen when result.era already set)", dps);
                         return false;
                     }
@@ -1103,7 +1075,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                     else
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         LexTraceExit("0170 (TEraToken seen when result.era already set)", dps);
                         return false;
                     }
@@ -1117,7 +1089,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     if ((result.flags & ParseFlags.TimeZoneUsed) != 0)
                     {
                         // Should not have two timezone offsets.
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         LexTraceExit("0180 (seen GMT or Z more than 1x)", dps);
                         return false;
                     }
@@ -1141,7 +1113,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                     else
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         LexTraceExit("0190 (AM/PM timeMark already set)", dps);
                         return false;
                     }
@@ -1149,7 +1121,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 case TokenType.UnknownToken:
                     if (Char.IsLetter(str.m_current))
                     {
-                        result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_UnknowDateTimeWord), str.Index);
+                        result.SetFailure(ParseFailureKind.FormatWithOriginalDateTimeAndParameter, nameof(SR.Format_UnknownDateTimeWord), str.Index);
                         LexTraceExit("0200", dps);
                         return (false);
                     }
@@ -1179,7 +1151,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         return true;
                     }
 
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                    result.SetBadDateTimeFailure();
                     LexTraceExit("0240", dps);
                     return false;
             }
@@ -1196,7 +1168,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             {
                 bool foundStart = false;
                 bool foundEnd = false;
-                for (int i = 0; i < str.len; i++)
+                for (int i = 0; i < str.Length; i++)
                 {
                     ch = str.Value[i];
                     if (ch == '#')
@@ -1228,7 +1200,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                     else if ((!Char.IsWhiteSpace(ch)))
                     {
-                        // Anthyhing other than whitespace outside hashes is invalid
+                        // Anything other than whitespace outside hashes is invalid
                         if (!foundStart || foundEnd)
                         {
                             return false;
@@ -1240,13 +1212,13 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     // The has was un-paired
                     return false;
                 }
-                // Valid Hash usage: eat the hash and continue. 
+                // Valid Hash usage: eat the hash and continue.
                 str.GetNext();
                 return true;
             }
             else if (ch == '\0')
             {
-                for (int i = str.Index; i < str.len; i++)
+                for (int i = str.Index; i < str.Length; i++)
                 {
                     if (str.Value[i] != '\0')
                     {
@@ -1255,7 +1227,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                 }
                 // Move to the end of the string
-                str.Index = str.len;
+                str.Index = str.Length;
                 return true;
             }
             return false;
@@ -1572,7 +1544,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if ((result.flags & ParseFlags.HaveDate) != 0)
             {
                 // Multiple dates in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -1605,7 +1577,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     return true;
                 }
             }
-            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+            result.SetBadDateTimeFailure();
             return false;
         }
 
@@ -1615,7 +1587,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if ((result.flags & ParseFlags.HaveDate) != 0)
             {
                 // Multiple dates in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -1663,7 +1635,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     return true;
                 }
             }
-            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+            result.SetBadDateTimeFailure();
             return false;
         }
 
@@ -1672,7 +1644,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if ((result.flags & ParseFlags.HaveDate) != 0)
             {
                 // Multiple dates in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -1707,7 +1679,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     int year;
                     if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                     return true;
@@ -1717,7 +1689,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             GetDefaultYear(ref result, ref styles);
             if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0)))
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
             return true;
@@ -1746,7 +1718,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     return true;
                 }
             }
-            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+            result.SetBadDateTimeFailure();
             return false;
         }
 
@@ -1755,7 +1727,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if ((result.flags & ParseFlags.HaveDate) != 0)
             {
                 // Multiple dates in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -1790,7 +1762,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     int year;
                     if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                     return true;
@@ -1800,7 +1772,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             GetDefaultYear(ref result, ref styles);
             if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0)))
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
             return true;
@@ -1811,7 +1783,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if ((result.flags & ParseFlags.HaveDate) != 0)
             {
                 // Multiple dates in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -1872,7 +1844,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 }
             }
 
-            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+            result.SetBadDateTimeFailure();
             return false;
         }
 
@@ -1881,7 +1853,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if ((result.flags & ParseFlags.HaveDate) != 0)
             {
                 // Multiple dates in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -1907,7 +1879,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     return true; // Year + MD
                 }
             }
-            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+            result.SetBadDateTimeFailure();
             return false;
         }
 
@@ -1916,7 +1888,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if ((result.flags & ParseFlags.HaveDate) != 0)
             {
                 // Multiple dates in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -1946,17 +1918,17 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     return true; // DM + Year
                 }
             }
-            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+            result.SetBadDateTimeFailure();
             return false;
         }
 
 
-        private static Boolean GetDayOfYMN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+        private static Boolean GetDayOfYMN(ref DateTimeResult result, ref DateTimeRawInfo raw)
         {
             if ((result.flags & ParseFlags.HaveDate) != 0)
             {
                 // Multiple dates in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -1965,16 +1937,16 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 result.flags |= ParseFlags.HaveDate;
                 return true;
             }
-            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+            result.SetBadDateTimeFailure();
             return false;
         }
 
-        private static Boolean GetDayOfYN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+        private static Boolean GetDayOfYN(ref DateTimeResult result, ref DateTimeRawInfo raw)
         {
             if ((result.flags & ParseFlags.HaveDate) != 0)
             {
                 // Multiple dates in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -1983,16 +1955,16 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 result.flags |= ParseFlags.HaveDate;
                 return true;
             }
-            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+            result.SetBadDateTimeFailure();
             return false;
         }
 
-        private static Boolean GetDayOfYM(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+        private static Boolean GetDayOfYM(ref DateTimeResult result, ref DateTimeRawInfo raw)
         {
             if ((result.flags & ParseFlags.HaveDate) != 0)
             {
                 // Multiple dates in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -2001,7 +1973,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 result.flags |= ParseFlags.HaveDate;
                 return true;
             }
-            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+            result.SetBadDateTimeFailure();
             return false;
         }
 
@@ -2059,12 +2031,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             return true;
         }
 
-        private static Boolean GetTimeOfN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw)
+        private static Boolean GetTimeOfN(ref DateTimeResult result, ref DateTimeRawInfo raw)
         {
             if ((result.flags & ParseFlags.HaveTime) != 0)
             {
                 // Multiple times in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
             //
@@ -2072,7 +2044,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             //
             if (raw.timeMark == TM.NotSet)
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
             result.Hour = raw.GetNumber(0);
@@ -2080,13 +2052,13 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             return true;
         }
 
-        private static Boolean GetTimeOfNN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw)
+        private static Boolean GetTimeOfNN(ref DateTimeResult result, ref DateTimeRawInfo raw)
         {
             Debug.Assert(raw.numCount >= 2, "raw.numCount >= 2");
             if ((result.flags & ParseFlags.HaveTime) != 0)
             {
                 // Multiple times in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -2096,12 +2068,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             return true;
         }
 
-        private static Boolean GetTimeOfNNN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw)
+        private static Boolean GetTimeOfNNN(ref DateTimeResult result, ref DateTimeRawInfo raw)
         {
             if ((result.flags & ParseFlags.HaveTime) != 0)
             {
                 // Multiple times in the input string
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
             Debug.Assert(raw.numCount >= 3, "raw.numCount >= 3");
@@ -2119,7 +2091,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         {
             if (raw.numCount != 1 || result.Day != -1)
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
             result.Day = raw.GetNumber(0);
@@ -2131,19 +2103,19 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if (result.Month == -1)
             {
                 //Should have a month suffix
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
             if (result.Year != -1)
             {
-                // Aleady has a year suffix
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                // Already has a year suffix
+                result.SetBadDateTimeFailure();
                 return false;
             }
             if (!TryAdjustYear(ref result, raw.GetNumber(0), out result.Year))
             {
                 // the year value is out of range
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
             result.Day = 1;
@@ -2193,7 +2165,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                 }
             }
-            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+            result.SetBadDateTimeFailure();
             return false;
         }
 
@@ -2264,7 +2236,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         //
         ////////////////////////////////////////////////////////////////////////
 
-        internal static Boolean ProcessHebrewTerminalState(DS dps, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+        internal static Boolean ProcessHebrewTerminalState(DS dps, ref __DTString str, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
         {
             // The following are accepted terminal state for Hebrew date.
             switch (dps)
@@ -2274,7 +2246,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     raw.year = raw.GetNumber(1);
                     if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true))
                     {
-                        result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
+                        result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar));
                         return false;
                     }
                     if (!GetDayOfMNN(ref result, ref raw, dtfi))
@@ -2286,10 +2258,10 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     // Deal with the default long/short date format when the year number is NOT ambigous (i.e. year >= 100).
                     if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true))
                     {
-                        result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
+                        result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar));
                         return false;
                     }
-                    if (!GetDayOfYMN(ref result, ref raw, dtfi))
+                    if (!GetDayOfYMN(ref result, ref raw))
                     {
                         return false;
                     }
@@ -2307,7 +2279,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                     if (!dtfi.YearMonthAdjustment(ref result.Year, ref raw.month, true))
                     {
-                        result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
+                        result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar));
                         return false;
                     }
                     break;
@@ -2317,7 +2289,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     GetDefaultYear(ref result, ref styles);
                     if (!dtfi.YearMonthAdjustment(ref result.Year, ref raw.month, true))
                     {
-                        result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
+                        result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar));
                         return false;
                     }
                     if (!GetHebrewDayOfNM(ref result, ref raw, dtfi))
@@ -2329,35 +2301,35 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     // Deal with Year/Month pattern.
                     if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true))
                     {
-                        result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
+                        result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar));
                         return false;
                     }
-                    if (!GetDayOfYM(ref result, ref raw, dtfi))
+                    if (!GetDayOfYM(ref result, ref raw))
                     {
                         return false;
                     }
                     break;
                 case DS.TX_N:
                     // Deal hour + AM/PM
-                    if (!GetTimeOfN(dtfi, ref result, ref raw))
+                    if (!GetTimeOfN(ref result, ref raw))
                     {
                         return false;
                     }
                     break;
                 case DS.TX_NN:
-                    if (!GetTimeOfNN(dtfi, ref result, ref raw))
+                    if (!GetTimeOfNN(ref result, ref raw))
                     {
                         return false;
                     }
                     break;
                 case DS.TX_NNN:
-                    if (!GetTimeOfNNN(dtfi, ref result, ref raw))
+                    if (!GetTimeOfNNN(ref result, ref raw))
                     {
                         return false;
                     }
                     break;
                 default:
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                    result.SetBadDateTimeFailure();
                     return false;
             }
             if (dps > DS.ERROR)
@@ -2374,7 +2346,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         // A terminal state has been reached, call the appropriate function to fill in the parsing result.
         // Return true if the state is a terminal state.
         //
-        internal static Boolean ProcessTerminaltState(DS dps, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+        internal static Boolean ProcessTerminalState(DS dps, ref __DTString str, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
         {
             bool passed = true;
             switch (dps)
@@ -2405,22 +2377,22 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     passed = GetDayOfNNY(ref result, ref raw, dtfi);
                     break;
                 case DS.DX_YMN:
-                    passed = GetDayOfYMN(ref result, ref raw, dtfi);
+                    passed = GetDayOfYMN(ref result, ref raw);
                     break;
                 case DS.DX_YN:
-                    passed = GetDayOfYN(ref result, ref raw, dtfi);
+                    passed = GetDayOfYN(ref result, ref raw);
                     break;
                 case DS.DX_YM:
-                    passed = GetDayOfYM(ref result, ref raw, dtfi);
+                    passed = GetDayOfYM(ref result, ref raw);
                     break;
                 case DS.TX_N:
-                    passed = GetTimeOfN(dtfi, ref result, ref raw);
+                    passed = GetTimeOfN(ref result, ref raw);
                     break;
                 case DS.TX_NN:
-                    passed = GetTimeOfNN(dtfi, ref result, ref raw);
+                    passed = GetTimeOfNN(ref result, ref raw);
                     break;
                 case DS.TX_NNN:
-                    passed = GetTimeOfNNN(dtfi, ref result, ref raw);
+                    passed = GetTimeOfNNN(ref result, ref raw);
                     break;
                 case DS.TX_TS:
                     // The result has got the correct value. Nothing to do.
@@ -2453,10 +2425,10 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             return true;
         }
 
-        internal static DateTime Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
+        internal static DateTime Parse(ReadOnlySpan<char> s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
         {
             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
-            result.Init();
+            result.Init(s);
             if (TryParse(s, dtfi, styles, ref result))
             {
                 return result.parsedDate;
@@ -2467,10 +2439,10 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             }
         }
 
-        internal static DateTime Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out TimeSpan offset)
+        internal static DateTime Parse(ReadOnlySpan<char> s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out TimeSpan offset)
         {
             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
-            result.Init();
+            result.Init(s);
             result.flags |= ParseFlags.CaptureOffset;
             if (TryParse(s, dtfi, styles, ref result))
             {
@@ -2484,11 +2456,11 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         }
 
 
-        internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result)
+        internal static bool TryParse(ReadOnlySpan<char> s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result)
         {
             result = DateTime.MinValue;
             DateTimeResult resultData = new DateTimeResult();       // The buffer to store the parsing result.
-            resultData.Init();
+            resultData.Init(s);
             if (TryParse(s, dtfi, styles, ref resultData))
             {
                 result = resultData.parsedDate;
@@ -2497,12 +2469,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             return false;
         }
 
-        internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result, out TimeSpan offset)
+        internal static bool TryParse(ReadOnlySpan<char> s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result, out TimeSpan offset)
         {
             result = DateTime.MinValue;
             offset = TimeSpan.Zero;
             DateTimeResult parseResult = new DateTimeResult();       // The buffer to store the parsing result.
-            parseResult.Init();
+            parseResult.Init(s);
             parseResult.flags |= ParseFlags.CaptureOffset;
             if (TryParse(s, dtfi, styles, ref parseResult))
             {
@@ -2517,16 +2489,11 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         //
         // This is the real method to do the parsing work.
         //
-        internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, ref DateTimeResult result)
+        internal static bool TryParse(ReadOnlySpan<char> s, DateTimeFormatInfo dtfi, DateTimeStyles styles, ref DateTimeResult result)
         {
-            if (s == null)
-            {
-                result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s));
-                return false;
-            }
             if (s.Length == 0)
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDateTime));
                 return false;
             }
 
@@ -2595,7 +2562,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     {
                         if (!ProcessDateTimeSuffix(ref result, ref raw, ref dtok))
                         {
-                            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                            result.SetBadDateTimeFailure();
                             TPTraceExit("0010", dps);
                             return false;
                         }
@@ -2614,7 +2581,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         }
                         else
                         {
-                            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                            result.SetBadDateTimeFailure();
                             TPTraceExit("0030", dps);
                             return false;
                         }
@@ -2624,7 +2591,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     {
                         if (dtok.dtt == DTT.YearEnd || dtok.dtt == DTT.YearSpace || dtok.dtt == DTT.YearDateSep)
                         {
-                            // When time and date separators are same and we are hitting a year number while the first parsed part of the string was recognized 
+                            // When time and date separators are same and we are hitting a year number while the first parsed part of the string was recognized
                             // as part of time (and not a date) DS.T_Nt, DS.T_NNt then change the state to be a date so we try to parse it as a date instead
                             if (dps == DS.T_Nt)
                             {
@@ -2641,7 +2608,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         {
                             switch (dtok.dtt)
                             {
-                                // we have the case of Serbia have dates in forms 'd.M.yyyy.' so we can expect '.' after the date parts. 
+                                // we have the case of Serbia have dates in forms 'd.M.yyyy.' so we can expect '.' after the date parts.
                                 // changing the token to end with space instead of Date Separator will avoid failing the parsing.
 
                                 case DTT.YearDateSep: dtok.dtt = atEnd ? DTT.YearEnd : DTT.YearSpace; break;
@@ -2659,7 +2626,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
 
                     if (dps == DS.ERROR)
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         TPTraceExit("0040 (invalid state transition)", dps);
                         return false;
                     }
@@ -2667,7 +2634,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     {
                         if ((dtfi.FormatFlags & DateTimeFormatFlags.UseHebrewRule) != 0)
                         {
-                            if (!ProcessHebrewTerminalState(dps, ref result, ref styles, ref raw, dtfi))
+                            if (!ProcessHebrewTerminalState(dps, ref str, ref result, ref styles, ref raw, dtfi))
                             {
                                 TPTraceExit("0050 (ProcessHebrewTerminalState)", dps);
                                 return false;
@@ -2675,7 +2642,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         }
                         else
                         {
-                            if (!ProcessTerminaltState(dps, ref result, ref styles, ref raw, dtfi))
+                            if (!ProcessTerminalState(dps, ref str, ref result, ref styles, ref raw, dtfi))
                             {
                                 TPTraceExit("0060 (ProcessTerminaltState)", dps);
                                 return false;
@@ -2695,7 +2662,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
 
             if (!reachTerminalState)
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 TPTraceExit("0070 (did not reach terminal state)", dps);
                 return false;
             }
@@ -2703,7 +2670,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             AdjustTimeMark(dtfi, ref raw);
             if (!AdjustHour(ref result.Hour, raw.timeMark))
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 TPTraceExit("0080 (AdjustHour)", dps);
                 return false;
             }
@@ -2724,7 +2691,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if (!result.calendar.TryToDateTime(result.Year, result.Month, result.Day,
                     result.Hour, result.Minute, result.Second, 0, result.era, out time))
             {
-                result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
+                result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar));
                 TPTraceExit("0100 (result.calendar.TryToDateTime)", dps);
                 return false;
             }
@@ -2735,7 +2702,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
 
             //
             // We have to check day of week before we adjust to the time zone.
-            // Otherwise, the value of day of week may change after adjustting to the time zone.
+            // Otherwise, the value of day of week may change after adjusting to the time zone.
             //
             if (raw.dayOfWeek != -1)
             {
@@ -2744,7 +2711,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 //
                 if (raw.dayOfWeek != (int)result.calendar.GetDayOfWeek(time))
                 {
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDayOfWeek), null);
+                    result.SetFailure(ParseFailureKind.FormatWithOriginalDateTime, nameof(SR.Format_BadDayOfWeek));
                     TPTraceExit("0110 (dayOfWeek check)", dps);
                     return false;
                 }
@@ -2752,7 +2719,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
 
             result.parsedDate = time;
 
-            if (!DetermineTimeZoneAdjustments(ref result, styles, bTimeOnly))
+            if (!DetermineTimeZoneAdjustments(ref str, ref result, styles, bTimeOnly))
             {
                 TPTraceExit("0120 (DetermineTimeZoneAdjustments)", dps);
                 return false;
@@ -2763,13 +2730,13 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
 
 
         // Handles time zone adjustments and sets DateTimeKind values as required by the styles
-        private static Boolean DetermineTimeZoneAdjustments(ref DateTimeResult result, DateTimeStyles styles, Boolean bTimeOnly)
+        private static Boolean DetermineTimeZoneAdjustments(ref __DTString str, ref DateTimeResult result, DateTimeStyles styles, Boolean bTimeOnly)
         {
             if ((result.flags & ParseFlags.CaptureOffset) != 0)
             {
-                // This is a DateTimeOffset parse, so the offset will actually be captured directly, and 
+                // This is a DateTimeOffset parse, so the offset will actually be captured directly, and
                 // no adjustment is required in most cases
-                return DateTimeOffsetTimeZonePostProcessing(ref result, styles);
+                return DateTimeOffsetTimeZonePostProcessing(ref str, ref result, styles);
             }
             else
             {
@@ -2778,7 +2745,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 // the DateTime offset must be within +- 14:00 hours.
                 if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset)
                 {
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_OffsetOutOfRange), null);
+                    result.SetFailure(ParseFailureKind.FormatWithOriginalDateTime, nameof(SR.Format_OffsetOutOfRange));
                     return false;
                 }
             }
@@ -2841,9 +2808,9 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         }
 
         // Apply validation and adjustments specific to DateTimeOffset
-        private static Boolean DateTimeOffsetTimeZonePostProcessing(ref DateTimeResult result, DateTimeStyles styles)
+        private static Boolean DateTimeOffsetTimeZonePostProcessing(ref __DTString str, ref DateTimeResult result, DateTimeStyles styles)
         {
-            // For DateTimeOffset, default to the Utc or Local offset when an offset was not specified by 
+            // For DateTimeOffset, default to the Utc or Local offset when an offset was not specified by
             // the input string.
             if ((result.flags & ParseFlags.TimeZoneUsed) == 0)
             {
@@ -2866,17 +2833,17 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             Int64 utcTicks = result.parsedDate.Ticks - offsetTicks;
 
             // For DateTimeOffset, both the parsed time and the corresponding UTC value must be within the boundaries
-            // of a DateTime instance.            
+            // of a DateTime instance.
             if (utcTicks < DateTime.MinTicks || utcTicks > DateTime.MaxTicks)
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_UTCOutOfRange), null);
+                result.SetFailure(ParseFailureKind.FormatWithOriginalDateTime, nameof(SR.Format_UTCOutOfRange));
                 return false;
             }
 
             // the offset must be within +- 14:00 hours.
             if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset)
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_OffsetOutOfRange), null);
+                result.SetFailure(ParseFailureKind.FormatWithOriginalDateTime, nameof(SR.Format_OffsetOutOfRange));
                 return false;
             }
 
@@ -2893,7 +2860,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 }
 
                 // The constructor should always succeed because of the range check earlier in the function
-                // Althought it is UTC, internally DateTimeOffset does not use this flag
+                // Although it is UTC, internally DateTimeOffset does not use this flag
                 result.parsedDate = new DateTime(utcTicks, DateTimeKind.Utc);
                 result.timeZoneOffset = TimeSpan.Zero;
             }
@@ -2919,7 +2886,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
 
             if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks)
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_DateOutOfRange), null);
+                result.SetFailure(ParseFailureKind.FormatWithOriginalDateTime, nameof(SR.Format_DateOutOfRange));
                 return false;
             }
             result.parsedDate = new DateTime(resultTicks, DateTimeKind.Utc);
@@ -2976,7 +2943,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks)
             {
                 result.parsedDate = DateTime.MinValue;
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_DateOutOfRange), null);
+                result.SetFailure(ParseFailureKind.FormatWithOriginalDateTime, nameof(SR.Format_DateOutOfRange));
                 return false;
             }
             result.parsedDate = new DateTime(resultTicks, DateTimeKind.Local, isAmbiguousLocalDst);
@@ -3000,19 +2967,19 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             str.SkipWhiteSpaces();
             if (!ParseDigits(ref str, 2, out hour))
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
             str.SkipWhiteSpaces();
             if (!str.Match(':'))
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
             str.SkipWhiteSpaces();
             if (!ParseDigits(ref str, 2, out minute))
             {
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
             str.SkipWhiteSpaces();
@@ -3021,14 +2988,14 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 str.SkipWhiteSpaces();
                 if (!ParseDigits(ref str, 2, out second))
                 {
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                    result.SetBadDateTimeFailure();
                     return false;
                 }
                 if (str.Match('.'))
                 {
                     if (!ParseFraction(ref str, out partSecond))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                     str.Index--;
@@ -3043,7 +3010,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     result.flags |= ParseFlags.TimeZoneUsed;
                     if (!ParseTimeZone(ref str, ref result.timeZoneOffset))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                 }
@@ -3062,7 +3029,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 {
                     if (!VerifyValidPunctuation(ref str))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                     str.SkipWhiteSpaces();
@@ -3071,14 +3038,14 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 {
                     if (!VerifyValidPunctuation(ref str))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                 }
                 if (str.GetNext())
                 {
                     // If this is true, there were non-white space characters remaining in the DateTime
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                    result.SetBadDateTimeFailure();
                     return false;
                 }
             }
@@ -3088,13 +3055,13 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if (!calendar.TryToDateTime(raw.year, raw.GetNumber(0), raw.GetNumber(1),
                     hour, minute, second, 0, result.era, out time))
             {
-                result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
+                result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar));
                 return false;
             }
 
             time = time.AddTicks((long)Math.Round(partSecond * Calendar.TicksPerSecond));
             result.parsedDate = time;
-            if (!DetermineTimeZoneAdjustments(ref result, styles, false))
+            if (!DetermineTimeZoneAdjustments(ref str, ref result, styles, false))
             {
                 return false;
             }
@@ -3166,7 +3133,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             Debug.Assert(minDigitLen > 0, "minDigitLen > 0");
             Debug.Assert(maxDigitLen < 9, "maxDigitLen < 9");
             Debug.Assert(minDigitLen <= maxDigitLen, "minDigitLen <= maxDigitLen");
-            result = 0;
+            int localResult = 0;
             int startingIndex = str.Index;
             int tokenLength = 0;
             while (tokenLength < maxDigitLen)
@@ -3176,9 +3143,10 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     str.Index--;
                     break;
                 }
-                result = result * 10 + str.GetDigit();
+                localResult = localResult * 10 + str.GetDigit();
                 tokenLength++;
             }
+            result = localResult;
             if (tokenLength < minDigitLen)
             {
                 str.Index = startingIndex;
@@ -3217,7 +3185,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 result = result * 10 + str.GetDigit();
             }
 
-            result = ((double)result / Math.Pow(10, digitLen));
+            result /= TimeSpanParse.Pow10(digitLen);
             return (digitLen == maxDigitLen);
         }
 
@@ -3639,15 +3607,18 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             // change the code below.
             if (str.GetNext())
             {
-                if (str.GetChar() == dtfi.AMDesignator[0])
+                string amDesignator = dtfi.AMDesignator;
+                if (amDesignator.Length > 0 && str.GetChar() == amDesignator[0])
                 {
                     result = TM.AM;
-                    return (true);
+                    return true;
                 }
-                if (str.GetChar() == dtfi.PMDesignator[0])
+
+                string pmDesignator = dtfi.PMDesignator;
+                if (pmDesignator.Length > 0 && str.GetChar() == pmDesignator[0])
                 {
                     result = TM.PM;
-                    return (true);
+                    return true;
                 }
             }
             return false;
@@ -3697,7 +3668,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 }
             }
 
-            // assume the offset is Local            
+            // assume the offset is Local
             return DateTime.Now;
         }
 
@@ -3718,7 +3689,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 if (((result.Month != -1) || (result.Day != -1))
                     && ((result.Year == -1 || ((result.flags & ParseFlags.YearDefault) != 0)) && (result.flags & ParseFlags.TimeZoneUsed) != 0))
                 {
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_MissingIncompleteDate), null);
+                    result.SetFailure(ParseFailureKind.FormatWithOriginalDateTime, nameof(SR.Format_MissingIncompleteDate));
                     return false;
                 }
             }
@@ -3803,7 +3774,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         // This method also set the dtfi according/parseInfo to some special pre-defined
         // formats.
         //
-        private static String ExpandPredefinedFormat(String format, ref DateTimeFormatInfo dtfi, ref ParsingInfo parseInfo, ref DateTimeResult result)
+        private static String ExpandPredefinedFormat(ReadOnlySpan<char> format, ref DateTimeFormatInfo dtfi, ref ParsingInfo parseInfo, ref DateTimeResult result)
         {
             //
             // Check the format to see if we need to override the dtfi to be InvariantInfo,
@@ -3901,7 +3872,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                     if (!parseResult)
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return (false);
                     }
                     if (!CheckNewValue(ref result.Year, tempYear, ch, ref result))
@@ -3918,7 +3889,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             if (!parseInfo.fCustomNumberParser ||
                                 !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempMonth))
                             {
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 return (false);
                             }
                         }
@@ -3929,7 +3900,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         {
                             if (!MatchAbbreviatedMonthName(ref str, dtfi, ref tempMonth))
                             {
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 return (false);
                             }
                         }
@@ -3937,7 +3908,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         {
                             if (!MatchMonthName(ref str, dtfi, ref tempMonth))
                             {
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 return (false);
                             }
                         }
@@ -3960,7 +3931,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             if (!parseInfo.fCustomNumberParser ||
                                 !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempDay))
                             {
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 return (false);
                             }
                         }
@@ -3976,7 +3947,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             // "ddd"
                             if (!MatchAbbreviatedDayName(ref str, dtfi, ref tempDayOfWeek))
                             {
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 return (false);
                             }
                         }
@@ -3985,7 +3956,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             // "dddd*"
                             if (!MatchDayName(ref str, dtfi, ref tempDayOfWeek))
                             {
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 return (false);
                             }
                         }
@@ -4000,7 +3971,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     // Put the era value in result.era.
                     if (!MatchEraName(ref str, dtfi, ref result.era))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return (false);
                     }
                     break;
@@ -4009,7 +3980,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     tokenLen = format.GetRepeatCount();
                     if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempHour))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return (false);
                     }
                     if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result))
@@ -4021,7 +3992,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     tokenLen = format.GetRepeatCount();
                     if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempHour))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return (false);
                     }
                     if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result))
@@ -4033,7 +4004,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     tokenLen = format.GetRepeatCount();
                     if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempMinute))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return (false);
                     }
                     if (!CheckNewValue(ref result.Minute, tempMinute, ch, ref result))
@@ -4045,7 +4016,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     tokenLen = format.GetRepeatCount();
                     if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempSecond))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return (false);
                     }
                     if (!CheckNewValue(ref result.Second, tempSecond, ch, ref result))
@@ -4062,7 +4033,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         {
                             if (ch == 'f')
                             {
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 return (false);
                             }
                         }
@@ -4081,7 +4052,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     }
                     else
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return (false);
                     }
                     break;
@@ -4092,7 +4063,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     {
                         if (!MatchAbbreviatedTimeMark(ref str, dtfi, ref tempTimeMark))
                         {
-                            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                            result.SetBadDateTimeFailure();
                             return (false);
                         }
                     }
@@ -4100,7 +4071,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     {
                         if (!MatchTimeMark(ref str, dtfi, ref tempTimeMark))
                         {
-                            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                            result.SetBadDateTimeFailure();
                             return (false);
                         }
                     }
@@ -4125,7 +4096,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         TimeSpan tempTimeZoneOffset = new TimeSpan(0);
                         if (!ParseTimeZoneOffset(ref str, tokenLen, ref tempTimeZoneOffset))
                         {
-                            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                            result.SetBadDateTimeFailure();
                             return (false);
                         }
                         if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset)
@@ -4155,7 +4126,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     str.Index++;
                     if (!GetTimeZoneName(ref str))
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                     str.Index--;
@@ -4180,7 +4151,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         TimeSpan tempTimeZoneOffset = new TimeSpan(0);
                         if (!ParseTimeZoneOffset(ref str, 3, ref tempTimeZoneOffset))
                         {
-                            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                            result.SetBadDateTimeFailure();
                             return (false);
                         }
                         if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset)
@@ -4200,7 +4171,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         !str.Match(dtfi.TimeSeparator))
                     {
                         // A time separator is expected.
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                     break;
@@ -4211,17 +4182,18 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         !str.Match(dtfi.DateSeparator))
                     {
                         // A date separator is expected.
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                     break;
                 case '\"':
                 case '\'':
-                    StringBuilder enquotedString = new StringBuilder();
+                    StringBuilder enquotedString = StringBuilderCache.Acquire();
                     // Use ParseQuoteString so that we can handle escape characters within the quoted string.
                     if (!TryParseQuoteString(format.Value, format.Index, enquotedString, out tokenLen))
                     {
                         result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadQuote), ch);
+                        StringBuilderCache.Release(enquotedString);
                         return (false);
                     }
                     format.Index += tokenLen - 1;
@@ -4229,7 +4201,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     // Some cultures uses space in the quoted string.  E.g. Spanish has long date format as:
                     // "dddd, dd' de 'MMMM' de 'yyyy".  When inner spaces flag is set, we should skip whitespaces if there is space
                     // in the quoted string.
-                    String quotedStr = enquotedString.ToString();
+                    String quotedStr = StringBuilderCache.GetStringAndRelease(enquotedString);
 
                     for (int i = 0; i < quotedStr.Length; i++)
                     {
@@ -4240,13 +4212,13 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         else if (!str.Match(quotedStr[i]))
                         {
                             // Can not find the matching quoted string.
-                            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                            result.SetBadDateTimeFailure();
                             return false;
                         }
                     }
 
                     // The "r" and "u" formats incorrectly quoted 'GMT' and 'Z', respectively.  We cannot
-                    // correct this mistake for DateTime.ParseExact for compatibility reasons, but we can 
+                    // correct this mistake for DateTime.ParseExact for compatibility reasons, but we can
                     // fix it for DateTimeOffset.ParseExact as DateTimeOffset has not been publically released
                     // with this issue.
                     if ((result.flags & ParseFlags.CaptureOffset) != 0)
@@ -4271,7 +4243,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     // Make sure the next character is not a '%' again.
                     if (format.Index >= format.Value.Length - 1 || format.Value[format.Index + 1] == '%')
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
+                        result.SetBadFormatSpecifierFailure(format.Value);
                         return false;
                     }
                     break;
@@ -4284,13 +4256,13 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         if (!str.Match(format.GetChar()))
                         {
                             // Can not find a match for the escaped character.
-                            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                            result.SetBadDateTimeFailure();
                             return false;
                         }
                     }
                     else
                     {
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
+                        result.SetBadFormatSpecifierFailure(format.Value);
                         return false;
                     }
                     break;
@@ -4307,7 +4279,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                                 break;
                             }
                         }
-                        result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                        result.SetBadDateTimeFailure();
                         return false;
                     }
                     break;
@@ -4342,7 +4314,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                                         }
                                     }
                                 }
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 return false;
                             }
                             // Found a macth.
@@ -4359,14 +4331,14 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             result.timeZoneOffset = TimeSpan.Zero;
                             if (!str.Match(GMTName))
                             {
-                                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                                result.SetBadDateTimeFailure();
                                 return false;
                             }
                         }
                         else if (!str.Match(ch))
                         {
                             // ch is expected.
-                            result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                            result.SetBadDateTimeFailure();
                             return false;
                         }
                     }
@@ -4379,7 +4351,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         // The pos should point to a quote character. This method will
         // get the string enclosed by the quote character.
         //
-        internal static bool TryParseQuoteString(String format, int pos, StringBuilder result, out int returnValue)
+        internal static bool TryParseQuoteString(ReadOnlySpan<char> format, int pos, StringBuilder result, out int returnValue)
         {
             //
             // NOTE : pos will be the index of the quote character in the 'format' string.
@@ -4456,8 +4428,8 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         ==============================================================================*/
 
         private static bool DoStrictParse(
-            String s,
-            String formatParam,
+            ReadOnlySpan<char> s,
+            ReadOnlySpan<char> formatParam,
             DateTimeStyles styles,
             DateTimeFormatInfo dtfi,
             ref DateTimeResult result)
@@ -4469,15 +4441,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             parseInfo.fAllowInnerWhite = ((styles & DateTimeStyles.AllowInnerWhite) != 0);
             parseInfo.fAllowTrailingWhite = ((styles & DateTimeStyles.AllowTrailingWhite) != 0);
 
-            // We need the original values of the following two below.
-            String originalFormat = formatParam;
-
             if (formatParam.Length == 1)
             {
                 if (((result.flags & ParseFlags.CaptureOffset) != 0) && formatParam[0] == 'U')
                 {
                     // The 'U' format is not allowed for DateTimeOffset
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
+                    result.SetBadFormatSpecifierFailure(formatParam);
                     return false;
                 }
                 formatParam = ExpandPredefinedFormat(formatParam, ref dtfi, ref parseInfo, ref result);
@@ -4534,7 +4503,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             if (str.Index < str.Value.Length - 1)
             {
                 // There are still remaining character in str.
-                result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                result.SetBadDateTimeFailure();
                 return false;
             }
 
@@ -4543,16 +4512,16 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 // A two digit year value is expected. Check if the parsed year value is valid.
                 if (result.Year >= 100)
                 {
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                    result.SetBadDateTimeFailure();
                     return false;
                 }
                 try
                 {
                     result.Year = parseInfo.calendar.ToFourDigitYear(result.Year);
                 }
-                catch (ArgumentOutOfRangeException e)
+                catch (ArgumentOutOfRangeException)
                 {
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), e);
+                    result.SetBadDateTimeFailure();
                     return false;
                 }
             }
@@ -4571,7 +4540,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 if (result.Hour > 12)
                 {
                     // AM/PM is used, but the value for HH is too big.
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                    result.SetBadDateTimeFailure();
                     return false;
                 }
                 if (parseInfo.timeMark == TM.AM)
@@ -4595,7 +4564,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 if ((parseInfo.timeMark == TM.AM && result.Hour >= 12)
                     || (parseInfo.timeMark == TM.PM && result.Hour < 12))
                 {
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
+                    result.SetBadDateTimeFailure();
                     return false;
                 }
             }
@@ -4612,14 +4581,14 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             {
                 if (!dtfi.YearMonthAdjustment(ref result.Year, ref result.Month, ((result.flags & ParseFlags.ParsedMonthName) != 0)))
                 {
-                    result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
+                    result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar));
                     return false;
                 }
             }
             if (!parseInfo.calendar.TryToDateTime(result.Year, result.Month, result.Day,
                     result.Hour, result.Minute, result.Second, 0, result.era, out result.parsedDate))
             {
-                result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
+                result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar));
                 return false;
             }
             if (result.fraction > 0)
@@ -4639,13 +4608,13 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 //
                 if (parseInfo.dayOfWeek != (int)parseInfo.calendar.GetDayOfWeek(result.parsedDate))
                 {
-                    result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDayOfWeek), null);
+                    result.SetFailure(ParseFailureKind.FormatWithOriginalDateTime, nameof(SR.Format_BadDayOfWeek));
                     return false;
                 }
             }
 
 
-            if (!DetermineTimeZoneAdjustments(ref result, styles, bTimeOnly))
+            if (!DetermineTimeZoneAdjustments(ref str, ref result, styles, bTimeOnly))
             {
                 return false;
             }
@@ -4663,73 +4632,72 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 case ParseFailureKind.FormatWithParameter:
                     return new FormatException(SR.Format(SR.GetResourceString(result.failureMessageID), result.failureMessageFormatArgument));
                 case ParseFailureKind.FormatBadDateTimeCalendar:
-                    return new FormatException(SR.Format(SR.GetResourceString(result.failureMessageID), result.calendar));
+                    return new FormatException(SR.Format(SR.GetResourceString(result.failureMessageID), new string(result.originalDateTimeString), result.calendar));
+                case ParseFailureKind.FormatWithOriginalDateTime:
+                    return new FormatException(SR.Format(SR.GetResourceString(result.failureMessageID), new string(result.originalDateTimeString)));
+                case ParseFailureKind.FormatWithFormatSpecifier:
+                    return new FormatException(SR.Format(SR.GetResourceString(result.failureMessageID), new string(result.failedFormatSpecifier)));
+                case ParseFailureKind.FormatWithOriginalDateTimeAndParameter:
+                    return new FormatException(SR.Format(SR.GetResourceString(result.failureMessageID), new string(result.originalDateTimeString), result.failureMessageFormatArgument));
                 default:
-                    Debug.Assert(false, "Unkown DateTimeParseFailure: " + result);
+                    Debug.Fail("Unknown DateTimeParseFailure: " + result.failure.ToString());
                     return null;
             }
         }
 
-        // Builds with _LOGGING defined (x86dbg, amd64chk, etc) support tracing
-        // Set the following internal-only/unsupported environment variables to enable DateTime tracing to the console:
-        //
-        // COMPlus_LogEnable=1
-        // COMPlus_LogToConsole=1
-        // COMPlus_LogLevel=9
-        // COMPlus_ManagedLogFacility=0x00001000
         [Conditional("_LOGGING")]
-        internal static void LexTraceExit(string message, DS dps)
+        private static void LexTraceExit(string message, DS dps)
         {
 #if _LOGGING
             if (!_tracingEnabled)
                 return;
-            BCLDebug.Trace("DATETIME", "[DATETIME] Lex return {0}, DS.{1}", message, dps);
+            Trace($"Lex return {message}, DS.{dps}");
 #endif // _LOGGING
         }
         [Conditional("_LOGGING")]
-        internal static void PTSTraceExit(DS dps, bool passed)
+        private static void PTSTraceExit(DS dps, bool passed)
         {
 #if _LOGGING
             if (!_tracingEnabled)
                 return;
-            BCLDebug.Trace("DATETIME", "[DATETIME] ProcessTerminalState {0} @ DS.{1}", passed ? "passed" : "failed", dps);
+            Trace($"ProcessTerminalState {(passed ? "passed" : "failed")} @ DS.{dps}");
 #endif // _LOGGING
         }
         [Conditional("_LOGGING")]
-        internal static void TPTraceExit(string message, DS dps)
+        private static void TPTraceExit(string message, DS dps)
         {
 #if _LOGGING
             if (!_tracingEnabled)
                 return;
-            BCLDebug.Trace("DATETIME", "[DATETIME] TryParse return {0}, DS.{1}", message, dps);
+            Trace($"TryParse return {message}, DS.{dps}");
 #endif // _LOGGING
         }
         [Conditional("_LOGGING")]
-        internal static void DTFITrace(DateTimeFormatInfo dtfi)
+        private static void DTFITrace(DateTimeFormatInfo dtfi)
         {
 #if _LOGGING
             if (!_tracingEnabled)
                 return;
 
-            BCLDebug.Trace("DATETIME", "[DATETIME] DateTimeFormatInfo Properties");
+            Trace("DateTimeFormatInfo Properties");
 #if !FEATURE_COREFX_GLOBALIZATION
-            BCLDebug.Trace("DATETIME", " NativeCalendarName {0}", Hex(dtfi.NativeCalendarName));
+            Trace($" NativeCalendarName {Hex(dtfi.NativeCalendarName)}");
 #endif
-            BCLDebug.Trace("DATETIME", "       AMDesignator {0}", Hex(dtfi.AMDesignator));
-            BCLDebug.Trace("DATETIME", "       PMDesignator {0}", Hex(dtfi.PMDesignator));
-            BCLDebug.Trace("DATETIME", "      TimeSeparator {0}", Hex(dtfi.TimeSeparator));
-            BCLDebug.Trace("DATETIME", "      AbbrvDayNames {0}", Hex(dtfi.AbbreviatedDayNames));
-            BCLDebug.Trace("DATETIME", "   ShortestDayNames {0}", Hex(dtfi.ShortestDayNames));
-            BCLDebug.Trace("DATETIME", "           DayNames {0}", Hex(dtfi.DayNames));
-            BCLDebug.Trace("DATETIME", "    AbbrvMonthNames {0}", Hex(dtfi.AbbreviatedMonthNames));
-            BCLDebug.Trace("DATETIME", "         MonthNames {0}", Hex(dtfi.MonthNames));
-            BCLDebug.Trace("DATETIME", " AbbrvMonthGenNames {0}", Hex(dtfi.AbbreviatedMonthGenitiveNames));
-            BCLDebug.Trace("DATETIME", "      MonthGenNames {0}", Hex(dtfi.MonthGenitiveNames));
+            Trace($"       AMDesignator {Hex(dtfi.AMDesignator)}");
+            Trace($"       PMDesignator {Hex(dtfi.PMDesignator)}");
+            Trace($"      TimeSeparator {Hex(dtfi.TimeSeparator)}");
+            Trace($"      AbbrvDayNames {Hex(dtfi.AbbreviatedDayNames)}");
+            Trace($"   ShortestDayNames {Hex(dtfi.ShortestDayNames)}");
+            Trace($"           DayNames {Hex(dtfi.DayNames)}");
+            Trace($"    AbbrvMonthNames {Hex(dtfi.AbbreviatedMonthNames)}");
+            Trace($"         MonthNames {Hex(dtfi.MonthNames)}");
+            Trace($" AbbrvMonthGenNames {Hex(dtfi.AbbreviatedMonthGenitiveNames)}");
+            Trace($"      MonthGenNames {Hex(dtfi.MonthGenitiveNames)}");
 #endif // _LOGGING
         }
 #if _LOGGING
         // return a string in the form: "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
-        internal static string Hex(string[] strs)
+        private static string Hex(string[] strs)
         {
             if (strs == null || strs.Length == 0)
                 return String.Empty;
@@ -4782,7 +4750,8 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             return buffer.ToString();
         }
         // return a string in the form: "Sun"
-        internal static string Hex(string str)
+        private static string Hex(string str) => Hex((ReadOnlySpan<char>)str);
+        private static string Hex(ReadOnlySpan<char> str)
         {
             StringBuilder buffer = new StringBuilder();
             buffer.Append("\"");
@@ -4797,7 +4766,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             return buffer.ToString();
         }
         // return an unicode escaped string form of char c
-        internal static String Hex(char c)
+        private static String Hex(char c)
         {
             if (c <= '\x007f')
                 return c.ToString(CultureInfo.InvariantCulture);
@@ -4805,7 +4774,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 return "\\u" + ((int)c).ToString("x4", CultureInfo.InvariantCulture);
         }
 
-        internal static bool _tracingEnabled = BCLDebug.CheckEnabled("DATETIME");
+        private static void Trace(string s)
+        {
+            // Internal.Console.WriteLine(s);
+        }
+
+        private static bool _tracingEnabled = false;
 #endif // _LOGGING
     }
 
@@ -4815,13 +4789,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
     // It has a Index property which tracks
     // the current parsing pointer of the string.
     //
-    internal
-    struct __DTString
+    internal ref struct __DTString
     {
         //
         // Value propery: stores the real string to be parsed.
         //
-        internal String Value;
+        internal ReadOnlySpan<char> Value;
 
         //
         // Index property: points to the character that we are currently parsing.
@@ -4829,7 +4802,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         internal int Index;
 
         // The length of Value string.
-        internal int len;
+        internal int Length => Value.Length;
 
         // The current chracter to be looked at.
         internal char m_current;
@@ -4839,16 +4812,15 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         // In some cultures, such as mn-MN, it uses "\x0031\x00a0\x0434\x04af\x0433\x044d\x044d\x0440\x00a0\x0441\x0430\x0440" in month names.
         private bool m_checkDigitToken;
 
-        internal __DTString(String str, DateTimeFormatInfo dtfi, bool checkDigitToken) : this(str, dtfi)
+        internal __DTString(ReadOnlySpan<char> str, DateTimeFormatInfo dtfi, bool checkDigitToken) : this(str, dtfi)
         {
             m_checkDigitToken = checkDigitToken;
         }
 
-        internal __DTString(String str, DateTimeFormatInfo dtfi)
+        internal __DTString(ReadOnlySpan<char> str, DateTimeFormatInfo dtfi)
         {
             Index = -1;
             Value = str;
-            len = Value.Length;
 
             m_current = '\0';
             if (dtfi != null)
@@ -4880,7 +4852,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         internal bool GetNext()
         {
             Index++;
-            if (Index < len)
+            if (Index < Length)
             {
                 m_current = Value[Index];
                 return (true);
@@ -4890,14 +4862,14 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
 
         internal bool AtEnd()
         {
-            return Index < len ? false : true;
+            return Index < Length ? false : true;
         }
 
         internal bool Advance(int count)
         {
-            Debug.Assert(Index + count <= len, "__DTString::Advance: Index + count <= len");
+            Debug.Assert(Index + count <= Length, "__DTString::Advance: Index + count <= len");
             Index += count;
-            if (Index < len)
+            if (Index < Length)
             {
                 m_current = Value[Index];
                 return (true);
@@ -4910,7 +4882,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         internal void GetRegularToken(out TokenType tokenType, out int tokenValue, DateTimeFormatInfo dtfi)
         {
             tokenValue = 0;
-            if (Index >= len)
+            if (Index >= Length)
             {
                 tokenType = TokenType.EndOfString;
                 return;
@@ -4929,7 +4901,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 //
                 // Collect other digits.
                 //
-                while (++Index < len)
+                while (++Index < Length)
                 {
                     m_current = Value[Index];
                     value = m_current - '0';
@@ -4985,7 +4957,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             else if (Char.IsWhiteSpace(m_current))
             {
                 // Just skip to the next character.
-                while (++Index < len)
+                while (++Index < Length)
                 {
                     m_current = Value[Index];
                     if (!(Char.IsWhiteSpace(m_current)))
@@ -5031,36 +5003,19 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             return (tokenType);
         }
 
-        internal bool MatchSpecifiedWord(String target)
-        {
-            return MatchSpecifiedWord(target, target.Length + Index);
-        }
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal bool MatchSpecifiedWord(String target) =>
+            Index + target.Length <= Length &&
+            m_info.Compare(Value.Slice(Index, target.Length), target, CompareOptions.IgnoreCase) == 0;
 
-        internal bool MatchSpecifiedWord(String target, int endIndex)
-        {
-            int count = endIndex - Index;
-
-            if (count != target.Length)
-            {
-                return false;
-            }
-
-            if (Index + count > len)
-            {
-                return false;
-            }
-
-            return (m_info.Compare(Value, Index, count, target, 0, count, CompareOptions.IgnoreCase) == 0);
-        }
-
-        private static Char[] WhiteSpaceChecks = new Char[] { ' ', '\u00A0' };
+        private static readonly Char[] WhiteSpaceChecks = new Char[] { ' ', '\u00A0' };
 
         internal bool MatchSpecifiedWords(String target, bool checkWordBoundary, ref int matchLength)
         {
             int valueRemaining = Value.Length - Index;
             matchLength = target.Length;
 
-            if (matchLength > valueRemaining || m_info.Compare(Value, Index, matchLength, target, 0, matchLength, CompareOptions.IgnoreCase) != 0)
+            if (matchLength > valueRemaining || m_info.Compare(Value.Slice(Index, matchLength), target, CompareOptions.IgnoreCase) != 0)
             {
                 // Check word by word
                 int targetPosition = 0;                 // Where we are in the target string
@@ -5090,7 +5045,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         {
                             return false;
                         }
-                        if (m_info.Compare(Value, thisPosition, segmentLength, target, targetPosition, segmentLength, CompareOptions.IgnoreCase) != 0)
+                        if (m_info.CompareOptionIgnoreCase(Value.Slice(thisPosition, segmentLength), target.AsSpan(targetPosition, segmentLength)) != 0)
                         {
                             return false;
                         }
@@ -5116,7 +5071,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                     {
                         return false;
                     }
-                    if (m_info.Compare(Value, thisPosition, segmentLength, target, targetPosition, segmentLength, CompareOptions.IgnoreCase) != 0)
+                    if (m_info.CompareOptionIgnoreCase(Value.Slice(thisPosition, segmentLength), target.AsSpan(targetPosition, segmentLength)) != 0)
                     {
                         return false;
                     }
@@ -5145,7 +5100,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         //
         internal bool Match(String str)
         {
-            if (++Index >= len)
+            if (++Index >= Length)
             {
                 return (false);
             }
@@ -5155,7 +5110,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 return false;
             }
 
-            if (m_info.Compare(Value, Index, str.Length, str, 0, str.Length, CompareOptions.Ordinal) == 0)
+            if (m_info.Compare(Value.Slice(Index, str.Length), str, CompareOptions.Ordinal) == 0)
             {
                 // Update the Index to the end of the matching string.
                 // So the following GetNext()/Match() opeartion will get
@@ -5168,7 +5123,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
 
         internal bool Match(char ch)
         {
-            if (++Index >= len)
+            if (++Index >= Length)
             {
                 return (false);
             }
@@ -5221,7 +5176,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         {
             char repeatChar = Value[Index];
             int pos = Index + 1;
-            while ((pos < len) && (Value[pos] == repeatChar))
+            while ((pos < Length) && (Value[pos] == repeatChar))
             {
                 pos++;
             }
@@ -5234,21 +5189,17 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         }
 
         // Return false when end of string is encountered or a non-digit character is found.
-        internal bool GetNextDigit()
-        {
-            if (++Index >= len)
-            {
-                return (false);
-            }
-            return (DateTimeParse.IsDigit(Value[Index]));
-        }
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal bool GetNextDigit() =>
+            ++Index < Length &&
+            DateTimeParse.IsDigit(Value[Index]);
 
         //
         // Get the current character.
         //
         internal char GetChar()
         {
-            Debug.Assert(Index >= 0 && Index < len, "Index >= 0 && Index < len");
+            Debug.Assert(Index >= 0 && Index < Length, "Index >= 0 && Index < len");
             return (Value[Index]);
         }
 
@@ -5257,7 +5208,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         //
         internal int GetDigit()
         {
-            Debug.Assert(Index >= 0 && Index < len, "Index >= 0 && Index < len");
+            Debug.Assert(Index >= 0 && Index < Length, "Index >= 0 && Index < len");
             Debug.Assert(DateTimeParse.IsDigit(Value[Index]), "IsDigit(Value[Index])");
             return (Value[Index] - '0');
         }
@@ -5271,7 +5222,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         {
             // Look ahead to see if the next character
             // is a whitespace.
-            while (Index + 1 < len)
+            while (Index + 1 < Length)
             {
                 char ch = Value[Index + 1];
                 if (!Char.IsWhiteSpace(ch))
@@ -5290,7 +5241,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         //
         internal bool SkipWhiteSpaceCurrent()
         {
-            if (Index >= len)
+            if (Index >= Length)
             {
                 return (false);
             }
@@ -5300,7 +5251,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 return (true);
             }
 
-            while (++Index < len)
+            while (++Index < Length)
             {
                 m_current = Value[Index];
                 if (!Char.IsWhiteSpace(m_current))
@@ -5314,20 +5265,19 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
 
         internal void TrimTail()
         {
-            int i = len - 1;
+            int i = Length - 1;
             while (i >= 0 && Char.IsWhiteSpace(Value[i]))
             {
                 i--;
             }
-            Value = Value.Substring(0, i + 1);
-            len = Value.Length;
+            Value = Value.Slice(0, i + 1);
         }
 
         // Trim the trailing spaces within a quoted string.
         // Call this after TrimTail() is done.
         internal void RemoveTrailingInQuoteSpaces()
         {
-            int i = len - 1;
+            int i = Length - 1;
             if (i <= 1)
             {
                 return;
@@ -5336,15 +5286,17 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             // Check if the last character is a quote.
             if (ch == '\'' || ch == '\"')
             {
-                if (Char.IsWhiteSpace(Value[i - 1]))
+                if (char.IsWhiteSpace(Value[i - 1]))
                 {
                     i--;
-                    while (i >= 1 && Char.IsWhiteSpace(Value[i - 1]))
+                    while (i >= 1 && char.IsWhiteSpace(Value[i - 1]))
                     {
                         i--;
                     }
-                    Value = Value.Remove(i, Value.Length - 1 - i);
-                    len = Value.Length;
+                    Span<char> result = new char[i + 1];
+                    result[i] = ch;
+                    Value.Slice(0, i).CopyTo(result);
+                    Value = result;
                 }
             }
         }
@@ -5353,7 +5305,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         // Call this after the leading spaces before quoted string are trimmed.
         internal void RemoveLeadingInQuoteSpaces()
         {
-            if (len <= 2)
+            if (Length <= 2)
             {
                 return;
             }
@@ -5362,14 +5314,16 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             // Check if the last character is a quote.
             if (ch == '\'' || ch == '\"')
             {
-                while ((i + 1) < len && Char.IsWhiteSpace(Value[i + 1]))
+                while ((i + 1) < Length && char.IsWhiteSpace(Value[i + 1]))
                 {
                     i++;
                 }
                 if (i != 0)
                 {
-                    Value = Value.Remove(1, i);
-                    len = Value.Length;
+                    Span<char> result = new char[Value.Length - i];
+                    result[0] = ch;
+                    Value.Slice(i + 1).CopyTo(result.Slice(1));
+                    Value = result;
                 }
             }
         }
@@ -5379,7 +5333,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             DTSubString sub = new DTSubString();
             sub.index = Index;
             sub.s = Value;
-            while (Index + sub.length < len)
+            while (Index + sub.length < Length)
             {
                 DTSubStringType currentType;
                 Char ch = Value[Index + sub.length];
@@ -5437,9 +5391,9 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         internal void ConsumeSubString(DTSubString sub)
         {
             Debug.Assert(sub.index == Index, "sub.index == Index");
-            Debug.Assert(sub.index + sub.length <= len, "sub.index + sub.length <= len");
+            Debug.Assert(sub.index + sub.length <= Length, "sub.index + sub.length <= len");
             Index = sub.index + sub.length;
-            if (Index < len)
+            if (Index < Length)
             {
                 m_current = Value[Index];
             }
@@ -5455,9 +5409,9 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         Other = 4,
     }
 
-    internal struct DTSubString
+    internal ref struct DTSubString
     {
-        internal String s;
+        internal ReadOnlySpan<char> s;
         internal Int32 index;
         internal Int32 length;
         internal DTSubStringType type;
@@ -5525,7 +5479,10 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         ArgumentNull = 1,
         Format = 2,
         FormatWithParameter = 3,
-        FormatBadDateTimeCalendar = 4,  // FormatException when ArgumentOutOfRange is thrown by a Calendar.TryToDateTime().
+        FormatWithOriginalDateTime = 4,
+        FormatWithFormatSpecifier = 5,
+        FormatWithOriginalDateTimeAndParameter = 6,
+        FormatBadDateTimeCalendar = 7,  // FormatException when ArgumentOutOfRange is thrown by a Calendar.TryToDateTime().
     };
 
     [Flags]
@@ -5553,7 +5510,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
     // used to construct a DateTime instance.
     //
     internal
-    struct DateTimeResult
+    ref struct DateTimeResult
     {
         internal int Year;
         internal int Month;
@@ -5580,9 +5537,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         internal string failureMessageID;
         internal object failureMessageFormatArgument;
         internal string failureArgumentName;
+        internal ReadOnlySpan<char> originalDateTimeString;
+        internal ReadOnlySpan<char> failedFormatSpecifier;
 
-        internal void Init()
+        internal void Init(ReadOnlySpan<char> originalDateTimeString)
         {
+            this.originalDateTimeString = originalDateTimeString;
             Year = -1;
             Month = -1;
             Day = -1;
@@ -5596,6 +5556,33 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             Month = month;
             Day = day;
         }
+
+        internal void SetBadFormatSpecifierFailure()
+        {
+            SetBadFormatSpecifierFailure(ReadOnlySpan<char>.Empty);
+        }
+
+        internal void SetBadFormatSpecifierFailure(ReadOnlySpan<char> failedFormatSpecifier)
+        {
+            this.failure = ParseFailureKind.FormatWithFormatSpecifier;
+            this.failureMessageID = nameof(SR.Format_BadFormatSpecifier);
+            this.failedFormatSpecifier = failedFormatSpecifier;
+        }
+
+        internal void SetBadDateTimeFailure()
+        {
+            this.failure = ParseFailureKind.FormatWithOriginalDateTime;
+            this.failureMessageID = nameof(SR.Format_BadDateTime);
+            this.failureMessageFormatArgument = null;
+        }
+
+        internal void SetFailure(ParseFailureKind failure, string failureMessageID)
+        {
+            this.failure = failure;
+            this.failureMessageID = failureMessageID;
+            this.failureMessageFormatArgument = null;
+        }
+
         internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument)
         {
             this.failure = failure;