<commit_before>// Copyright 2009 The Noda Time Authors. All rights reserved.
// Use of this source code is governed by the Apache License 2.0,
// as found in the LICENSE.txt file.

using System;
using System.Collections.Generic;
using NodaTime.TimeZones;
using NodaTime.Utility;

namespace NodaTime.TzdbCompiler.Tzdb
{
    /// <summary>
    /// Provides a means of programatically creating complex time zones. Currently internal, but we
    /// may want to make it public again eventually.
    /// </summary>
    /// <remarks>
    /// <para>
    /// DateTimeZoneBuilder allows complex DateTimeZones to be constructed. Since creating a new
    /// DateTimeZone this way is a relatively expensive operation, built zones can be written to a
    /// file. Reading back the encoded data is a quick operation.
    /// </para>
    /// <para>
    /// DateTimeZoneBuilder itself is mutable and not thread-safe, but the DateTimeZone objects that
    /// it builds are thread-safe and immutable.
    /// </para>
    /// <para>
    /// It is intended that {@link NodaTime.TzdbCompiler} be used to read time zone data files,
    /// indirectly calling DateTimeZoneBuilder. The following complex example defines the
    /// America/Los_Angeles time zone, with all historical transitions:
    /// </para>
    /// <para>
    /// <example>
    ///     DateTimeZone America_Los_Angeles = new DateTimeZoneBuilder()
    ///         .AddCutover(-2147483648, 'w', 1, 1, 0, false, 0)
    ///         .SetStandardOffset(-28378000)
    ///         .SetFixedSavings("LMT", 0)
    ///         .AddCutover(1883, 'w', 11, 18, 0, false, 43200000)
    ///         .SetStandardOffset(-28800000)
    ///         .AddRecurringSavings("PDT", 3600000, 1918, 1919, 'w',  3, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PST",       0, 1918, 1919, 'w', 10, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PWT", 3600000, 1942, 1942, 'w',  2,  9, 0, false, 7200000)
    ///         .AddRecurringSavings("PPT", 3600000, 1945, 1945, 'u',  8, 14, 0, false, 82800000)
    ///         .AddRecurringSavings("PST",       0, 1945, 1945, 'w',  9, 30, 0, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1948, 1948, 'w',  3, 14, 0, false, 7200000)
    ///         .AddRecurringSavings("PST",       0, 1949, 1949, 'w',  1,  1, 0, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1950, 1966, 'w',  4, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PST",       0, 1950, 1961, 'w',  9, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PST",       0, 1962, 1966, 'w', 10, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PST",       0, 1967, 2147483647, 'w', 10, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1967, 1973, 'w', 4, -1,  7, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1974, 1974, 'w', 1,  6,  0, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1975, 1975, 'w', 2, 23,  0, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1976, 1986, 'w', 4, -1,  7, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1987, 2147483647, 'w', 4, 1, 7, true, 7200000)
    ///         .ToDateTimeZone("America/Los_Angeles");
    /// </example>
    /// </para>
    /// <para>
    /// Original name: DateTimeZoneBuilder.
    /// </para>
    /// </remarks>
    internal sealed class DateTimeZoneBuilder
    {
        private readonly IList<ZoneRecurrenceCollection> ruleSets = new List<ZoneRecurrenceCollection>();

        /// <summary>
        /// Gets the last rule set in this builder. If there are currently no rule sets,
        /// one that spans all of time is created and returned.
        /// </summary>
        /// <value>The last rule set.</value>
        private ZoneRecurrenceCollection LastRuleSet
        {
            get
            {
                if (ruleSets.Count == 0)
                {
                    AddEndOfTimeRuleSet();
                }
                return ruleSets[ruleSets.Count - 1];
            }
        }

        /// <summary>
        /// Adds a cutover for added rules.
        /// </summary>
        /// <remarks>
        /// A cutover is a point where the standard offset from GMT/UTC changed. This occurs mostly
        /// pre-1900. The standard offset at the cutover defaults to 0.
        /// Call <see cref="DateTimeZoneBuilder.SetStandardOffset"/> afterwards to change it.
        /// </remarks>
        /// <param name="year">The year of cutover.</param>
        /// <param name="yearOffset">The offset into the year of the cutover.</param>
        /// <returns>This <see cref="DateTimeZoneBuilder"/> for chaining.</returns>
        public DateTimeZoneBuilder AddCutover(int year, ZoneYearOffset yearOffset)
        {
            Preconditions.CheckNotNull(yearOffset, "yearOffset");

            if (ruleSets.Count > 0)
            {
                LastRuleSet.SetUpperLimit(year, yearOffset);
            }
            AddEndOfTimeRuleSet();
            return this;
        }

        /// <summary>
        /// Sets the standard offset to use for newly added rules until the next cutover is added.
        /// </summary>
        /// <param name="standardOffset">The standard offset.</param>
        /// <returns>This <see cref="DateTimeZoneBuilder"/> for chaining.</returns>
        public DateTimeZoneBuilder SetStandardOffset(Offset standardOffset)
        {
            LastRuleSet.StandardOffset = standardOffset;
            return this;
        }

