/// <summary>Return the time span as a nice looking string</summary> public static string ToPrettyString(this TimeSpan ts, bool short_format = true, ETimeUnits min_unit = ETimeUnits.Seconds, ETimeUnits max_unit = ETimeUnits.Days, bool leading_zeros = false, bool trailing_zeros = true) { // Get each of the parts var y = max_unit > ETimeUnits.Years ? ts.Years() : (long)ts.TotalYears(); var w = max_unit > ETimeUnits.Weeks ? ts.Weeks() : (long)ts.TotalWeeks(); var d = max_unit > ETimeUnits.Days ? ts.Days() : (long)ts.TotalDays; var h = max_unit > ETimeUnits.Hours ? ts.Hours : (long)ts.TotalHours; var m = max_unit > ETimeUnits.Minutes ? ts.Minutes : (long)ts.TotalMinutes; var s = max_unit > ETimeUnits.Seconds ? ts.Seconds : (long)ts.TotalSeconds; var f = max_unit > ETimeUnits.Milliseconds ? ts.Milliseconds : (long)ts.TotalMilliseconds; var unit_y = short_format ? "y" : y != 1 ? "years" : "year"; var unit_w = short_format ? "w" : w != 1 ? "weeks" : "week"; var unit_d = short_format ? "d" : d != 1 ? "days" : "day"; var unit_h = short_format ? "h" : h != 1 ? "hrs" : "hr"; var unit_m = short_format ? "m" : m != 1 ? "mins" : "min"; var unit_s = short_format ? "s" : s != 1 ? "secs" : "sec"; var unit_f = short_format ? "ms" : f != 1 ? "msecs" : "msec"; // Show if: (unit in range [min_unit,max_unit]) and ((value != 0) or (showing leading zeros) or (showing trailing zeros and high unit is shown) or (unit = min_unit and 'ts' is smaller than unit)) var show_y = (min_unit <= ETimeUnits.Years && ETimeUnits.Years <= max_unit) && (y != 0 || leading_zeros || (min_unit == ETimeUnits.Years && (long)(ts.TotalDays / 365.0) == 0)); var show_w = (min_unit <= ETimeUnits.Weeks && ETimeUnits.Weeks <= max_unit) && (w != 0 || leading_zeros || (min_unit == ETimeUnits.Weeks && (long)(ts.TotalDays / 7.0) == 0)); var show_d = (min_unit <= ETimeUnits.Days && ETimeUnits.Days <= max_unit) && (d != 0 || leading_zeros || (min_unit == ETimeUnits.Days && (long)ts.TotalDays == 0)); var show_h = (min_unit <= ETimeUnits.Hours && ETimeUnits.Hours <= max_unit) && (h != 0 || leading_zeros || (trailing_zeros && show_d) || (min_unit == ETimeUnits.Hours && (long)ts.TotalHours == 0)); var show_m = (min_unit <= ETimeUnits.Minutes && ETimeUnits.Minutes <= max_unit) && (m != 0 || leading_zeros || (trailing_zeros && show_h) || (min_unit == ETimeUnits.Minutes && (long)ts.TotalMinutes == 0)); var show_s = (min_unit <= ETimeUnits.Seconds && ETimeUnits.Seconds <= max_unit) && (s != 0 || leading_zeros || (trailing_zeros && show_m) || (min_unit == ETimeUnits.Seconds && (long)ts.TotalSeconds == 0)); var show_f = (min_unit <= ETimeUnits.Milliseconds && ETimeUnits.Milliseconds <= max_unit) && (f != 0 || leading_zeros || (trailing_zeros && show_s) || (min_unit == ETimeUnits.Milliseconds && (long)ts.TotalMilliseconds == 0)); var sb = new StringBuilder(); if (show_y) { sb.Append($"{y}{unit_y} "); } if (show_w) { sb.Append($"{w}{unit_w} "); } if (show_d) { sb.Append($"{d}{unit_d} "); } if (show_h) { sb.Append($"{h}{unit_h} "); } if (show_m) { sb.Append($"{m}{unit_m} "); } if (show_s) { sb.Append($"{s}{unit_s} "); } if (show_f) { sb.Append($"{f}{unit_f} "); } return(sb.ToString().TrimEnd(' ')); }
/// <summary> /// Parse expressions representing time values<para/> /// E.g. 10 (seconds implied), 2:30 (2 minutes, 30 seconds), 1:3:10 (1 hour, 3 mins, 10 secs),<para/> /// 10sec, 2min, 1 hr, 2 min 30sec, 2.5 min (2:30), 30s 10m 1h (01:10:30)</summary> public static TimeSpan?TryParseExpr(string val, ETimeUnits default_units = ETimeUnits.Seconds) { try { // If the value parses as a double, treat the number as 'default_units' if (double.TryParse(val, out var x)) { switch (default_units) { default: throw new Exception($"unknown time units {default_units}"); case ETimeUnits.Milliseconds: return(TimeSpan.FromMilliseconds(x)); case ETimeUnits.Seconds: return(TimeSpan.FromSeconds(x)); case ETimeUnits.Minutes: return(TimeSpan.FromMinutes(x)); case ETimeUnits.Hours: return(TimeSpan.FromHours(x)); case ETimeUnits.Days: return(TimeSpan.FromDays(x)); case ETimeUnits.Weeks: return(TimeSpan.FromDays(x * 7)); case ETimeUnits.Years: return(TimeSpan.FromDays(x * 365)); } } // Try standard TimeSpan expressions var ts = TimeSpan.Zero; if (TimeSpan.TryParse(val, out ts)) { return(ts); } const string num_patn = @"(?:^|\s+)" + // Start of the string or preceded by whitespace @"(" + // @"[+-]?" + // Optional + or - sign @"(?:" + // @"(?:\d*\.\d+)" + // ##.### or .#### @"|(?:\d+)" + // or #### @")" + // @")\s*" + // trailed by optional whitespace @"({0})" + // time unit patterns @"(?:$|\s)"; // End of the string or followed by whitespace // Extract parts var msec = val.SubstringRegex(string.Format(num_patn, "msecs|msec|ms"), RegexOptions.IgnoreCase).FirstOrDefault(); // A decimal value followed by 'msecs', 'msec', or 'ms' var sec = val.SubstringRegex(string.Format(num_patn, "secs|sec|s"), RegexOptions.IgnoreCase).FirstOrDefault(); // A decimal value followed by 'secs', 'sec', or 's' var min = val.SubstringRegex(string.Format(num_patn, "mins|min|m"), RegexOptions.IgnoreCase).FirstOrDefault(); // A decimal value followed by 'mins', 'min', or 'm' var hrs = val.SubstringRegex(string.Format(num_patn, "hrs|hr|h"), RegexOptions.IgnoreCase).FirstOrDefault(); // A decimal value followed by 'hrs', 'hr', 'h' var days = val.SubstringRegex(string.Format(num_patn, "days|day|d"), RegexOptions.IgnoreCase).FirstOrDefault(); // A decimal value followed by 'days', 'day', 'd' var weeks = val.SubstringRegex(string.Format(num_patn, "weeks|week|w"), RegexOptions.IgnoreCase).FirstOrDefault(); // A decimal value followed by 'weeks', 'week', 'w' var years = val.SubstringRegex(string.Format(num_patn, "years|year|y"), RegexOptions.IgnoreCase).FirstOrDefault(); // A decimal value followed by 'years', 'year', 'y' if (msec == null && sec == null && min == null && hrs == null && days == null && weeks == null && years == null) { return(null); } // Build the time span from parts if (msec != null && double.TryParse(msec, out x)) { ts += TimeSpan.FromMilliseconds(x); } if (sec != null && double.TryParse(sec, out x)) { ts += TimeSpan.FromSeconds(x); } if (min != null && double.TryParse(min, out x)) { ts += TimeSpan.FromMinutes(x); } if (hrs != null && double.TryParse(hrs, out x)) { ts += TimeSpan.FromHours(x); } if (days != null && double.TryParse(days, out x)) { ts += TimeSpan.FromDays(x); } if (weeks != null && double.TryParse(weeks, out x)) { ts += TimeSpan.FromDays(7 * x); } if (years != null && double.TryParse(years, out x)) { ts += TimeSpan.FromDays(365 * x); } // Note: Not accounting for leap years because there's no way to know if the time span crosses a leap year // (e.g 2y might span a leap year). Users will have to add additional days to the offset as needed return(ts); } catch { return(null); } }