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); }
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 }
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 }
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 }
/// <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); }