Example #1
0
 public void ParseFraction_NonAscii_NeverMatches()
 {
     // Arabic-Indic digits 0 and 1. See
     // http://www.unicode.org/charts/PDF/U0600.pdf
     var value = new ValueCursor("\u0660\u0661");
     Assert.True(value.MoveNext());
     int actual;
     Assert.False(value.ParseFraction(2, 2, out actual, 2));
 }
Example #2
0
            // TODO(misc): Tidy this up a *lot*.
            public ParseResult <Period> Parse(string text)
            {
                if (text is null)
                {
                    return(ParseResult <Period> .ArgumentNull("text"));
                }
                if (text.Length == 0)
                {
                    return(ParseResult <Period> .ValueStringEmpty);
                }

                ValueCursor valueCursor = new ValueCursor(text);

                valueCursor.MoveNext();
                if (valueCursor.Current != 'P')
                {
                    return(ParseResult <Period> .MismatchedCharacter(valueCursor, 'P'));
                }
                bool          inDate     = true;
                PeriodBuilder builder    = new PeriodBuilder();
                PeriodUnits   unitsSoFar = 0;

                while (valueCursor.MoveNext())
                {
                    if (inDate && valueCursor.Current == 'T')
                    {
                        inDate = false;
                        continue;
                    }
                    bool negative = valueCursor.Current == '-';
                    var  failure  = valueCursor.ParseInt64 <Period>(out long unitValue);
                    if (failure != null)
                    {
                        return(failure);
                    }
                    if (valueCursor.Length == valueCursor.Index)
                    {
                        return(ParseResult <Period> .EndOfString(valueCursor));
                    }
                    // Various failure cases:
                    // - Repeated unit (e.g. P1M2M)
                    // - Time unit is in date part (e.g. P5M)
                    // - Date unit is in time part (e.g. PT1D)
                    // - Unit is in incorrect order (e.g. P5D1Y)
                    // - Unit is invalid (e.g. P5J)
                    // - Unit is missing (e.g. P5)
                    PeriodUnits unit;
                    switch (valueCursor.Current)
                    {
                    case 'Y': unit = PeriodUnits.Years; break;

                    case 'M': unit = inDate ? PeriodUnits.Months : PeriodUnits.Minutes; break;

                    case 'W': unit = PeriodUnits.Weeks; break;

                    case 'D': unit = PeriodUnits.Days; break;

                    case 'H': unit = PeriodUnits.Hours; break;

                    case 'S': unit = PeriodUnits.Seconds; break;

                    case ',':
                    case '.': unit = PeriodUnits.Nanoseconds; break;     // Special handling below

                    default: return(InvalidUnit(valueCursor, valueCursor.Current));
                    }
                    if ((unit & unitsSoFar) != 0)
                    {
                        return(RepeatedUnit(valueCursor, valueCursor.Current));
                    }

                    // This handles putting months before years, for example. Less significant units
                    // have higher integer representations.
                    if (unit < unitsSoFar)
                    {
                        return(MisplacedUnit(valueCursor, valueCursor.Current));
                    }

                    // The result of checking "there aren't any time units in this unit" should be
                    // equal to "we're still in the date part".
                    if ((unit & PeriodUnits.AllTimeUnits) == 0 != inDate)
                    {
                        return(MisplacedUnit(valueCursor, valueCursor.Current));
                    }

                    // Seen a . or , which need special handling.
                    if (unit == PeriodUnits.Nanoseconds)
                    {
                        // Check for already having seen seconds, e.g. PT5S0.5
                        if ((unitsSoFar & PeriodUnits.Seconds) != 0)
                        {
                            return(MisplacedUnit(valueCursor, valueCursor.Current));
                        }
                        builder.Seconds = unitValue;

                        if (!valueCursor.MoveNext())
                        {
                            return(ParseResult <Period> .MissingNumber(valueCursor));
                        }
                        // Can cope with at most 999999999 nanoseconds
                        if (!valueCursor.ParseFraction(9, 9, out int totalNanoseconds, 1))
                        {
                            return(ParseResult <Period> .MissingNumber(valueCursor));
                        }
                        // Use whether or not the seconds value was negative (even if 0)
                        // as the indication of whether this value is negative.
                        if (negative)
                        {
                            totalNanoseconds = -totalNanoseconds;
                        }
                        builder.Milliseconds = (totalNanoseconds / NanosecondsPerMillisecond) % MillisecondsPerSecond;
                        builder.Ticks        = (totalNanoseconds / NanosecondsPerTick) % TicksPerMillisecond;
                        builder.Nanoseconds  = totalNanoseconds % NanosecondsPerTick;

                        if (valueCursor.Current != 'S')
                        {
                            return(ParseResult <Period> .MismatchedCharacter(valueCursor, 'S'));
                        }
                        if (valueCursor.MoveNext())
                        {
                            return(ParseResult <Period> .ExpectedEndOfString(valueCursor));
                        }
                        return(ParseResult <Period> .ForValue(builder.Build()));
                    }

                    builder[unit] = unitValue;
                    unitsSoFar   |= unit;
                }
                if (unitsSoFar == 0)
                {
                    return(ParseResult <Period> .ForInvalidValue(valueCursor, TextErrorMessages.EmptyPeriod));
                }
                return(ParseResult <Period> .ForValue(builder.Build()));
            }