77

I would like to format some commands execution times in a human readable format, for example:

3 -> 3ms
1100 -> 1s 100ms
62000 -> 1m 2s
etc ..

Taking into account days, hours, minutes, seconds, ...

Is it possible using C#?

Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219
  • 7
    Basically you just have to use modulo and division. – STT LCU Apr 03 '12 at 13:07
  • 1
    how about `new TimeSpan(30000).ToString()`? – juergen d Apr 03 '12 at 13:09
  • Well, I'm asking because I have other things to do here, in my company, better than basic programming exercises ;-) – Daniel Peñalba Apr 03 '12 at 13:09
  • 3
    @DanielPeñalba: then, instead of asking whether it's possible using C# (yes, it is), you should have asked "is there a ready-made class which does this?" – Igor Korkhov Apr 03 '12 at 13:12
  • 1
    So you're basically asking for a `TimeSpan.Format` custom string that doesn't result in "0 years 0 months 0 days 0 hours 0 min 1 sec 100 ms"? – Mr Lister Apr 03 '12 at 13:13
  • If you have 'long milliseconds' then a quick way is 'milliseconds.ToString("n0")' -- this inserts commas as thousands separators, making it easy to see where the seconds are. Not the *most* human readable, but handy if you're in a hurry or don't want to bother creating a TimeSpan. – yoyo Jan 30 '15 at 23:36

12 Answers12

134

You can use TimeSpan class, something like this:

TimeSpan t = TimeSpan.FromMilliseconds(ms);
string answer = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms", 
                        t.Hours, 
                        t.Minutes, 
                        t.Seconds, 
                        t.Milliseconds);

It's quite similar as this thread I've just found:

What is the best way to convert seconds into (Hour:Minutes:Seconds:Milliseconds) time?

Community
  • 1
  • 1
walther
  • 13,466
  • 5
  • 41
  • 67
  • 8
    This would produce `00h:00m:00s:003ms` instead of `3ms` for the input `3`, so I don't think it's exactly what the OP wants ;) – Nuffin Apr 03 '12 at 13:17
  • 6
    @Nuffin, he can always alter the code to suit his needs ;) I've just provided an example... – walther Apr 03 '12 at 13:20
30

I know this is old, but I wanted to answer with a great nuget package.

Install-Package Humanizer

https://www.nuget.org/packages/Humanizer

https://github.com/MehdiK/Humanizer

Example from their readme.md

TimeSpan.FromMilliseconds(1299630020).Humanize(4) => "2 weeks, 1 day, 1 hour, 30 seconds"
Ian Becker
  • 301
  • 3
  • 4
17

You could utilize the static TimeSpan.FromMilliseconds method as well as the resulting TimeSpan's Days, Hours, Minutes, Seconds and Milliseconds properties.

But I'm busy right now, so I'll leave the rest to you as an exercise.

Nuffin
  • 3,882
  • 18
  • 34
17

What about this?

var ts = TimeSpan.FromMilliseconds(86300000 /*whatever */);
var parts = string
                .Format("{0:D2}d:{1:D2}h:{2:D2}m:{3:D2}s:{4:D3}ms",
                    ts.Days, ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds)
                .Split(':')
                .SkipWhile(s => Regex.Match(s, @"00\w").Success) // skip zero-valued components
                .ToArray();
var result = string.Join(" ", parts); // combine the result

Console.WriteLine(result);            // prints '23h 58m 20s 000ms'
Igor Korkhov
  • 8,283
  • 1
  • 26
  • 31
9

.NET 4 accepts format in TimeSpan.Tostring().

For other you can implement extension method like

    public static string Format(this TimeSpan obj)
    {
        StringBuilder sb = new StringBuilder();
        if (obj.Hours != 0)
        {
            sb.Append(obj.Hours);
            sb.Append(" "); 
            sb.Append("hours");
            sb.Append(" ");
        }
        if (obj.Minutes != 0 || sb.Length != 0)
        {
            sb.Append(obj.Minutes);
            sb.Append(" "); 
            sb.Append("minutes");
            sb.Append(" ");
        }
        if (obj.Seconds != 0 || sb.Length != 0)
        {
            sb.Append(obj.Seconds);
            sb.Append(" "); 
            sb.Append("seconds");
            sb.Append(" ");
        }
        if (obj.Milliseconds != 0 || sb.Length != 0)
        {
            sb.Append(obj.Milliseconds);
            sb.Append(" "); 
            sb.Append("Milliseconds");
            sb.Append(" ");
        }
        if (sb.Length == 0)
        {
            sb.Append(0);
            sb.Append(" "); 
            sb.Append("Milliseconds");
        }
        return sb.ToString();
    }

and call as

foreach (TimeSpan span in spans)
{
    MessageBox.Show(string.Format("{0}",  span.Format()));
}
bluish
  • 26,356
  • 27
  • 122
  • 180
