private static void ProcessDirectory(string directory, Options options, TextWriter writer) { var pairs = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories) .Select(file => new { file, id = file.StartsWith(directory) ? file.Substring(directory.Length).Replace("\\", "/").TrimStart('/') : file }) .Where(pair => pair.id != "zone.tab") .OrderBy(pair => pair.id, StringComparer.Ordinal) .ToList(); if (options.ZoneId != null) { var pair = pairs.FirstOrDefault(p => p.id == options.ZoneId); if (pair == null) { throw new Exception($"Unknown zone ID: {options.ZoneId}"); } ProcessFile(pair.id, pair.file, options, writer); } else { foreach (var pair in pairs) { ProcessFile(pair.id, pair.file, options, writer); } } }
static int Main(string[] args) { Options options = new Options(); ICommandLineParser parser = new CommandLineParser(new CommandLineParserSettings(Console.Error) { MutuallyExclusive = true }); if (!parser.ParseArguments(args, options)) { return 1; } using (var output = options.OutputFile == null ? Console.Out : File.CreateText(options.OutputFile)) { var file = options.Source; if (File.Exists(file)) { ProcessFile(file, file, options, output); } else if (Directory.Exists(file)) { var writer = new StringWriter(); ProcessDirectory(file, options, writer); var text = writer.ToString(); WriteHeaders(text, options, output); output.Write(text); } else { Console.Error.WriteLine($"File not found: {file}"); return 1; } } return 0; }
private static void ProcessFile(string id, string file, Options options, TextWriter writer) { writer.Write($"{id}\n"); using (var stream = File.OpenRead(file)) { var zone = ZoneFile.FromStream(stream); var transitions = zone.GetTransitions(options); foreach (var transition in transitions) { writer.Write($"{transition}\n"); } } writer.Write("\n"); }
private static void WriteHeaders(string text, Options options, TextWriter writer) { if (options.Version != null) { writer.Write($"Version: {options.Version}\n"); } using (var hashAlgorithm = SHA256.Create()) { var bytes = Encoding.UTF8.GetBytes(text); var hash = hashAlgorithm.ComputeHash(bytes); var hashText = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); writer.Write($"Body-SHA-256: {hashText}\n"); } writer.Write("Format: tzvalidate-0.1\n"); writer.Write($"Range: {options.FromYear ?? 1}-{options.ToYear}\n"); writer.Write($"Generator: {typeof(Program).GetTypeInfo().Assembly.GetName().Name}\n"); writer.Write($"GeneratorUrl: https://github.com/nodatime/tzvalidate\n"); writer.Write("\n"); }
internal IEnumerable<Transition> GetTransitions(Options options) { if (transitionTypes.Length == 0) { throw new Exception("No transition types, so unable to get any data at all"); } var lowerBound = (long) (DateTimeOffset.MinValue - UnixEpoch).TotalSeconds; var transitions = transitionTimestamps.Zip(transitionTypeIndexes, (tt, tti) => new { timestamp = tt < lowerBound ? (DateTimeOffset?) null : UnixEpoch.AddSeconds(tt), type = transitionTypes[tti] }) .Select(pair => new Transition(pair.timestamp, TimeSpan.FromSeconds(pair.type.Offset), pair.type.IsDst, pair.type.Abbreviation)) .ToList(); if (options.FakeInitialTransition && transitions.Count > 0 && transitions[0].Instant != null) { // Note: in some very odd cases (e.g. tzdata94f, MET) the first transition is into standard time. // We'll still add one for the big bang here, and let the next part de-dupe. // Work out the initial type according to the localtime man page: // - First standard type (by transition type array) if there are any // - First type if not var initialType = transitionTypes.FirstOrDefault(t => !t.IsDst) ?? transitionTypes[0]; transitions.Insert(0, new Transition(null, TimeSpan.FromSeconds(initialType.Offset), initialType.IsDst, initialType.Abbreviation)); } Transition previousTransition = null; foreach (var transition in transitions) { // There may be types which only differ in ways we don't care about, e.g. whether // the transition is specified as wall, standard or UTC. if (previousTransition?.Abbreviation == transition.Abbreviation && previousTransition.Offset == transition.Offset && previousTransition.IsDaylight == transition.IsDaylight) { continue; } previousTransition = transition; if (transition.Instant != null) { var year = transition.Instant.Value.Year; if (options.FromYear != null && year < options.FromYear.Value) { continue; } if (year >= options.ToYear) { yield break; } } yield return transition; } }