/// <summary> /// Compiles the specified files and generates the output resource file. /// </summary> /// <param name="fileList">The enumeration of <see cref="FileInfo" /> objects.</param> /// <param name="output">The destination <see cref="DirectoryInfo" /> object.</param> /// <returns></returns> internal int Compile(IEnumerable<FileInfo> fileList, ResourceOutput output) { var database = new TzdbDatabase(); ParseAllFiles(fileList, database); GenerateDateTimeZones(database, output); LogCounts(database); return 0; }
public void Parse_twoZonesTwoRule() { const string text = "# A comment\n" + "Rule US 1987 2006 - Apr Sun>=1 2:00 1:00 D\n" + "Rule US 2007 max - Mar Sun>=8 2:00 1:00 D\n" + "Zone PST 2:00 US P%sT # An end of line comment\n" + " 3:00 - P%sT\n" + " 4:00 - P%sT\n" + "Zone EST 2:00 US E%sT # An end of line comment\n" + " 3:00 - E%sT\n"; var reader = new StringReader(text); var database = new TzdbDatabase(); Parser.Parse(reader, database); ValidateCounts(database, 1, 2, 0); Assert.AreEqual(2, database.Zones[0].Count, "Zones in set " + database.Zones[0].Name); Assert.AreEqual(3, database.Zones[1].Count, "Zones in set " + database.Zones[1].Name); }
public void Parse_twoZones() { const string text = "# A comment\n" + "Zone PST 2:00 US P%sT # An end of line comment\n" + " 3:00 - P%sT\n" + " 4:00 - P%sT\n" + "Zone EST 2:00 US E%sT # An end of line comment\n" + " 3:00 - E%sT\n"; var reader = new StringReader(text); var database = new TzdbDatabase(); Parser.Parse(reader, database); ValidateCounts(database, 0, 2, 0); Assert.AreEqual(2, database.Zones[0].Count, "Zones in set " + database.Zones[0].Name); Assert.AreEqual(3, database.Zones[1].Count, "Zones in set " + database.Zones[1].Name); }
public void Parse_twoLinks() { const string text = "# First line must be a comment\n" + "Link from to\n" + "Link target source\n"; var reader = new StringReader(text); var database = new TzdbDatabase(); Parser.Parse(reader, database); ValidateCounts(database, 0, 0, 2); }
public void Parse_twoLines() { const string text = "# A comment\n" + "Zone PST 2:00 US P%sT\n"; var reader = new StringReader(text); var database = new TzdbDatabase(); Parser.Parse(reader, database); ValidateCounts(database, 0, 1, 0); Assert.AreEqual(1, database.Zones[0].Count, "Zones in set"); }
public void Parse_emptyStream() { var reader = new StringReader(string.Empty); var database = new TzdbDatabase(); Parser.Parse(reader, database); ValidateCounts(database, 0, 0, 0); }
/// <summary> /// Writes various informational counts to the log. /// </summary> /// <param name="database">The database to query for the counts.</param> private void LogCounts(TzdbDatabase database) { log.Info("======================================="); log.Info("Rule sets: {0:D}", database.Rules.Count); log.Info("Zones: {0:D}", database.Zones.Count); log.Info("Aliases: {0:D}", database.Aliases.Count); log.Info("======================================="); }
/// <summary> /// Parses the TZDB time zone info file from the given reader and merges its information /// with the given database. The reader is not closed or disposed. /// </summary> /// <param name="reader">The reader to read.</param> /// <param name="database">The database to fill.</param> public void Parse(TextReader reader, TzdbDatabase database) { Log.LineNumber = 1; try { for (var line = reader.ReadLine(); line != null; line = reader.ReadLine()) { try { if (Log.LineNumber == 1) { if (!line.StartsWith("# ", StringComparison.Ordinal)) { return; } } else { ParseLine(line, database); } } catch (ParseException) { // Nothing to do } catch (Exception e) { Log.Error("Exception {0} occurred: {1}", e.GetType().Name, e.Message); throw; } Log.LineNumber++; } } finally { Log.LineNumber = -1; } }
public void ParseLine_link() { const string line = "Link from to"; var database = new TzdbDatabase(); Parser.ParseLine(line, database); ValidateCounts(database, 0, 0, 1); }
public void ParseLine_emptyString() { var database = new TzdbDatabase(); Parser.ParseLine(string.Empty, database); ValidateCounts(database, 0, 0, 0); }
public void ParseLine_commentWithLeadingWhitespace() { const string line = " # Comment"; var database = new TzdbDatabase(); Parser.ParseLine(line, database); ValidateCounts(database, 0, 0, 0); }
/// <summary> /// Parses the file defined by the given <see cref="FileInfo" />. /// </summary> /// <remarks> /// Currently this compiler only handles files in the Olson (TZDB) zone info format. /// </remarks> /// <param name="file">The file to parse.</param> /// <param name="database">The <see cref="TzdbDatabase" /> where the parsed data is placed.</param> internal void ParseFile(FileInfo file, TzdbDatabase database) { log.FileName = file.Name; try { using (FileStream stream = file.OpenRead()) { tzdbParser.Parse(stream, database); } } finally { log.FileName = null; } }
/// <summary> /// Parses all of the given files. /// </summary> /// <param name="files">The <see cref="IEnumerable{T}" /> of <see cref="FileInfo" /> objects.</param> /// <param name="database">The <see cref="TzdbDatabase" /> where the parsed data is placed.</param> private void ParseAllFiles(IEnumerable<FileInfo> files, TzdbDatabase database) { foreach (var file in files) { log.Info("Parsing file {0} . . .", file.Name); ParseFile(file, database); } }
private static void ValidateCounts(TzdbDatabase database, int ruleSets, int zoneLists, int links) { Assert.AreEqual(ruleSets, database.Rules.Count, "Rules"); Assert.AreEqual(zoneLists, database.Zones.Count, "Zones"); Assert.AreEqual(links, database.Aliases.Count, "Links"); }
public void ParseLine_whiteSpace() { const string line = " \t\t\n"; var database = new TzdbDatabase(); Parser.ParseLine(line, database); ValidateCounts(database, 0, 0, 0); }
/// <summary> /// Parses the TZDB time zone info file from the given stream and merges its information /// with the given database. The stream is not closed or disposed. /// </summary> /// <param name="input">The stream input to parse.</param> /// <param name="database">The database to fill.</param> public void Parse(Stream input, TzdbDatabase database) { Parse(new StreamReader(input, true), database); }
public void ParseLine_zonePlus() { const string line = "Zone PST 2:00 US P%sT"; var database = new TzdbDatabase(); Parser.ParseLine(line, database); const string line2 = " 3:00 US P%sT"; Parser.ParseLine(line2, database); ValidateCounts(database, 0, 1, 0); Assert.AreEqual(2, database.Zones[0].Count, "Zones in set"); }
/// <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 { Error("Unexpected zone database keyword: {0}", keyword); } break; } }
/// <summary> /// Generates the date time zones from the given parsed time zone information object. /// </summary> /// <remarks> /// <para> /// First we go through the list of time zones and generate an <see cref="DateTimeZone" /> /// object for each one. We create a mapping between the time zone name and itself (for /// writing out later). Then we write out the time zone as a resource to the current writer. /// </para> /// <para> /// Second we go through all of the alias mappings and find the actual time zone that they /// map to. we do this by redirecting through aliases until there are no more aliases. This /// allows for on alias to refer to another. We add the alias mapping to the time zone /// mapping created in the first step. When done, we write out the entire mapping as a /// resource. The keys of the mapping can be used as the list of valid time zone ids /// supported by this resource file. /// </para> /// </remarks> /// <param name="database">The database of parsed zone info records.</param> /// <param name="output">The output file <see cref="ResourceOutput" />.</param> private static void GenerateDateTimeZones(TzdbDatabase database, ResourceOutput output) { var timeZoneMap = new Dictionary<string, string>(); foreach (var zoneList in database.Zones) { var timeZone = CreateTimeZone(zoneList, database.Rules); timeZoneMap.Add(timeZone.Id, timeZone.Id); output.WriteTimeZone(timeZone.Id, timeZone); } foreach (var key in database.Aliases.Keys) { var value = database.Aliases[key]; while (database.Aliases.ContainsKey(value)) { value = database.Aliases[value]; } timeZoneMap.Add(key, value); } output.WriteDictionary(DateTimeZoneResourceProvider.IdMapKey, timeZoneMap); }