hungryMind
  • 6,931
  • 4
  • 29
  • 45
  • I've used this approach and I just wanted to note that the highest interval (in this case `obj.Hours`) should be `obj.TotalHours,ToString("0")`. `.TotalHours` so it can show values above 24h, and `,ToString("0")` to convert it to a single digit instead of a comma separated value. – Xavier Peña Aug 04 '16 at 14:31
7
public static string ReadableTime(int milliseconds)
{
    var parts = new List<string>();
    Action<int, string> add = (val, unit) => { if (val > 0) parts.Add(val+unit); };
    var t = TimeSpan.FromMilliseconds(milliseconds);

    add(t.Days, "d");
    add(t.Hours, "h");
    add(t.Minutes, "m");
    add(t.Seconds, "s");
    add(t.Milliseconds, "ms");

    return string.Join(" ", parts);
}
Travis J
  • 81,153
  • 41
  • 202
  • 273
Sedat Gokalp
  • 71
  • 1
  • 1
  • This is a nice approach for longer time spans (for which `t.TotalDays` also works). – Travis J Oct 19 '16 at 23:15
  • Great approach, and for optional formatting. Action add = (val, unit, zeroplaceholder) => {if (val > 0) parts.Add( string.Format( "{0:DZ}X".Replace("X", unit.ToString()) .Replace("Z",zeroplaceholder.ToString()) , val ); }; and call with add(t.Milliseconds, "ms", 4); to get 2m 37s 0456ms – Markus Sep 26 '17 at 14:50
4

This probably has a slightly different output than requested, but the result is human readable - and it can be adapted to fit many other use cases.

private static List<double> _intervals = new List<double>
{
    1.0 / 1000 / 1000,
    1.0 / 1000,
    1,
    1000,
    60 * 1000,
    60 * 60 * 1000
};
private static List<string> _units = new List<string>
{
    "ns",
    "µs",
    "ms",
    "s",
    "min",
    "h"
};

public string FormatUnits(double milliseconds, string format = "#.#")
{
    var interval = _intervals.Last(i=>i<=milliseconds);
    var index = _intervals.IndexOf(interval);

    return string.Concat((milliseconds / interval).ToString(format) , " " , _units[index]);
}

Example calls...

Console.WriteLine(FormatUnits(1));
Console.WriteLine(FormatUnits(20));
Console.WriteLine(FormatUnits(300));
Console.WriteLine(FormatUnits(4000));
Console.WriteLine(FormatUnits(50000));
Console.WriteLine(FormatUnits(600000));
Console.WriteLine(FormatUnits(7000000));
Console.WriteLine(FormatUnits(80000000));

...and results:

1000 µs
20 ms
300 ms
4 s
50 s
10 min
1.9 h
22.2 h
mike
  • 1,627
  • 1
  • 14
  • 37
4

Old question, new answer:

public static string GetReadableTimeByMs(long ms)
{
   TimeSpan t = TimeSpan.FromMilliseconds(ms);
   if (t.Hours > 0) return $"{t.Hours}h:{t.Minutes}m:{t.Seconds}s";
   else if (t.Minutes > 0) return $"{t.Minutes}m:{t.Seconds}s";
   else if (t.Seconds > 0) return $"{t.Seconds}s:{t.Milliseconds}ms";
   else return $"{t.Milliseconds}ms";
}
Stix
  • 455
  • 5
  • 16
2

For example to get 00:01:35.0090000 as 0 hours, 1 minutes, 35 seconds and 9 milliseconds you can use this:

Console.WriteLine("Time elapsed:" +TimeSpan.FromMilliseconds(numberOfMilliseconds).ToString());

Your output:

Time elapsed: 00:01:35.0090000
Hassen Ch.
  • 1,693
  • 18
  • 31
1

You can use TimeSpan.FromMilliseconds function

var tspan = TimeSpan.FromMilliseconds(YOUR_MILLI_SECONDS);
int h = tspan.Hours;
int m = tspan.Minutes;
int s = tspan.Seconds;
sridharnetha
  • 2,104
  • 8
  • 35
  • 69
1

Maybe something like this?

DateTime.Now.ToString("%d 'd' %h 'h' %m 'm' %s 'seconds' %ms 'ms'")
Arion
  • 31,011
  • 10
  • 70
  • 88
  • The question is about `TimeSpan`s, not `DateTime`s. With a `DateTime`, you'll always end up with at least one day, which wouldn't really match the input of 3 milliseconds. – Nuffin Apr 03 '12 at 13:18
0

Well i normally hate writing if statements but some times what you really have is a nail and need a hammer.

string time;
if (elapsedTime.TotalMinutes > 2)
    time = string.Format("{0:n2} minutes", elapsedTime.TotalMinutes);
else if (elapsedTime.TotalSeconds > 15)
    time = string.Format("{0:n2} seconds", elapsedTime.TotalSeconds);
else
    time = string.Format("{0:n0}ms", elapsedTime.TotalMilliseconds);
Chris Marisic
  • 32,487
  • 24
  • 164
  • 258