Beispiel #1
0
        /// <summary>
        /// Parses a single line of an TZDB zone info file.
        /// </summary>
        /// <remarks>
        /// <para>
        /// TZDB files have a simple line based structure. Each line defines one item. Comments
        /// start with a hash or pound sign (#) and continue to the end of the line. Blank lines are
        /// ignored. Of the remaining there are four line types which are determined by the first
        /// keyword on the line.
        /// </para>
        /// <para>
        /// A line beginning with the keyword <c>Link</c> defines an alias between one time zone and
        /// another. Both time zones use the same definition but have different names.
        /// </para>
        /// <para>
        /// A line beginning with the keyword <c>Rule</c> defines a daylight savings time
        /// calculation rule.
        /// </para>
        /// <para>
        /// A line beginning with the keyword <c>Zone</c> defines a time zone.
        /// </para>
        /// <para>
        /// A line beginning with leading whitespace (an empty keyword) defines another part of the
        /// preceeding time zone. As many lines as necessary to define the time zone can be listed,
        /// but they must all be together and only the first line can have a name.
        /// </para>
        /// </remarks>
        /// <param name="line">The line to parse.</param>
        /// <param name="database">The database to fill.</param>
        /// <return>The zone name just parsed, if any - so that it can be passed into the next call.</return>
        private string ParseLine(string line, string previousZone, TzdbDatabase database)
        {
            // Trim end-of-line comments
            int index = line.IndexOf("#", StringComparison.Ordinal);

            if (index >= 0)
            {
                line = line.Substring(0, index);
            }
            line = line.TrimEnd();
            if (line.Length == 0)
            {
                // We can still continue with the previous zone
                return(previousZone);
            }

            // Okay, everything left in the line should be "real" now.
            var tokens  = Tokens.Tokenize(line);
            var keyword = NextString(tokens, "Keyword");

            switch (keyword)
            {
            case KeywordRule:
                database.AddRule(ParseRule(tokens));
                return(null);

            case KeywordLink:
                var alias = ParseLink(tokens);
                database.AddAlias(alias.Item1, alias.Item2);
                return(null);

            case KeywordZone:
                var name = NextString(tokens, "GetName");
                database.AddZone(ParseZone(name, tokens));
                return(name);

            default:
                if (string.IsNullOrEmpty(keyword))
                {
                    if (previousZone == null)
                    {
                        throw new InvalidDataException("Zone continuation provided with no previous zone line");
                    }
                    database.AddZone(ParseZone(previousZone, tokens));
                    return(previousZone);
                }
                else
                {
                    throw new InvalidDataException($"Unexpected zone database keyword: {keyword}");
                }
            }
        }
Beispiel #2
0
        /// <summary>
        ///   Parses a single line of an TZDB zone info file.
        /// </summary>
        /// <remarks>
        ///   <para>
        ///     TZDB files have a simple line based structure. Each line defines one item. Comments
        ///     start with a hash or pound sign (#) and continue to the end of the line. Blank lines are
        ///     ignored. Of the remaining there are four line types which are determined by the first
        ///     keyword on the line.
        ///   </para>
        ///   <para>
        ///     A line beginning with the keyword <c>Link</c> defines an alias between one time zone and
        ///     another. Both time zones use the same definition but have different names.
        ///   </para>
        ///   <para>
        ///     A line beginning with the keyword <c>Rule</c> defines a daylight savings time
        ///     calculation rule.
        ///   </para>
        ///   <para>
        ///     A line beginning with the keyword <c>Zone</c> defines a time zone.
        ///   </para>
        ///   <para>
        ///     A line beginning with leading whitespace (an empty keyword) defines another part of the
        ///     preceeding time zone. As many lines as necessary to define the time zone can be listed,
        ///     but they must all be together and only the first line can have a name.
        ///   </para>
        /// </remarks>
        /// <param name="line">The line to parse.</param>
        /// <param name="database">The database to fill.</param>
        internal void ParseLine(string line, TzdbDatabase database)
        {
            int index = line.IndexOf("#", StringComparison.Ordinal);

            if (index == 0)
            {
                return;
            }
            if (index > 0)
            {
                line = line.Substring(0, index - 1);
            }
            line = line.TrimEnd();
            if (line.Length == 0)
            {
                return;
            }
            var tokens  = Tokens.Tokenize(line);
            var keyword = NextString(tokens, "Keyword");

            switch (keyword)
            {
            case KeywordRule:
                database.AddRule(ParseRule(tokens));
                break;

            case KeywordLink:
                database.AddAlias(ParseLink(tokens));
                break;

            case KeywordZone:
                var name      = NextString(tokens, "GetName");
                var namedZone = ParseZone(name, tokens);
                database.AddZone(namedZone);
                break;

            default:
                if (string.IsNullOrEmpty(keyword))
                {
                    var zone = ParseZone(string.Empty, tokens);
                    database.AddZone(zone);
                }
                else
                {
                    throw new InvalidDataException("Unexpected zone database keyword: " + keyword);
                }
                break;
            }
        }