        /// <summary>
        /// Sets a fixed savings rule at the cutover.
        /// </summary>
        /// <param name="nameKey">The name key of new rule.</param>
        /// <param name="savings">The <see cref="Duration"/> to add to standard offset.</param>
        /// <returns>This <see cref="DateTimeZoneBuilder"/> for chaining.</returns>
        public DateTimeZoneBuilder SetFixedSavings(String nameKey, Offset savings)
        {
            LastRuleSet.SetFixedSavings(nameKey, savings);
            return this;
        }

        /// <summary>
        /// Adds a recurring daylight saving time rule.
        /// </summary>
        /// <param name="recurrence">The zone recurrence defining the recurrening savings.</param>
        /// <returns>This <see cref="DateTimeZoneBuilder"/> for chaining.</returns> 
        public DateTimeZoneBuilder AddRecurringSavings(ZoneRecurrence recurrence)
        {
            Preconditions.CheckNotNull(recurrence, "recurrence");
            if (recurrence.FromYear <= recurrence.ToYear)
            {
                LastRuleSet.AddRule(recurrence);
            }
            return this;
        }

        /// <summary>
        /// Processes all the rules and builds a DateTimeZone.
        /// </summary>
        /// <param name="zoneId">Time zone ID to assign</param>
        public DateTimeZone ToDateTimeZone(String zoneId)
        {
            Preconditions.CheckNotNull(zoneId, "zoneId");

            var transitions = new List<ZoneTransition>();
            DateTimeZone tailZone = null;
            Instant instant = Instant.BeforeMinValue;

            int ruleSetCount = ruleSets.Count;
            bool tailZoneSeamValid = false;
            for (int i = 0; i < ruleSetCount; i++)
            {
                var ruleSet = ruleSets[i];
                var transitionIterator = ruleSet.Iterator(instant);
                ZoneTransition nextTransition = transitionIterator.First();
                if (nextTransition == null)
                {
                    continue;
                }<commit_msg>Note for future self, to have yet another go at tidying up DateTimeZoneBuilder. One day I'll understand that code...<commit_after>// Copyright 2009 The Noda Time Authors. All rights reserved.
// Use of this source code is governed by the Apache License 2.0,
// as found in the LICENSE.txt file.

using System;
using System.Collections.Generic;
using NodaTime.TimeZones;
using NodaTime.Utility;

namespace NodaTime.TzdbCompiler.Tzdb
{
    /// <summary>
    /// Provides a means of programatically creating complex time zones. Currently internal, but we
    /// may want to make it public again eventually.
    /// </summary>
    /// <remarks>
    /// <para>
    /// DateTimeZoneBuilder allows complex DateTimeZones to be constructed. Since creating a new
    /// DateTimeZone this way is a relatively expensive operation, built zones can be written to a
    /// file. Reading back the encoded data is a quick operation.
    /// </para>
    /// <para>
    /// DateTimeZoneBuilder itself is mutable and not thread-safe, but the DateTimeZone objects that
    /// it builds are thread-safe and immutable.
    /// </para>
    /// <para>
    /// It is intended that {@link NodaTime.TzdbCompiler} be used to read time zone data files,
    /// indirectly calling DateTimeZoneBuilder. The following complex example defines the
    /// America/Los_Angeles time zone, with all historical transitions:
    /// </para>
    /// <para>
    /// <example>
    ///     DateTimeZone America_Los_Angeles = new DateTimeZoneBuilder()
    ///         .AddCutover(-2147483648, 'w', 1, 1, 0, false, 0)
    ///         .SetStandardOffset(-28378000)
    ///         .SetFixedSavings("LMT", 0)
    ///         .AddCutover(1883, 'w', 11, 18, 0, false, 43200000)
    ///         .SetStandardOffset(-28800000)
    ///         .AddRecurringSavings("PDT", 3600000, 1918, 1919, 'w',  3, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PST",       0, 1918, 1919, 'w', 10, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PWT", 3600000, 1942, 1942, 'w',  2,  9, 0, false, 7200000)
    ///         .AddRecurringSavings("PPT", 3600000, 1945, 1945, 'u',  8, 14, 0, false, 82800000)
    ///         .AddRecurringSavings("PST",       0, 1945, 1945, 'w',  9, 30, 0, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1948, 1948, 'w',  3, 14, 0, false, 7200000)
    ///         .AddRecurringSavings("PST",       0, 1949, 1949, 'w',  1,  1, 0, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1950, 1966, 'w',  4, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PST",       0, 1950, 1961, 'w',  9, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PST",       0, 1962, 1966, 'w', 10, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PST",       0, 1967, 2147483647, 'w', 10, -1, 7, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1967, 1973, 'w', 4, -1,  7, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1974, 1974, 'w', 1,  6,  0, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1975, 1975, 'w', 2, 23,  0, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1976, 1986, 'w', 4, -1,  7, false, 7200000)
    ///         .AddRecurringSavings("PDT", 3600000, 1987, 2147483647, 'w', 4, 1, 7, true, 7200000)
    ///         .ToDateTimeZone("America/Los_Angeles");
    /// </example>
    /// </para>
    /// <para>
    /// Original name: DateTimeZoneBuilder.
    /// </para>
    /// </remarks>
    internal sealed class DateTimeZoneBuilder
    {
        private readonly IList<ZoneRecurrenceCollection> ruleSets = new List<ZoneRecurrenceCollection>();

        /// <summary>
        /// Gets the last rule set in this builder. If there are currently no rule sets,
        /// one that spans all of time is created and returned.
        /// </summary>
        /// <value>The last rule set.</value>
        private ZoneRecurrenceCollection LastRuleSet
        {
            get
            {
                if (ruleSets.Count == 0)
                {
                    AddEndOfTimeRuleSet();
                }
                return ruleSets[ruleSets.Count - 1];
            }
        }

        /// <summary>
        /// Adds a cutover for added rules.
        /// </summary>
        /// <remarks>
        /// A cutover is a point where the standard offset from GMT/UTC changed. This occurs mostly
        /// pre-1900. The standard offset at the cutover defaults to 0.
        /// Call <see cref="DateTimeZoneBuilder.SetStandardOffset"/> afterwards to change it.
        /// </remarks>
        /// <param name="year">The year of cutover.</param>
        /// <param name="yearOffset">The offset into the year of the cutover.</param>
        /// <returns>This <see cref="DateTimeZoneBuilder"/> for chaining.</returns>
        public DateTimeZoneBuilder AddCutover(int year, ZoneYearOffset yearOffset)
        {
            Preconditions.CheckNotNull(yearOffset, "yearOffset");

            if (ruleSets.Count > 0)
            {
                LastRuleSet.SetUpperLimit(year, yearOffset);
            }
            AddEndOfTimeRuleSet();
            return this;
        }

        /// <summary>
        /// Sets the standard offset to use for newly added rules until the next cutover is added.
        /// </summary>
        /// <param name="standardOffset">The standard offset.</param>
        /// <returns>This <see cref="DateTimeZoneBuilder"/> for chaining.</returns>
        public DateTimeZoneBuilder SetStandardOffset(Offset standardOffset)
        {
            LastRuleSet.StandardOffset = standardOffset;
            return this;
        }

        /// <summary>
        /// Sets a fixed savings rule at the cutover.
        /// </summary>
        /// <param name="nameKey">The name key of new rule.</param>
        /// <param name="savings">The <see cref="Duration"/> to add to standard offset.</param>
        /// <returns>This <see cref="DateTimeZoneBuilder"/> for chaining.</returns>
        public DateTimeZoneBuilder SetFixedSavings(String nameKey, Offset savings)
        {
            LastRuleSet.SetFixedSavings(nameKey, savings);
            return this;
        }

        /// <summary>
        /// Adds a recurring daylight saving time rule.
        /// </summary>
        /// <param name="recurrence">The zone recurrence defining the recurrening savings.</param>
        /// <returns>This <see cref="DateTimeZoneBuilder"/> for chaining.</returns> 
        public DateTimeZoneBuilder AddRecurringSavings(ZoneRecurrence recurrence)
        {
            Preconditions.CheckNotNull(recurrence, "recurrence");
            if (recurrence.FromYear <= recurrence.ToYear)
            {
                LastRuleSet.AddRule(recurrence);
            }
            return this;
        }

        /// <summary>
        /// Processes all the rules and builds a DateTimeZone.
        /// </summary>
        /// <param name="zoneId">Time zone ID to assign</param>
        public DateTimeZone ToDateTimeZone(String zoneId)
        {
            Preconditions.CheckNotNull(zoneId, "zoneId");

            var transitions = new List<ZoneTransition>();
            DateTimeZone tailZone = null;
            Instant instant = Instant.BeforeMinValue;

            // TODO: See whether PartialZoneIntervalMap would help to tidy this up.
            int ruleSetCount = ruleSets.Count;
            bool tailZoneSeamValid = false;
            for (int i = 0; i < ruleSetCount; i++)
            {
                var ruleSet = ruleSets[i];
                var transitionIterator = ruleSet.Iterator(instant);
                ZoneTransition nextTransition = transitionIterator.First();
                if (nextTransition == null)
                {
                    continue;
                }
{'fraction_non_alphanumeric': 0.12863151083791652, 'fraction_numerical': 0.057845357489485605, 'mean_word_length': 1.951308000763796, 'pattern_counts': {'":': 0, '<': 133, '<?xml version=': 0, '>': 131, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4819066', 'n_tokens_mistral': 5132, 'n_tokens_neox': 4320, 'n_words': 1312}