2013-05-22 3 views
13

私はTimeSpanというクライアントが自分のサーバーに接続された時間を表しています。そのTimeSpanをユーザに表示したいと思います。しかし、私は、その情報の表示に過度に冗長になりたくない(例:!あまりにも詳細な= 2時間の3分の32.2345secを)例えばTimeSpanを表す "人間が判読可能な"文字列を生成する方法

:接続時間があれば...

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

のをもちろん、数値が1(例:1秒、1分、1時間、1日)の場合は、テキストを特異にしたい(例:1秒、1分、1時間、1日)。

巨大なif/else節がなければ簡単に実装できますか?ここに私が現在やっていることがあります。スイッチは、あなたがそれに応じて指定されたTimeSpanをフォーマットするTotalSecondsに基づいて正しいフォーマット文字列とCustomFormatterのための辞書検索を使用することができます構築する場合

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; 
} 
+0

残念ながらありません。それは、FCL –

+2

に含めるにはあまりにも特定のタクです。参考になるかもしれません:http://stackoverflow.com/questions/11/how-do-i-calculate-relative-time – Habib

+0

[C#Libraryの可能な複製は人間が読める時間のスパンを解析する](http://stackoverflow.com/questions/1304677/c-sharp-library-to-parse-human-readable-time-spans) – Moeri

答えて

20

は複雑を取り除くために。

public string GetReadableTimespan(TimeSpan ts) 
{ 
    // formats and its cutoffs based on totalseconds 
    var cutoff = new SortedList<long, string> { 
     {60, "{3:S}" }, 
     {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<string, string> timeformats = new Dictionary<string, string> { 
     {"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; 
    } 
} 
+2

これは確かに巧妙で、私はそれが好きです。私はそれが質問に答えるのでそれを受け入れるだろう。私が投稿したコードは即座に理解でき、容易に理解できるという利点があります。あなたのコードにはしばらく時間がかかります。それは私が嫌いではない唯一のものです。 –

+2

ありがとう、ありがとう。私たちが解決しようとする単純な問題ではないかもしれません:-) – rene

+0

これはTimeSpan.FromHours(1)のようなタイムスパンでは "0分、0秒"をレンダリングしません。 –

1

私は私が考えるより、「読み」である。このようなものが好まれているでしょう:

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; 
} 
+1

' var totalMinutes =(int)value.TotalHours; '< - は' value.TotalMinutes'にする必要がありますか? – wal

0

(ドイツ語で)別のアプローチ

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; 
} 
5

なぜ、このようなない単に何かを?

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

     return [email protected]"{t:%d} days"; 
    } 
} 

2つの時間単位(分単位と秒単位など)が望ましい場合は、追加するのが非常に簡単です。

+0

@Kiquenet @は文字列の内容をエスケープするので、\が必要なときは\\を書く必要はありません。 $はむしろ新機能で、 '{x}'文字列補間が可能です。 – mafu

0

これは私の持っているものです - 受け入れられた答えよりも少しシンプルですね。また、文字列の分割/解析も行いません。

var components = new List<Tuple<int, string>> { 
    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))); 
1

私は自分のニーズに合わせてBjornの回答に基づいて作成しましたが、他の誰かがこの問題を見た場合に分かち合いたいと思っていました。時間を節約することができます。受け入れられた答えは、私のニーズにとって少し重いです。

private static string FormatTimeSpan(TimeSpan timeSpan) 
    { 
     Func<Tuple<int,string>, string> tupleFormatter = t => $"{t.Item1} {t.Item2}{(t.Item1 == 1 ? string.Empty : "s")}"; 
     var components = new List<Tuple<int, string>> 
     { 
      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}"; 
    } 
1
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

関連する問題