public ParseResult <Offset> ParsePartial(ValueCursor cursor)
            {
                int startIndex = cursor.Index;
                // TODO: Do better than this. It's horrible, and may well be invalid
                // for some cultures. Or just remove the NumberPattern from 2.0...
                int longestPossible = Math.Min(maxLength, cursor.Length - cursor.Index);

                for (int length = longestPossible; length >= 0; length--)
                {
                    string candidate = cursor.Value.Substring(cursor.Index, length);
                    int    milliseconds;
                    if (Int32.TryParse(candidate, NumberStyles.Integer | NumberStyles.AllowThousands,
                                       formatInfo.NumberFormat, out milliseconds))
                    {
                        if (milliseconds < -NodaConstants.MillisecondsPerStandardDay ||
                            NodaConstants.MillisecondsPerStandardDay < milliseconds)
                        {
                            cursor.Move(startIndex);
                            return(ParseResult <Offset> .ValueOutOfRange(cursor, milliseconds));
                        }
                        cursor.Move(cursor.Index + length);
                        return(ParseResult <Offset> .ForValue(Offset.FromMilliseconds(milliseconds)));
                    }
                }
                cursor.Move(startIndex);
                return(ParseResult <Offset> .CannotParseValue(cursor, "n"));
            }
            public ParseResult <T> ParsePartial(ValueCursor cursor)
            {
                int index = cursor.Index;

                foreach (IPartialPattern <T> pattern in patterns)
                {
                    cursor.Move(index);
                    ParseResult <T> result = pattern.ParsePartial(cursor);
                    if (result.Success || !result.ContinueAfterErrorWithMultipleFormats)
                    {
                        return(result);
                    }
                }
                cursor.Move(index);
                return(ParseResult <T> .NoMatchingFormat(cursor));
            }
            /// <summary>
            /// Attempts to parse a fixed time zone from "UTC" with an optional
            /// offset, expressed as +HH, +HH:mm, +HH:mm:ss or +HH:mm:ss.fff - i.e. the
            /// general format. If it manages, it will move the cursor and return the
            /// zone. Otherwise, it will return null and the cursor will remain where
            /// it was.
            /// </summary>
            private DateTimeZone TryParseFixedZone(ValueCursor value)
            {
                if (value.CompareOrdinal(DateTimeZone.UtcId) != 0)
                {
                    return(null);
                }
                value.Move(value.Index + 3);
                var pattern     = OffsetPattern.GeneralInvariant.UnderlyingPattern;
                var parseResult = pattern.ParsePartial(value);

                return(parseResult.Success ? DateTimeZone.ForOffset(parseResult.Value) : DateTimeZone.Utc);
            }
            /// <summary>
            /// Tries to parse a time zone ID from the provider. Returns the zone
            /// on success (after moving the cursor to the end of the ID) or null on failure
            /// (leaving the cursor where it was).
            /// </summary>
            private DateTimeZone TryParseProviderZone(ValueCursor value)
            {
                // The IDs from the provider are guaranteed to be in order (using ordinal comparisons).
                // Use a binary search to find a match, then make sure it's the longest possible match.
                var ids        = zoneProvider.Ids;
                int lowerBound = 0;         // Inclusive
                int upperBound = ids.Count; // Exclusive

                while (lowerBound < upperBound)
                {
                    int guess  = (lowerBound + upperBound) / 2;
                    int result = value.CompareOrdinal(ids[guess]);
                    if (result < 0)
                    {
                        // Guess is later than our text: lower the upper bound
                        upperBound = guess;
                    }
                    else if (result > 0)
                    {
                        // Guess is earlier than our text: raise the lower bound
                        lowerBound = guess + 1;
                    }
                    else
                    {
                        // We've found a match! But it may not be as long as it
                        // could be. Keep looking until we find a value which isn't a match...
                        while (guess + 1 < upperBound && value.CompareOrdinal(ids[guess + 1]) == 0)
                        {
                            guess++;
                        }
                        string id = ids[guess];
                        value.Move(value.Index + id.Length);
                        return(zoneProvider[id]);
                    }
                }
                return(null);
            }
