如何生成“人类可读”字符串来表示TimeSpan

我有一个TimeSpan表示客户端连接到我的服务器的时间。 我想将TimeSpan显示给用户。 但我不想过于冗长地显示这些信息(例如:2小时3分钟32.2345秒=太详细了!)

例如:如果连接时间是……

 > 0 seconds and  0 Seconds > 1 minute and  0 Minutes, 0 Seconds > 1 hour and  0 Hours, 0 Minutes > 1 day -----> 0 Days, 0 Hours 

当然,在数字为1(例如:1秒,1分钟,1小时,1天)的情况下,我想使文本单数(例如:1秒,1分钟,1小时,1天) 。

无论如何,如果没有一组巨大的if / else子句,是否可以轻松实现这一点? 这是我目前正在做的事情。

 public string GetReadableTimeSpan(TimeSpan value) { string duration; if (value.TotalMinutes < 1) duration = value.Seconds + " Seconds"; else if (value.TotalHours < 1) duration = value.Minutes + " Minutes, " + value.Seconds + " Seconds"; else if (value.TotalDays < 1) duration = value.Hours + " Hours, " + value.Minutes + " Minutes"; else duration = value.Days + " Days, " + value.Hours + " Hours"; if (duration.StartsWith("1 Seconds") || duration.EndsWith(" 1 Seconds")) duration = duration.Replace("1 Seconds", "1 Second"); if (duration.StartsWith("1 Minutes") || duration.EndsWith(" 1 Minutes")) duration = duration.Replace("1 Minutes", "1 Minute"); if (duration.StartsWith("1 Hours") || duration.EndsWith(" 1 Hours")) duration = duration.Replace("1 Hours", "1 Hour"); if (duration.StartsWith("1 Days")) duration = duration.Replace("1 Days", "1 Day"); return duration; } 

