/// <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);
            }
예제 #3
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
 }
예제 #4
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
 }
예제 #5
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
 }
예제 #6
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);
            }