Port the DateTime parser fix from the Desktop
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Wed, 12 Aug 2015 19:50:14 +0000 (12:50 -0700)
committerMarek Safar <marek.safar@gmail.com>
Tue, 3 May 2016 09:40:05 +0000 (11:40 +0200)
This change is fixing the DateTime parser in cases of date/time strings for cultures which has the date and time separators are same. This happen with Norway, Serbia and Finland cultures

[tfs-changeset: 1513801]

mcs/class/referencesource/mscorlib/system/globalization/datetimeparse.cs

index 4bc6b82254e135ceea37fcb8652e6946b47e519f..5ff5059ac69f0d4e6fd7f896e83bfa83e02af8d6 100644 (file)
@@ -699,6 +699,18 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                                 case TokenType.SEP_Date:
                                     dtok.dtt     = DTT.YearDateSep;
                                     break;
+                                case TokenType.SEP_Time:
+                                    if (!raw.hasSameDateAndTimeSeparators)
+                                    {
+                                        result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+                                        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 are sure we are not parsing time.
+                                    dtok.dtt = DTT.YearDateSep;
+                                    break;
                                 case TokenType.SEP_DateOrOffset:
                                     // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
                                     // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
@@ -804,6 +816,14 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             raw.AddNumber(dtok.num);
                             break;                            
                         case TokenType.SEP_Time:
+                            if (raw.hasSameDateAndTimeSeparators &&
+                                (dps == DS.D_Y || dps == DS.D_YN || dps == DS.D_YNd || dps == DS.D_YM || dps == DS.D_YMd))
+                            {
+                                // we are parsing a date and we have the time separator same as date separator, so we mark the token as date separator
+                                dtok.dtt = DTT.NumDatesep;
+                                raw.AddNumber(dtok.num);
+                                break;
+                            }
                             dtok.dtt = DTT.NumTimesep;
                             raw.AddNumber(dtok.num);
                             break;
@@ -949,6 +969,18 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                             case TokenType.SEP_Date:
                                 dtok.dtt = DTT.MonthDatesep;
                                 break;
+                            case TokenType.SEP_Time:
+                                if (!raw.hasSameDateAndTimeSeparators)
+                                {
+                                    result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+                                    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 are sure we are not parsing time.
+                                dtok.dtt = DTT.MonthDatesep;
+                                break;
                             case TokenType.SEP_DateOrOffset:
                                 // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
                                 // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
@@ -2329,6 +2361,8 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 Int32 * numberPointer = stackalloc Int32[3];
                 raw.Init(numberPointer);
             }
+            raw.hasSameDateAndTimeSeparators = dtfi.DateSeparator.Equals(dtfi.TimeSeparator, StringComparison.Ordinal);
+
             result.calendar = dtfi.Calendar;
             result.era = Calendar.CurrentEra;
 
@@ -2390,6 +2424,27 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         }
                     }
 
+                    if (raw.hasSameDateAndTimeSeparators && (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 
+                        // 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)
+                        {
+                            dps = DS.D_Nd;
+                        }
+                        if (dps == DS.T_NNt)
+                        {
+                            dps = DS.D_NNd;
+
+                            // we have the case of Serbia have dates in forms 'd.M.yyyy.' so we can expect '.' after the year number. 
+                            // changing the token to YearSpace instead of YearDateSep will make the parsing not failing this case.
+                            if (dtok.dtt == DTT.YearDateSep)
+                            {
+                                dtok.dtt = DTT.YearSpace;
+                            }
+                        }
+                    }
+
                     //
                     // Advance to the next state, and continue
                     //
@@ -4833,6 +4888,7 @@ Start:
         internal int era;
         internal DateTimeParse.TM timeMark;
         internal double fraction;
+        internal bool hasSameDateAndTimeSeparators;
         //
         // <