要摆脱复杂的if和switch结构,可以使用Dictionary查找基于TotalSeconds的正确格式字符串和CustomFormatter来相应地格式化提供的Timespan。

 public string GetReadableTimespan(TimeSpan ts) { // formats and its cutoffs based on totalseconds var cutoff = new SortedList { {59, "{3:S}" }, {60, "{2:M}" }, {60*60-1, "{2:M}, {3:S}"}, {60*60, "{1:H}"}, {24*60*60-1, "{1:H}, {2:M}"}, {24*60*60, "{0:D}"}, {Int64.MaxValue , "{0:D}, {1:H}"} }; // find nearest best match var find = cutoff.Keys.ToList() .BinarySearch((long)ts.TotalSeconds); // negative values indicate a nearest match var near = find<0?Math.Abs(find)-1:find; // use custom formatter to get the string return String.Format( new HMSFormatter(), cutoff[cutoff.Keys[near]], ts.Days, ts.Hours, ts.Minutes, ts.Seconds); } // formatter for forms of // seconds/hours/day public class HMSFormatter:ICustomFormatter, IFormatProvider { // list of Formats, with a P customformat for pluralization static Dictionary timeformats = new Dictionary { {"S", "{0:P:Seconds:Second}"}, {"M", "{0:P:Minutes:Minute}"}, {"H","{0:P:Hours:Hour}"}, {"D", "{0:P:Days:Day}"} }; public string Format(string format, object arg, IFormatProvider formatProvider) { return String.Format(new PluralFormatter(),timeformats[format], arg); } public object GetFormat(Type formatType) { return formatType == typeof(ICustomFormatter)?this:null; } } // formats a numeric value based on a format P:Plural:Singular public class PluralFormatter:ICustomFormatter, IFormatProvider { public string Format(string format, object arg, IFormatProvider formatProvider) { if (arg !=null) { var parts = format.Split(':'); // ["P", "Plural", "Singular"] if (parts[0] == "P") // correct format? { // which index postion to use int partIndex = (arg.ToString() == "1")?2:1; // pick string (safe guard for array bounds) and format return String.Format("{0} {1}", arg, (parts.Length>partIndex?parts[partIndex]:"")); } } return String.Format(format, arg); } public object GetFormat(Type formatType) { return formatType == typeof(ICustomFormatter)?this:null; } } 

为什么不简单这样呢?

 public static class TimespanExtensions { public static string ToHumanReadableString (this TimeSpan t) { if (t.TotalSeconds <= 1) { return $@"{t:s\.ff} seconds"; } if (t.TotalMinutes <= 1) { return $@"{t:%s} seconds"; } if (t.TotalHours <= 1) { return $@"{t:%m} minutes"; } if (t.TotalDays <= 1) { return $@"{t:%h} hours"; } return $@"{t:%d} days"; } } 

如果您更喜欢两个时间单位(例如分钟加秒),那么添加起来非常简单。

另一种方法(德语)

 public static string GetReadableTimeSpan(TimeSpan span) { var formatted = string.Format("{0}{1}{2}{3}", span.Duration().Days > 0 ? $"{span.Days:0} Tag{(span.Days == 1 ? string.Empty : "e")}, " : string.Empty, span.Duration().Hours > 0 ? $"{span.Hours:0} Stunde{(span.Hours == 1 ? string.Empty : "n")}, " : string.Empty, span.Duration().Minutes > 0 ? $"{span.Minutes:0} Minute{(span.Minutes == 1 ? string.Empty : "n")}, " : string.Empty, span.Duration().Seconds > 0 ? $"{span.Seconds:0} Sekunde{(span.Seconds == 1 ? string.Empty : "n")}" : string.Empty); if (formatted.EndsWith(", ")) formatted = formatted.Substring(0, formatted.Length - 2); return string.IsNullOrEmpty(formatted) ? "0 Sekunden" : ReplaceLastOccurrence(formatted, ",", " und ").Replace(" ", " "); } private static string ReplaceLastOccurrence(string source, string find, string replace) { var place = source.LastIndexOf(find, StringComparison.Ordinal); if (place == -1) return source; var result = source.Remove(place, find.Length).Insert(place, replace); return result; } 

这是我的看法 – 比接受的答案简单一点,你不觉得吗? 此外,没有字符串拆分/解析。

 var components = new List> { Tuple.Create((int)span.TotalDays, "day"), Tuple.Create(span.Hours, "hour"), Tuple.Create(span.Minutes, "minute"), Tuple.Create(span.Seconds, "second"), }; while(components.Any() && components[0].Item1 == 0) { components.RemoveAt(0); } var result = string.Join(", ", components.Select(t => t.Item1 + " " + t.Item2 + (t.Item1 != 1 ? "s" : string.Empty))); 

我希望这样的东西更具“可读性”我认为:

 public string GetReadableTimeSpan(TimeSpan value) { string duration = ""; var totalDays = (int)value.TotalDays; if (totalDays >= 1) { duration = totalDays + " day" + (totalDays > 1 ? "s" : string.Empty); value = value.Add(TimeSpan.FromDays(-1 * totalDays)); } var totalHours = (int)value.TotalHours; if (totalHours >= 1) { if (totalDays >= 1) { duration += ", "; } duration += totalHours + " hour" + (totalHours > 1 ? "s" : string.Empty); value = value.Add(TimeSpan.FromHours(-1 * totalHours)); } var totalMinutes = (int)value.TotalMinutes; if (totalMinutes >= 1) { if (totalHours >= 1) { duration += ", "; } duration += totalMinutes + " minute" + (totalMinutes > 1 ? "s" : string.Empty); } return duration; } 

我建立在Bjorn的答案以满足我的需求,希望分享以防其他任何人看到这个问题。 可以节省他们的时间。 接受的答案对我的需求来说有点重要。

  private static string FormatTimeSpan(TimeSpan timeSpan) { Func, string> tupleFormatter = t => $"{t.Item1} {t.Item2}{(t.Item1 == 1 ? string.Empty : "s")}"; var components = new List> { Tuple.Create((int) timeSpan.TotalDays, "day"), Tuple.Create(timeSpan.Hours, "hour"), Tuple.Create(timeSpan.Minutes, "minute"), Tuple.Create(timeSpan.Seconds, "second"), }; components.RemoveAll(i => i.Item1 == 0); string extra = ""; if (components.Count > 1) { var finalComponent = components[components.Count - 1]; components.RemoveAt(components.Count - 1); extra = $" and {tupleFormatter(finalComponent)}"; } return $"{string.Join(", ", components.Select(tupleFormatter))}{extra}"; } 
 public string ToHumanDuration(TimeSpan? duration, bool displaySign = true) { if (duration == null) return null; var builder = new StringBuilder(); if (displaySign) { builder.Append(duration.Value.TotalMilliseconds < 0 ? "-" : "+"); } duration = duration.Value.Duration(); if (duration.Value.Days > 0) { builder.Append($"{duration.Value.Days}d "); } if (duration.Value.Hours > 0) { builder.Append($"{duration.Value.Hours}h "); } if (duration.Value.Minutes > 0) { builder.Append($"{duration.Value.Minutes}m "); } if (duration.Value.TotalHours < 1) { if (duration.Value.Seconds > 0) { builder.Append(duration.Value.Seconds); if (duration.Value.Milliseconds > 0) { builder.Append($".{duration.Value.Milliseconds.ToString().PadLeft(3, '0')}"); } builder.Append("s "); } else { if (duration.Value.Milliseconds > 0) { builder.Append($"{duration.Value.Milliseconds}ms "); } } } if (builder.Length <= 1) { builder.Append(" <1ms "); } builder.Remove(builder.Length - 1, 1); return builder.ToString(); } 

资料来源: https : //github.com/HangfireIO/Hangfire/blob/master/src/Hangfire.Core/Dashboard/HtmlHelper.cs

另一个刺伤了这个。 更加连贯地处理复数单位(并省略零单位):

 private string GetValueWithPluralisedUnits(int value, string units, int prefix_value) { if (value != 0) { return (prefix_value == 0 ? "" : ", ") + value.ToString() + " " + units + (value == 1 ? "" : "s"); } return ""; } private string GetReadableTimeSpan(TimeSpan value) { string duration; if (value.TotalMinutes < 1) { if (value.Seconds > 0) { duration = GetValueWithPluralisedUnits(value.Seconds, "Second", 0); } else { duration = ""; } } else if (value.TotalHours < 1) { duration = GetValueWithPluralisedUnits(value.Minutes, "Minute", 0) + GetValueWithPluralisedUnits(value.Seconds, "Second", value.Minutes); } else if (value.TotalDays < 1) { duration = GetValueWithPluralisedUnits(value.Hours, "Hour", 0) + GetValueWithPluralisedUnits(value.Minutes, "Minute", value.Hours); } else { int days_left = (int)value.TotalDays; int years = days_left / 365; days_left -= years * 365; int months = days_left / 12; days_left -= months * 12; duration = GetValueWithPluralisedUnits(years, "Year", 0) + GetValueWithPluralisedUnits(months, "Month", years) + GetValueWithPluralisedUnits(days_left, "Day", years + months); } return duration; } 

这是我的,非常简单 –

 TimeSpan timeElapsed = DateTime.Now - referenceTime_; string timeString = ""; if (timeElapsed.Hours > 0) timeString = timeElapsed.Hours.ToString() + " hour(s), " + timeElapsed.Minutes.ToString() + " minutes, " + timeElapsed.Seconds.ToString() + " seconds"; else if (timeElapsed.Minutes > 0) timeString = timeElapsed.Minutes.ToString() + " minutes, " + timeElapsed.Seconds.ToString() + " seconds"; else timeString = timeElapsed.Seconds.ToString() + " seconds";