using System; using System.IO; using System.Net; using System.Text; using System.Xml; using System.Xml.Linq; using System.Xml.Schema; internal class Program { private static string app = AppDomain.CurrentDomain.FriendlyName; private static TextWriter stderr = Console.Error; private static TextWriter stdout = Console.Out; private static void usage() { stderr.WriteLine("Usage:"); stderr.WriteLine(app + " "); Environment.Exit(1); } private static bool writeZoneMapC(string path) { string fname = "TimeZoneNameMap"; string fpath = Path.Combine(path, fname + "_static.h"); using (StreamWriter fs = new StreamWriter(fpath)) { fs.WriteLine("/* Automatically generated by " + app + " */"); fs.WriteLine(""); fs.WriteLine("#include \"" + fname + ".h\""); fs.WriteLine(""); fs.WriteLine("static const " + fname + "Entry " + fname + "[] ={"); bool first = true; foreach (System.TimeZoneInfo tz in System.TimeZoneInfo.GetSystemTimeZones()) { string iana; System.TimeZoneInfo.TryConvertWindowsIdToIanaId(tz.Id, out iana); StringBuilder sb = new StringBuilder(); if (!first) sb.Append(","); first = false; sb.Append("{ \""); sb.Append(tz.Id); sb.Append("\", \""); sb.Append(tz.StandardName); sb.Append("\", \""); sb.Append(tz.DisplayName); sb.Append("\", \""); sb.Append(tz.DaylightName); sb.Append("\", \""); sb.Append(iana); sb.Append("\" }"); fs.WriteLine(sb.ToString()); } fs.WriteLine("};"); fs.WriteLine(""); fs.WriteLine("static const size_t " + fname + "Size = ARRAYSIZE(" + fname + ");"); } return true; } private static bool writeZoneMapJSON(string path) { string fname = "TimeZoneNameMap"; string fpath = Path.Combine(path, fname + ".json"); using (StreamWriter fs = new StreamWriter(fpath)) { fs.WriteLine("{"); fs.WriteLine("\"TimeZoneNameMap\": ["); bool first = true; foreach (System.TimeZoneInfo tz in System.TimeZoneInfo.GetSystemTimeZones()) { string iana; System.TimeZoneInfo.TryConvertWindowsIdToIanaId(tz.Id, out iana); StringBuilder sb = new StringBuilder(); if (!first) sb.Append(","); first = false; sb.Append("{ "); sb.Append("\"Id\": \""); sb.Append(tz.Id); sb.Append("\", \"StandardName\": \""); sb.Append(tz.StandardName); sb.Append("\", \"DisplayName\": \""); sb.Append(tz.DisplayName); sb.Append("\", \"DaylightName\": \""); sb.Append(tz.DaylightName); sb.Append("\", \"Iana\": \""); sb.Append(iana); sb.Append("\" }"); fs.WriteLine(sb.ToString()); } fs.WriteLine("]"); fs.WriteLine("}"); } return true; } private static bool writeZoneMap(string path) { if (!writeZoneMapC(path)) return false; if (!writeZoneMapJSON(path)) return false; return true; } private static void onValidation(object sender, ValidationEventArgs e) { switch (e.Severity) { case XmlSeverityType.Warning: case XmlSeverityType.Error: stderr.WriteLine(e.ToString()); break; default: break; } } private static bool updateWindowsIanaMap(string path) { string url = "https://raw.githubusercontent.com/unicode-org/cldr/main/common/supplemental/windowsZones.xml"; string fname = "WindowsZones"; string fpath = Path.Combine(path, fname + ".c"); XmlDocument doc = new XmlDocument(); doc.Load(url); stdout.WriteLine("Downloaded and parsed XML from '" + url + "'"); ValidationEventHandler handler = new ValidationEventHandler(onValidation); // doc.Validate(handler); XmlNodeList versions = doc.SelectNodes("//supplementalData/version"); XmlNodeList zones = doc.SelectNodes("//supplementalData/windowsZones/mapTimezones"); XmlNodeList mzones = doc.SelectNodes("//supplementalData/windowsZones/mapTimezones/mapZone"); using (StreamWriter fs = new StreamWriter(fpath)) { fs.WriteLine("/* Automatically generated by " + app); fs.WriteLine(" *"); fs.WriteLine(" * url " + url); foreach (XmlNode version in versions) { XmlNode nr = version.Attributes.GetNamedItem("number"); fs.WriteLine(" * version: " + nr.InnerText); } foreach (XmlNode node in zones) { XmlNode over = node.Attributes.GetNamedItem("otherVersion"); XmlNode tver = node.Attributes.GetNamedItem("typeVersion"); fs.WriteLine(" * mapTimezones: otherVersion: " + over.InnerText + ", typeVersion: " + tver.InnerText); } fs.WriteLine(" */"); fs.WriteLine(""); fs.WriteLine("#include \"" + fname + ".h\""); fs.WriteLine(""); fs.WriteLine("const WINDOWS_TZID_ENTRY " + fname + "[] = {"); foreach (XmlNode mzone in mzones) { XmlAttributeCollection attrs = mzone.Attributes; XmlNode wzid = attrs.GetNamedItem("other"); XmlNode territory = attrs.GetNamedItem("territory"); XmlNode iana = attrs.GetNamedItem("type"); fs.WriteLine("\t{ \"" + iana.InnerText + "\", \"" + wzid.InnerText + "\" }, // " + territory.InnerText); } fs.WriteLine("};"); fs.WriteLine(""); fs.WriteLine("const size_t " + fname + "NrElements = ARRAYSIZE(" + fname + ");"); } stdout.WriteLine("Finished update"); return true; } private static void Main(string[] args) { try { if (args.Length == 0) { stderr.WriteLine("Missing arguments!"); usage(); } DirectoryInfo info = new DirectoryInfo(args[0]); if (!info.Exists) { stderr.WriteLine("Path '" + info.FullName + "' does not exist"); usage(); } if (!writeZoneMap(info.FullName)) return; updateWindowsIanaMap(info.FullName); } catch (Exception e) { stderr.WriteLine(e.ToString()); Environment.Exit(-23); } } }