/// <summary> /// Decodes a version value and writes the time to the console. /// </summary> /// <param name="scheme">The version scheme specification of the version value.</param> /// <param name="value">The version value to decode.</param> public static void ShowDecode(string scheme, string value) { SchemeData data = ParseTimeScheme(scheme); DateTime time = DateTime.MinValue; switch (data.SchemeType) { case SchemeType.Readable: time = DateTime.ParseExact(value, data.TimeFormat, CultureInfo.InvariantCulture).ToUniversalTime(); break; case SchemeType.DottedDecimal: time = DecodeDecimal(value, data.IntervalSeconds, data.BaseYear); break; case SchemeType.BaseEncoded: time = DecodeBase(value, data.Alphabet, data.IntervalSeconds, data.BaseYear); break; case SchemeType.Hours: time = DecodeHours(value, data.BaseYear, data.BaseMonth); break; } if (time == DateTime.MinValue) { throw new FormatException("Invalid revision ID value: " + value); } Console.WriteLine(time.ToString("yyyy-MM-dd HH:mm:ss") + " UTC"); Console.WriteLine(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss K")); }
/// <summary> /// Formats the current data for the specified time scheme. /// </summary> /// <param name="match"></param> /// <returns></returns> private string FormatTimeScheme(Match match) { string scheme = match.Value; SchemeData data = ParseTimeScheme(scheme); DateTimeOffset time; if (data.TimeSource == TimeSource.Build) { time = BuildTime; } else if (data.TimeSource == TimeSource.Author) { time = RevisionData.AuthorTime; } else { time = RevisionData.CommitTime; } switch (data.SchemeType) { case SchemeType.Readable: if (data.Utc) { time = time.UtcDateTime; } return(time.ToString(data.TimeFormat, CultureInfo.InvariantCulture)); case SchemeType.DottedDecimal: return(EncodeDecimal(time, data.IntervalSeconds, data.BaseYear)); case SchemeType.BaseEncoded: return(EncodeBase(time, data.Alphabet, data.IntervalSeconds, data.BaseYear, data.MinLength, data.UpperCase)); case SchemeType.Hours: return(EncodeHours(time, data.BaseYear, data.BaseMonth)); } // Return match unprocessed (should not happen) return(scheme); }
/// <summary> /// Parses a time-based version scheme specification. /// </summary> /// <param name="scheme">The version scheme specification to parse.</param> /// <returns>The parsed scheme data.</returns> private static SchemeData ParseTimeScheme(string scheme) { var data = new SchemeData(); Match match; string sourceStr = null; string timeComponents = null; string timeSeparator = null; int numberBase = 0; int intervalValue = 0; string intervalType = null; // Split scheme string if ((match = Regex.Match(scheme, @"^\{?([abc]):(u?)(ymd|hms|hm|h)([-.:]?)\}?$")).Success) { data.SchemeType = SchemeType.Readable; sourceStr = match.Groups[1].Value; data.Utc = match.Groups[2].Value == "u"; timeComponents = match.Groups[3].Value; timeSeparator = match.Groups[4].Value; } else if ((match = Regex.Match(scheme, @"^\{?([abc]):([0-9]+)([smhd]):([0-9]+)\}?$")).Success) { data.SchemeType = SchemeType.DottedDecimal; sourceStr = match.Groups[1].Value; intervalValue = int.Parse(match.Groups[2].Value); intervalType = match.Groups[3].Value; data.BaseYear = int.Parse(match.Groups[4].Value); } else if ((match = Regex.Match(scheme, @"^\{?([AaBbCc]):([0-9]+):([0-9]+)([smhd]):([0-9]+)(?::([0-9]+))?\}?$")).Success) { data.SchemeType = SchemeType.BaseEncoded; sourceStr = match.Groups[1].Value.ToLowerInvariant(); data.UpperCase = char.IsUpper(match.Groups[1].Value[0]); numberBase = int.Parse(match.Groups[2].Value); intervalValue = int.Parse(match.Groups[3].Value); intervalType = match.Groups[4].Value; data.BaseYear = int.Parse(match.Groups[5].Value); if (match.Groups[6].Success) { data.MinLength = int.Parse(match.Groups[6].Value); } } else if ((match = Regex.Match(scheme, @"^\{?([abc]):h:([0-9]{4})-([0-9]{2})\}?$")).Success) { data.SchemeType = SchemeType.Hours; sourceStr = match.Groups[1].Value; data.BaseYear = int.Parse(match.Groups[2].Value); data.BaseMonth = int.Parse(match.Groups[3].Value); data.IntervalSeconds = 3600; } #region Legacy formats else if ((match = Regex.Match(scheme, @"^\{?([Xx])min:([0-9]+)(?::([0-9]+))?\}?$")).Success) { data.SchemeType = SchemeType.BaseEncoded; sourceStr = "c"; data.UpperCase = match.Groups[1].Value == "X"; numberBase = 16; intervalValue = 1; intervalType = "m"; data.BaseYear = int.Parse(match.Groups[2].Value); if (match.Groups[3].Success) { data.MinLength = int.Parse(match.Groups[3].Value); } } else if ((match = Regex.Match(scheme, @"^\{?([Bb])(36)?min:([0-9]+)(?::([0-9]+))?\}?$")).Success) { data.SchemeType = SchemeType.BaseEncoded; sourceStr = "c"; data.UpperCase = match.Groups[1].Value == "B"; numberBase = match.Groups[2].Success ? 36 : 28; intervalValue = match.Groups[2].Success ? 10 : 20; intervalType = "m"; data.BaseYear = int.Parse(match.Groups[3].Value); if (match.Groups[4].Success) { data.MinLength = int.Parse(match.Groups[4].Value); } } else if ((match = Regex.Match(scheme, @"^\{?d(2)?min:([0-9]+)\}?$")).Success) { data.SchemeType = SchemeType.DottedDecimal; sourceStr = "c"; intervalValue = match.Groups[1].Success ? 2 : 15; intervalType = "m"; data.BaseYear = int.Parse(match.Groups[2].Value); } #endregion Legacy formats else { throw new FormatException("Invalid time scheme: " + scheme); } // Select time source switch (sourceStr) { case "a": data.TimeSource = TimeSource.Author; break; case "b": data.TimeSource = TimeSource.Build; break; case "c": data.TimeSource = TimeSource.Commit; break; default: throw new FormatException("Invalid time source specification: " + sourceStr); } if (data.SchemeType == SchemeType.Readable) { // Select time format from components and separator switch (timeComponents + timeSeparator) { case "ymd": data.TimeFormat = "yyyyMMdd"; break; case "ymd-": data.TimeFormat = "yyyy-MM-dd"; break; case "ymd.": data.TimeFormat = "yyyy.MM.dd"; break; case "hms": data.TimeFormat = "HHmmss"; break; case "hms-": data.TimeFormat = "HH-mm-ss"; break; case "hms.": data.TimeFormat = "HH.mm.ss"; break; case "hms:": data.TimeFormat = "HH:mm:ss"; break; case "hm": data.TimeFormat = "HHmm"; break; case "hm-": data.TimeFormat = "HH-mm"; break; case "hm.": data.TimeFormat = "HH.mm"; break; case "hm:": data.TimeFormat = "HH:mm"; break; case "h": data.TimeFormat = "HH"; break; default: throw new FormatException("Invalid time components and separator: " + timeComponents + timeSeparator); } } if (data.SchemeType == SchemeType.DottedDecimal || data.SchemeType == SchemeType.BaseEncoded) { // Convert interval specification to seconds data.IntervalSeconds = intervalValue; switch (intervalType) { case "s": break; case "m": data.IntervalSeconds *= 60; break; case "h": data.IntervalSeconds *= 60 * 60; break; case "d": data.IntervalSeconds *= 24 * 60 * 60; break; } } if (data.SchemeType == SchemeType.BaseEncoded) { // Select alphabet for number base if (numberBase < 2 || numberBase > 36) { throw new FormatException("Invalid number base: " + numberBase); } else if (numberBase == 28) { data.Alphabet = base28Alphabet; } else { data.Alphabet = new char[numberBase]; Array.Copy(base36Alphabet, data.Alphabet, numberBase); } } return(data); }