/* ----------------------------------------------------- * * File: TimeSpanParser.cs * Author: sven.kopacz@keysight.com * Created: 21.10.2016 * * ----------------------------------------------------- * * Description: See class summary * * ----------------------------------------------------- */ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; namespace OpenTap { /// /// A time span parser can parse a text input (which would typically be a user input) for /// timespan information like "1 hour 2 minutes" or "1 week 250ms" and sum them all up into a /// TimeSpan. Currently only English units are supported. /// public static class TimeSpanParser { /// /// Parses a string for TimeSpan Information like "1 week 2 days" or "2h12m3s". Currently only /// English units are accepted. Note that "month" is not a valid unit, since it does not /// correspond to a determined number of days and so is "year" because of leap years. However you /// can have both units parsed to hardwired values using the parameter. /// Note that Microseconds are accepted, but the .NET TimeSpan only resolves milliseconds. Thus only /// multiples of thousands of Microseconds will affect the result, e.g. "2000µs", when reformatted, will /// evaluate to "2ms" whereas "200µs" will simply return TimeSpan.Zero. /// /// /// Thrown when one or more arguments have unsupported or /// illegal values. /// Thrown when a unit is unknown. /// /// The input string. /// The acceptable number styles. /// The culture for number parsing. If omitted, DefaultThreadCurrentCulture is chosen. /// true to accept months (as 30 days) and years (as 356 /// days). /// /// A TimeSpan. public static TimeSpan Parse(string input, NumberStyles numberParseStyles = NumberStyles.Any, CultureInfo numberParseCulture = null, bool acceptIndeterminate = false) { TimeSpan result = TimeSpan.Zero; var csplit = input.Split(':'); if (csplit.Length > 1) { // Parse superbrief format: (dd:)(HH:)mm:ss(.fff) // TimeSpan.Parse does not work, because it misinterprets 24:0:0 as 24 days. if (csplit.Length > 4) throw new FormatException("Invalid TimeSpan format."); double[] multipliers = new double[] { 24 * 60 * 60, 60 * 60, 60, 1 }; var last = csplit[csplit.Length - 1]; double frac = 0.0; if (last.Contains('.')) { var fsplit = last.Split('.'); if (fsplit.Length == 2) { csplit[csplit.Length - 1] = fsplit[0]; fsplit[1] = fsplit[1].Trim(); int decimals = fsplit[1].Length; frac = double.Parse(fsplit[1]) / Math.Pow(10, decimals); } } int j = multipliers.Length - 1; for(int i = csplit.Length - 1; i >= 0; i--) { frac += multipliers[j] * int.Parse(csplit[i]); j--; } return Time.FromSeconds(frac); } List> entries = NumberUnitSplitter.Split(input, numberParseStyles, numberParseCulture); foreach (Tuple valueUnitPair in entries) { switch (valueUnitPair.Item2.ToLower()) { case "µs": case "µsec": case "µsecond": case "µseconds": case "microsec": case "microsecond": case "microseconds": result += Time.FromSeconds(valueUnitPair.Item1 / 1E6); break; case "ms": case "millisec": case "millisecond": case "milliseconds": result += Time.FromSeconds(valueUnitPair.Item1 / 1e3); break; case "": case "s": case "ss": case "sec": case "second": case "seconds": result += Time.FromSeconds(valueUnitPair.Item1); break; case "m": case "mm": case "min": case "minute": case "minutes": result += Time.FromSeconds(valueUnitPair.Item1 * 60); break; case "h": case "hh": case "hour": case "hours": result += Time.FromSeconds(valueUnitPair.Item1 * 60 * 60); break; case "d": case "dd": case "day": case "days": result += Time.FromSeconds(valueUnitPair.Item1 * 60 * 60 * 24); break; case "w": case "week": case "weeks": result += Time.FromSeconds(valueUnitPair.Item1 * 60 * 60 * 24 * 7); break; case "month": case "months": if (acceptIndeterminate) result += Time.FromSeconds(valueUnitPair.Item1 * 60 * 60 * 24 * 30); else throw new ArgumentException("Unit 'month' is indeterminate"); break; case "y": case "year": case "years": if (acceptIndeterminate) result += Time.FromSeconds(valueUnitPair.Item1 * 60 * 60 * 24 * 365); else throw new ArgumentException("Unit 'year' is indeterminate"); break; default: throw new FormatException("Unknown Unit: '" + valueUnitPair.Item2 + "'"); } } return result; } } }