< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 305
Coverable lines: 305
Total lines: 682
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 258
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%110%
Format(...)0%12120%
TryFormat(...)0%14140%
FormatC(...)100%110%
FormatG(...)0%220%
TryFormatStandard(...)0%66660%
FormatCustomized(...)0%60600%
InitInvariant(...)0%220%
Init(...)0%1021020%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers.Text;
 5using System.Collections.Generic;
 6using System.Diagnostics;
 7using System.Numerics;
 8using System.Runtime.InteropServices;
 9using System.Text;
 10
 11namespace System.Globalization
 12{
 13    internal static class TimeSpanFormat
 14    {
 015        internal static readonly FormatLiterals PositiveInvariantFormatLiterals = FormatLiterals.InitInvariant(isNegativ
 016        internal static readonly FormatLiterals NegativeInvariantFormatLiterals = FormatLiterals.InitInvariant(isNegativ
 17
 18        /// <summary>Main method called from TimeSpan.ToString.</summary>
 19        internal static string Format(TimeSpan value, string? format, IFormatProvider? formatProvider)
 20        {
 021            if (string.IsNullOrEmpty(format))
 22            {
 023                return FormatC(value); // formatProvider ignored, as "c" is invariant
 24            }
 25
 026            if (format.Length == 1)
 27            {
 028                char c = format[0];
 29
 030                if (c == 'c' || (c | 0x20) == 't') // special-case to optimize the default TimeSpan format
 31                {
 032                    return FormatC(value); // formatProvider ignored, as "c" is invariant
 33                }
 34
 035                if ((c | 0x20) == 'g') // special-case to optimize the remaining 'g'/'G' standard formats
 36                {
 037                    return FormatG(value, DateTimeFormatInfo.GetInstance(formatProvider), c == 'G' ? StandardFormat.G : 
 38                }
 39
 040                throw new FormatException(SR.Format_InvalidString);
 41            }
 42
 043            var vlb = new ValueListBuilder<char>(stackalloc char[256]);
 044            FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider), ref vlb);
 045            string resultString = vlb.AsSpan().ToString();
 046            vlb.Dispose();
 047            return resultString;
 48        }
 49
 50        /// <summary>Main method called from TimeSpan.TryFormat.</summary>
 51        internal static bool TryFormat<TChar>(TimeSpan value, Span<TChar> destination, out int charsWritten, ReadOnlySpa
 52        {
 053            Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
 54
 055            if (format.Length == 0)
 56            {
 057                return TryFormatStandard(value, StandardFormat.C, null, destination, out charsWritten);
 58            }
 59
 060            if (format.Length == 1)
 61            {
 062                char c = format[0];
 063                if (c == 'c' || ((c | 0x20) == 't'))
 64                {
 065                    return TryFormatStandard(value, StandardFormat.C, null, destination, out charsWritten);
 66                }
 67                else
 68                {
 069                    StandardFormat sf =
 070                        c == 'g' ? StandardFormat.g :
 071                        c == 'G' ? StandardFormat.G :
 072                        throw new FormatException(SR.Format_InvalidString);
 073                    return TryFormatStandard(value, sf, DateTimeFormatInfo.GetInstance(formatProvider).DecimalSeparatorT
 74                }
 75            }
 76
 077            var vlb = new ValueListBuilder<TChar>(stackalloc TChar[256]);
 078            FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider), ref vlb);
 079            bool result = vlb.TryCopyTo(destination, out charsWritten);
 080            vlb.Dispose();
 081            return result;
 82        }
 83
 84        internal static string FormatC(TimeSpan value)
 85        {
 086            Span<char> destination = stackalloc char[26]; // large enough for any "c" TimeSpan
 087            TryFormatStandard(value, StandardFormat.C, null, destination, out int charsWritten);
 088            return new string(destination.Slice(0, charsWritten));
 89        }
 90
 91        private static string FormatG(TimeSpan value, DateTimeFormatInfo dtfi, StandardFormat format)
 92        {
 093            string decimalSeparator = dtfi.DecimalSeparator;
 094            int maxLength = checked(25 + decimalSeparator.Length); // large enough for any "g"/"G" TimeSpan
 095            Span<char> destination = (uint)maxLength < 128 ?
 096                stackalloc char[maxLength] :
 097                new char[maxLength]; // the chances of needing this case are almost 0, as DecimalSeparator.Length will b
 098            TryFormatStandard(value, format, decimalSeparator, destination, out int charsWritten);
 099            return new string(destination.Slice(0, charsWritten));
 100        }
 101
 102        internal enum StandardFormat
 103        {
 104            C,
 105            G,
 106            g
 107        }
 108
 109        internal static bool TryFormatStandard<TChar>(TimeSpan value, StandardFormat format, ReadOnlySpan<TChar> decimal
 110        {
 0111            Debug.Assert(format == StandardFormat.C || format == StandardFormat.G || format == StandardFormat.g);
 112
 113            // First, calculate how large an output buffer is needed to hold the entire output.
 0114            int requiredOutputLength = 8; // start with "hh:mm:ss" and adjust as necessary
 115
 116            uint fraction;
 117            ulong totalSecondsRemaining;
 118            {
 119                // Turn this into a non-negative TimeSpan if possible.
 0120                long ticks = value.Ticks;
 0121                if (ticks < 0)
 122                {
 0123                    requiredOutputLength = 9; // requiredOutputLength + 1 for the leading '-' sign
 0124                    ticks = -ticks;
 0125                    if (ticks < 0)
 126                    {
 0127                        Debug.Assert(ticks == long.MinValue /* -9223372036854775808 */);
 128
 129                        // We computed these ahead of time; they're straight from the decimal representation of Int64.Mi
 0130                        fraction = 4775808;
 0131                        totalSecondsRemaining = 922337203685;
 0132                        goto AfterComputeFraction;
 133                    }
 134                }
 135
 136                ulong fraction64;
 0137                (totalSecondsRemaining, fraction64) = Math.DivRem((ulong)ticks, TimeSpan.TicksPerSecond);
 0138                fraction = (uint)fraction64;
 139            }
 140
 141        AfterComputeFraction:
 142            // Only write out the fraction if it's non-zero, and in that
 143            // case write out the entire fraction (all digits).
 0144            Debug.Assert(fraction < 10_000_000);
 0145            int fractionDigits = 0;
 146            switch (format)
 147            {
 148                case StandardFormat.C:
 149                    // "c": Write out a fraction only if it's non-zero, and write out all 7 digits of it.
 0150                    if (fraction != 0)
 151                    {
 0152                        Debug.Assert(decimalSeparator.IsEmpty);
 0153                        fractionDigits = DateTimeFormat.MaxSecondsFractionDigits;
 0154                        requiredOutputLength += fractionDigits + 1; // digits plus leading decimal separator
 155                    }
 0156                    break;
 157
 158                case StandardFormat.G:
 159                    // "G": Write out a fraction regardless of whether it's 0, and write out all 7 digits of it.
 0160                    fractionDigits = DateTimeFormat.MaxSecondsFractionDigits;
 0161                    requiredOutputLength += fractionDigits;
 0162                    requiredOutputLength += decimalSeparator.Length;
 0163                    break;
 164
 165                default:
 166                    // "g": Write out a fraction only if it's non-zero, and write out only the most significant digits.
 0167                    Debug.Assert(format == StandardFormat.g);
 0168                    if (fraction != 0)
 169                    {
 0170                        fractionDigits = DateTimeFormat.MaxSecondsFractionDigits - FormattingHelpers.CountDecimalTrailin
 0171                        requiredOutputLength += fractionDigits;
 0172                        requiredOutputLength += decimalSeparator.Length;
 173                    }
 174                    break;
 175            }
 176
 0177            ulong totalMinutesRemaining = 0, seconds = 0;
 0178            if (totalSecondsRemaining > 0)
 179            {
 180                // Only compute minutes if the TimeSpan has an absolute value of >= 1 minute.
 0181                (totalMinutesRemaining, seconds) = Math.DivRem(totalSecondsRemaining, 60 /* seconds per minute */);
 0182                Debug.Assert(seconds < 60);
 183            }
 184
 0185            ulong totalHoursRemaining = 0, minutes = 0;
 0186            if (totalMinutesRemaining > 0)
 187            {
 188                // Only compute hours if the TimeSpan has an absolute value of >= 1 hour.
 0189                (totalHoursRemaining, minutes) = Math.DivRem(totalMinutesRemaining, 60 /* minutes per hour */);
 0190                Debug.Assert(minutes < 60);
 191            }
 192
 193            // At this point, we can switch over to 32-bit DivRem since the data has shrunk far enough.
 0194            Debug.Assert(totalHoursRemaining <= uint.MaxValue);
 195
 0196            uint days = 0, hours = 0;
 0197            if (totalHoursRemaining > 0)
 198            {
 199                // Only compute days if the TimeSpan has an absolute value of >= 1 day.
 0200                (days, hours) = Math.DivRem((uint)totalHoursRemaining, 24 /* hours per day */);
 0201                Debug.Assert(hours < 24);
 202            }
 203
 0204            int hourDigits = 2;
 0205            if (format == StandardFormat.g && hours < 10)
 206            {
 207                // "g": Only writing a one-digit hour, rather than expected two-digit hour
 0208                hourDigits = 1;
 0209                requiredOutputLength--;
 210            }
 211
 0212            int dayDigits = 0;
 0213            if (days > 0)
 214            {
 0215                dayDigits = FormattingHelpers.CountDigits(days);
 0216                Debug.Assert(dayDigits <= 8);
 0217                requiredOutputLength += dayDigits + 1; // for the leading "d."
 218            }
 0219            else if (format == StandardFormat.G)
 220            {
 221                // "G": has a leading "0:" if days is 0
 0222                requiredOutputLength += 2;
 0223                dayDigits = 1;
 224            }
 225
 0226            if (destination.Length < requiredOutputLength)
 227            {
 0228                written = 0;
 0229                return false;
 230            }
 231
 0232            int pos = 0;
 233
 234            // Write leading '-' if necessary
 0235            if (value.Ticks < 0)
 236            {
 0237                destination[pos++] = TChar.CastFrom('-');
 238            }
 239
 240            // Write day and separator, if necessary
 0241            if (dayDigits != 0)
 242            {
 0243                Number.WriteDigits(days, destination.Slice(pos, dayDigits));
 0244                pos += dayDigits;
 0245                destination[pos++] = TChar.CastFrom(format == StandardFormat.C ? '.' : ':');
 246            }
 247
 248            // After writing the variable-length prefix into destination[0..pos), write the
 249            // fixed "[h]h:mm:ss[.fraction]" suffix.  We branch on hourDigits (1 or 2) and
 250            // initialize suffixLen to the minimum length (8 or 7) before conditionally adding
 251            // the fraction part, giving the JIT a concrete lower bound to hoist bounds checks
 252            // out of the inner writes.
 0253            Debug.Assert(hourDigits == 1 || hourDigits == 2);
 254            int suffixLen;
 0255            if (hourDigits == 2)
 256            {
 0257                int decSepLen = 0;
 0258                suffixLen = 8; // hh:mm:ss
 0259                if (fractionDigits != 0)
 260                {
 0261                    decSepLen = format == StandardFormat.C ? 1 : decimalSeparator.Length;
 0262                    suffixLen += decSepLen + fractionDigits;
 263                }
 264                // Invariant: suffixLen >= 8 by construction; this check is unreachable but lets
 265                // the JIT prove that all suffix writes at constant offsets 0..7 are in bounds.
 0266                if ((uint)suffixLen < 8u)
 267                {
 0268                    ThrowHelper.ThrowUnreachableException();
 269                }
 0270                Span<TChar> suffix = destination.Slice(pos, suffixLen);
 271
 0272                Number.WriteTwoDigits(hours, suffix.Slice(0, 2));
 0273                suffix[2] = TChar.CastFrom(':');
 0274                Number.WriteTwoDigits((uint)minutes, suffix.Slice(3, 2));
 0275                suffix[5] = TChar.CastFrom(':');
 0276                Number.WriteTwoDigits((uint)seconds, suffix.Slice(6, 2));
 277
 0278                if (fractionDigits != 0)
 279                {
 0280                    if (format == StandardFormat.C)
 281                    {
 0282                        suffix[8] = TChar.CastFrom('.');
 283                    }
 0284                    else if (decSepLen == 1)
 285                    {
 0286                        suffix[8] = decimalSeparator[0];
 287                    }
 288                    else
 289                    {
 0290                        decimalSeparator.CopyTo(suffix.Slice(8, decSepLen));
 291                    }
 292
 0293                    Number.WriteDigits(fraction, suffix.Slice(8 + decSepLen, fractionDigits));
 294                }
 295            }
 296            else
 297            {
 0298                int decSepLen = 0;
 0299                suffixLen = 7; // h:mm:ss
 0300                if (fractionDigits != 0)
 301                {
 0302                    decSepLen = format == StandardFormat.C ? 1 : decimalSeparator.Length;
 0303                    suffixLen += decSepLen + fractionDigits;
 304                }
 305                // Invariant: suffixLen >= 7 by construction; this check is unreachable but lets
 306                // the JIT prove that all suffix writes at constant offsets 0..6 are in bounds.
 0307                if ((uint)suffixLen < 7u)
 308                {
 0309                    ThrowHelper.ThrowUnreachableException();
 310                }
 0311                Span<TChar> suffix = destination.Slice(pos, suffixLen);
 312
 0313                suffix[0] = TChar.CastFrom('0' + (int)hours);
 0314                suffix[1] = TChar.CastFrom(':');
 0315                Number.WriteTwoDigits((uint)minutes, suffix.Slice(2, 2));
 0316                suffix[4] = TChar.CastFrom(':');
 0317                Number.WriteTwoDigits((uint)seconds, suffix.Slice(5, 2));
 318
 0319                if (fractionDigits != 0)
 320                {
 0321                    if (format == StandardFormat.C)
 322                    {
 0323                        suffix[7] = TChar.CastFrom('.');
 324                    }
 0325                    else if (decSepLen == 1)
 326                    {
 0327                        suffix[7] = decimalSeparator[0];
 328                    }
 329                    else
 330                    {
 0331                        decimalSeparator.CopyTo(suffix.Slice(7, decSepLen));
 332                    }
 333
 0334                    Number.WriteDigits(fraction, suffix.Slice(7 + decSepLen, fractionDigits));
 335                }
 336            }
 337
 0338            Debug.Assert(pos + suffixLen == requiredOutputLength);
 339
 0340            written = requiredOutputLength;
 0341            return true;
 342        }
 343
 344        /// <summary>Format the TimeSpan instance using the specified format.</summary>
 345        private static void FormatCustomized<TChar>(TimeSpan value, scoped ReadOnlySpan<char> format, DateTimeFormatInfo
 346        {
 0347            Debug.Assert(dtfi != null);
 348
 0349            int day = (int)(value.Ticks / TimeSpan.TicksPerDay);
 0350            long time = value.Ticks % TimeSpan.TicksPerDay;
 351
 0352            if (value.Ticks < 0)
 353            {
 0354                day = -day;
 0355                time = -time;
 356            }
 0357            int hours = (int)(time / TimeSpan.TicksPerHour % 24);
 0358            int minutes = (int)(time / TimeSpan.TicksPerMinute % 60);
 0359            int seconds = (int)(time / TimeSpan.TicksPerSecond % 60);
 0360            int fraction = (int)(time % TimeSpan.TicksPerSecond);
 361
 362            int tmp;
 0363            int i = 0;
 364            int tokenLen;
 365
 0366            while (i < format.Length)
 367            {
 0368                char ch = format[i];
 369                int nextChar;
 370                switch (ch)
 371                {
 372                    case 'h':
 0373                        tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
 0374                        if (tokenLen > 2)
 375                        {
 376                            goto default; // to release the builder and throw
 377                        }
 0378                        DateTimeFormat.FormatDigits(ref result, hours, tokenLen);
 0379                        break;
 380                    case 'm':
 0381                        tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
 0382                        if (tokenLen > 2)
 383                        {
 384                            goto default; // to release the builder and throw
 385                        }
 0386                        DateTimeFormat.FormatDigits(ref result, minutes, tokenLen);
 0387                        break;
 388                    case 's':
 0389                        tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
 0390                        if (tokenLen > 2)
 391                        {
 392                            goto default; // to release the builder and throw
 393                        }
 0394                        DateTimeFormat.FormatDigits(ref result, seconds, tokenLen);
 0395                        break;
 396                    case 'f':
 397                        //
 398                        // The fraction of a second in single-digit precision. The remaining digits are truncated.
 399                        //
 0400                        tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
 0401                        if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits)
 402                        {
 403                            goto default; // to release the builder and throw
 404                        }
 405
 0406                        tmp = fraction;
 0407                        tmp /= TimeSpanParse.Pow10UpToMaxFractionDigits(DateTimeFormat.MaxSecondsFractionDigits - tokenL
 0408                        DateTimeFormat.FormatFraction(ref result, tmp, DateTimeFormat.fixedNumberFormats[tokenLen - 1]);
 0409                        break;
 410                    case 'F':
 411                        //
 412                        // Displays the most significant digit of the seconds fraction. Nothing is displayed if the digi
 413                        //
 0414                        tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
 0415                        if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits)
 416                        {
 417                            goto default; // to release the builder and throw
 418                        }
 419
 0420                        tmp = fraction;
 0421                        tmp /= TimeSpanParse.Pow10UpToMaxFractionDigits(DateTimeFormat.MaxSecondsFractionDigits - tokenL
 0422                        int effectiveDigits = tokenLen;
 0423                        while (effectiveDigits > 0)
 424                        {
 0425                            if (tmp % 10 == 0)
 426                            {
 0427                                tmp /= 10;
 0428                                effectiveDigits--;
 429                            }
 430                            else
 431                            {
 432                                break;
 433                            }
 434                        }
 0435                        if (effectiveDigits > 0)
 436                        {
 0437                            DateTimeFormat.FormatFraction(ref result, tmp, DateTimeFormat.fixedNumberFormats[effectiveDi
 438                        }
 0439                        break;
 440                    case 'd':
 441                        //
 442                        // tokenLen == 1 : Day as digits with no leading zero.
 443                        // tokenLen == 2+: Day as digits with leading zero for single-digit days.
 444                        //
 0445                        tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
 0446                        if (tokenLen > 8)
 447                        {
 448                            goto default; // to release the builder and throw
 449                        }
 450
 0451                        DateTimeFormat.FormatDigits(ref result, day, tokenLen);
 0452                        break;
 453                    case '\'':
 454                    case '\"':
 0455                        tokenLen = DateTimeFormat.ParseQuoteString(format, i, ref result);
 0456                        break;
 457                    case '%':
 458                        // Optional format character.
 459                        // For example, format string "%d" will print day
 460                        // Most of the cases, "%" can be ignored.
 0461                        nextChar = DateTimeFormat.ParseNextChar(format, i);
 462                        // nextChar will be -1 if we already reach the end of the format string.
 463                        // Besides, we will not allow "%%" appear in the pattern.
 0464                        if (nextChar >= 0 && nextChar != (int)'%')
 465                        {
 0466                            char nextCharChar = (char)nextChar;
 0467                            FormatCustomized(value, new ReadOnlySpan<char>(in nextCharChar), dtfi, ref result);
 0468                            tokenLen = 2;
 469                        }
 470                        else
 471                        {
 472                            //
 473                            // This means that '%' is at the end of the format string or
 474                            // "%%" appears in the format string.
 475                            //
 476                            goto default; // to release the builder and throw
 477                        }
 478                        break;
 479                    case '\\':
 480                        // Escaped character.  Can be used to insert character into the format string.
 481                        // For example, "\d" will insert the character 'd' into the string.
 482                        //
 0483                        nextChar = DateTimeFormat.ParseNextChar(format, i);
 0484                        if (nextChar >= 0)
 485                        {
 0486                            char escapedChar = (char)nextChar;
 0487                            if (char.IsHighSurrogate(escapedChar) && i + 2 < format.Length && char.IsLowSurrogate(format
 488                            {
 0489                                tokenLen = 1 + DateTimeFormat.AppendChar(ref result, format[(i + 1)..]);
 490                            }
 491                            else
 492                            {
 0493                                DateTimeFormat.AppendChar(ref result, escapedChar);
 0494                                tokenLen = 2;
 495                            }
 496                        }
 497                        else
 498                        {
 499                            //
 500                            // This means that '\' is at the end of the formatting string.
 501                            //
 502                            goto default; // to release the builder and throw
 503                        }
 504                        break;
 505                    default:
 506                        // Invalid format string
 0507                        throw new FormatException(SR.Format_InvalidString);
 508                }
 0509                i += tokenLen;
 510            }
 0511        }
 512
 513        internal struct FormatLiterals
 514        {
 515            internal string AppCompatLiteral;
 516            internal int dd;
 517            internal int hh;
 518            internal int mm;
 519            internal int ss;
 520            internal int ff;
 521
 522            private string[] _literals;
 523
 0524            internal string Start => _literals[0];
 0525            internal string DayHourSep => _literals[1];
 0526            internal string HourMinuteSep => _literals[2];
 0527            internal string MinuteSecondSep => _literals[3];
 0528            internal string SecondFractionSep => _literals[4];
 0529            internal string End => _literals[5];
 530
 531            /* factory method for static invariant FormatLiterals */
 532            internal static FormatLiterals InitInvariant(bool isNegative)
 533            {
 0534                FormatLiterals x = default;
 0535                x._literals = new string[6];
 0536                x._literals[0] = isNegative ? "-" : string.Empty;
 0537                x._literals[1] = ".";
 0538                x._literals[2] = ":";
 0539                x._literals[3] = ":";
 0540                x._literals[4] = ".";
 0541                x._literals[5] = string.Empty;
 0542                x.AppCompatLiteral = ":."; // MinuteSecondSep+SecondFractionSep;
 0543                x.dd = 2;
 0544                x.hh = 2;
 0545                x.mm = 2;
 0546                x.ss = 2;
 0547                x.ff = DateTimeFormat.MaxSecondsFractionDigits;
 0548                return x;
 549            }
 550
 551            // For the "v1" TimeSpan localized patterns, the data is simply literal field separators with
 552            // the constants guaranteed to include DHMSF ordered greatest to least significant.
 553            // Once the data becomes more complex than this we will need to write a proper tokenizer for
 554            // parsing and formatting
 555            internal void Init(ReadOnlySpan<char> format, bool useInvariantFieldLengths)
 556            {
 0557                dd = hh = mm = ss = ff = 0;
 0558                _literals = new string[6];
 0559                for (int i = 0; i < _literals.Length; i++)
 560                {
 0561                    _literals[i] = string.Empty;
 562                }
 563
 0564                var sb = new ValueStringBuilder(stackalloc char[256]);
 0565                bool inQuote = false;
 0566                char quote = '\'';
 0567                int field = 0;
 568
 0569                for (int i = 0; i < format.Length; i++)
 570                {
 0571                    switch (format[i])
 572                    {
 573                        case '\'':
 574                        case '\"':
 0575                            if (inQuote && (quote == format[i]))
 576                            {
 577                                /* we were in a quote and found a matching exit quote, so we are outside a quote now */
 0578                                Debug.Assert(field >= 0 && field <= 5);
 0579                                _literals[field] = sb.AsSpan().ToString();
 0580                                sb.Length = 0;
 0581                                inQuote = false;
 582                            }
 0583                            else if (!inQuote)
 584                            {
 585                                /* we are at the start of a new quote block */
 0586                                quote = format[i];
 0587                                inQuote = true;
 588                            }
 589                            else
 590                            {
 591                                /* we were in a quote and saw the other type of quote character, so we are still in a qu
 592                            }
 593                            break;
 594                        case '%':
 0595                            Debug.Fail("Unexpected special token '%', Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Ne
 596                            goto default;
 597                        case '\\':
 0598                            if (!inQuote)
 599                            {
 0600                                i++; /* skip next character that is escaped by this backslash or percent sign */
 0601                                break;
 602                            }
 603                            goto default;
 604                        case 'd':
 0605                            if (!inQuote)
 606                            {
 0607                                Debug.Assert((field == 0 && sb.Length == 0) || field == 1, "field == 0 || field == 1, Bu
 0608                                field = 1; // DayHourSep
 0609                                dd++;
 610                            }
 0611                            break;
 612                        case 'h':
 0613                            if (!inQuote)
 614                            {
 0615                                Debug.Assert((field == 1 && sb.Length == 0) || field == 2, "field == 1 || field == 2, Bu
 0616                                field = 2; // HourMinuteSep
 0617                                hh++;
 618                            }
 0619                            break;
 620                        case 'm':
 0621                            if (!inQuote)
 622                            {
 0623                                Debug.Assert((field == 2 && sb.Length == 0) || field == 3, "field == 2 || field == 3, Bu
 0624                                field = 3; // MinuteSecondSep
 0625                                mm++;
 626                            }
 0627                            break;
 628                        case 's':
 0629                            if (!inQuote)
 630                            {
 0631                                Debug.Assert((field == 3 && sb.Length == 0) || field == 4, "field == 3 || field == 4, Bu
 0632                                field = 4; // SecondFractionSep
 0633                                ss++;
 634                            }
 0635                            break;
 636                        case 'f':
 637                        case 'F':
 0638                            if (!inQuote)
 639                            {
 0640                                Debug.Assert((field == 4 && sb.Length == 0) || field == 5, "field == 4 || field == 5, Bu
 0641                                field = 5; // End
 0642                                ff++;
 643                            }
 0644                            break;
 645                        default:
 0646                            sb.Append(format[i]);
 647                            break;
 648                    }
 649                }
 650
 0651                sb.Dispose();
 652
 0653                Debug.Assert(field == 5);
 0654                AppCompatLiteral = MinuteSecondSep + SecondFractionSep;
 655
 0656                Debug.Assert(0 < dd && dd < 3, "0 < dd && dd < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTim
 0657                Debug.Assert(0 < hh && hh < 3, "0 < hh && hh < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTim
 0658                Debug.Assert(0 < mm && mm < 3, "0 < mm && mm < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTim
 0659                Debug.Assert(0 < ss && ss < 3, "0 < ss && ss < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTim
 0660                Debug.Assert(0 < ff && ff < 8, "0 < ff && ff < 8, Bug in System.Globalization.DateTimeFormatInfo.FullTim
 661
 0662                if (useInvariantFieldLengths)
 663                {
 0664                    dd = 2;
 0665                    hh = 2;
 0666                    mm = 2;
 0667                    ss = 2;
 0668                    ff = DateTimeFormat.MaxSecondsFractionDigits;
 669                }
 670                else
 671                {
 0672                    if (dd < 1 || dd > 2) dd = 2;   // The DTFI property has a problem. let's try to make the best of th
 0673                    if (hh < 1 || hh > 2) hh = 2;
 0674                    if (mm < 1 || mm > 2) mm = 2;
 0675                    if (ss < 1 || ss > 2) ss = 2;
 0676                    if (ff < 1 || ff > 7) ff = 7;
 677                }
 0678            }
 679        }
 680    }
 681}
 682