Example #5
0
 public void CompareOrdinal_LongMatch_ValueIsLater()
 {
     var value = new ValueCursor("xabc");
     value.Move(1);
     Assert.Greater(value.CompareOrdinal("aaaa"), 0);
     Assert.AreEqual(1, value.Index); // Cursor hasn't moved
 }
Example #6
0
 public void CompareOrdinal_LongMatch_ValueIsEarlier()
 {
     var value = new ValueCursor("xabc");
     value.Move(1);
     Assert.Less(value.CompareOrdinal("cccc"), 0);
     Assert.AreEqual(1, value.Index); // Cursor hasn't moved
 }
Example #7
0
 public void CompareOrdinal_ExactMatchValueContinues()
 {
     var value = new ValueCursor("xabc");
     value.Move(1);
     Assert.AreEqual(0, value.CompareOrdinal("ab"));
     Assert.AreEqual(1, value.Index); // Cursor hasn't moved
 }
Example #8
0
            /// <summary>
            /// Tries to parse a time zone ID from the provider. Returns the zone
            /// on success (after moving the cursor to the end of the ID) or null on failure
            /// (leaving the cursor where it was).
            /// </summary>
            private DateTimeZone TryParseProviderZone(ValueCursor value)
            {
                // The IDs from the provider are guaranteed to be in order (using ordinal comparisons).
                // Use a binary search to find a match, then make sure it's the longest possible match.
                var ids        = zoneProvider.Ids;
                int lowerBound = 0;         // Inclusive
                int upperBound = ids.Count; // Exclusive

                while (lowerBound < upperBound)
                {
                    int guess  = (lowerBound + upperBound) / 2;
                    int result = value.CompareOrdinal(ids[guess]);
                    if (result < 0)
                    {
                        // Guess is later than our text: lower the upper bound
                        upperBound = guess;
                    }
                    else if (result > 0)
                    {
                        // Guess is earlier than our text: raise the lower bound
                        lowerBound = guess + 1;
                    }
                    else
                    {
                        // We've found a match! But it may not be as long as it
                        // could be. Keep track of a "longest match so far" (starting with the match we've found),
                        // and keep looking through the IDs until we find an ID which doesn't start with that "longest
                        // match so far", at which point we know we're done.
                        //
                        // We can't just look through all the IDs from "guess" to "lowerBound" and stop when we hit
                        // a non-match against "value", because of situations like this:
                        // value=Etc/GMT-12
                        // guess=Etc/GMT-1
                        // IDs includes { Etc/GMT-1, Etc/GMT-10, Etc/GMT-11, Etc/GMT-12, Etc/GMT-13 }
                        // We can't stop when we hit Etc/GMT-10, because otherwise we won't find Etc/GMT-12.
                        // We *can* stop when we get to Etc/GMT-13, because by then our longest match so far will
                        // be Etc/GMT-12, and we know that anything beyond Etc/GMT-13 won't match that.
                        // We can also stop when we hit upperBound, without any more comparisons.

                        string longestSoFar = ids[guess];
                        for (int i = guess + 1; i < upperBound; i++)
                        {
                            string candidate = ids[i];
                            if (candidate.Length < longestSoFar.Length)
                            {
                                break;
                            }
                            if (string.CompareOrdinal(longestSoFar, 0, candidate, 0, longestSoFar.Length) != 0)
                            {
                                break;
                            }
                            if (value.CompareOrdinal(candidate) == 0)
                            {
                                longestSoFar = candidate;
                            }
                        }
                        value.Move(value.Index + longestSoFar.Length);
                        return(zoneProvider[longestSoFar]);
                    }
                }
                return(null);
            }