How can I String.Format a TimeSpan object with a custom format in .NET?
Asked Answered
S

20

200

What is the recommended way of formatting TimeSpan objects into a string with a custom format?

Sosthena answered 22/2, 2009 at 12:57 Comment(0)
B
281

Please note: this answer is for .Net 4.0 and above. If you want to format a TimeSpan in .Net 3.5 or below please see JohannesH's answer.

Custom TimeSpan format strings were introduced in .Net 4.0. You can find a full reference of available format specifiers at the MSDN Custom TimeSpan Format Strings page.

Here's an example timespan format string:

string.Format("{0:hh\\:mm\\:ss}", myTimeSpan); //example output 15:36:15

(UPDATE) and here is an example using C# 6 string interpolation:

$"{myTimeSpan:hh\\:mm\\:ss}"; //example output 15:36:15

You need to escape the ":" character with a "\" (which itself must be escaped unless you're using a verbatim string).

This excerpt from the MSDN Custom TimeSpan Format Strings page explains about escaping the ":" and "." characters in a format string:

The custom TimeSpan format specifiers do not include placeholder separator symbols, such as the symbols that separate days from hours, hours from minutes, or seconds from fractional seconds. Instead, these symbols must be included in the custom format string as string literals. For example, "dd.hh:mm" defines a period (.) as the separator between days and hours, and a colon (:) as the separator between hours and minutes.

Baryton answered 8/12, 2010 at 10:13 Comment(9)
@Andrei Rinea: Correct, as stated at the start of my second paragraph ".Net 4 allows you to use custom format strings with Timespan".Baryton
Yeah I saw that, just trying to point out to other people. I tried it in 3.5 hoping it's not a v4 only feature.Anzac
Equivalent way, but more succinct: myTimeSpan.ToString("hh\\mm\\ss") (As noted above, works only in .Net 4.0 and above).Hen
@Edward, that's not quite right. In your example you're escaping the first m and the first s, so with an input of myTimeSpan = new TimeSpan(15, 35, 54); the statement myTimeSpan .ToString("hh\\mm\\ss"); will result in 15m35s54. I don't think that's what you intended as it'll place an m after your hours and an s after your minutes.Baryton
@Doctor Jones - Thank you! I meant myTimeSpan.ToString("h\\hm\\ms\\s"); or myTimeSpan.ToString(@"h\hm\ms\s"); which gives 15h35m54sHen
just be careful with this solution, because it will not work correctly when the Hours part is more than 24Slick
This solution is not quite right if your TimeSpan has day. @JohannesH solution works betterCalvillo
How can I include days in "hh" using C# 6 string interpolation?Joanne
@QuarK, there is no custom format specifier that does that, they all give you the hours that are not counted as part of days. You could do this instead though $"{myTimeSpan.TotalHours}:{myTimeSpan:mm\\:ss}". From a user point of view, it might be better to output the days though, nobody wants to mentally figure out how many days are in 200+ hours.Baryton
E
95

For .NET 3.5 and lower you could use:

string.Format ("{0:00}:{1:00}:{2:00}", 
               (int)myTimeSpan.TotalHours, 
                    myTimeSpan.Minutes, 
                    myTimeSpan.Seconds);

Code taken from a Jon Skeet answer on bytes

For .NET 4.0 and above, see DoctaJonez answer.

Emelineemelita answered 22/2, 2009 at 13:6 Comment(3)
Yes, thank you. But I think that DateTime approach is more customizable, as it would work for any time format supported by DateTime. This approach is hard to use for showing AM/PM for example.Sosthena
Sure, TimeSpan is meant to represents a period of time, not a time of day (Even though the DateTime.Now.TimeOfDay property would have you believe otherwise). If you need to represent a specific time of day I suggest you continue using the DateTime class.Emelineemelita
Just remember that if the TimeSpan is equal to or more than 24 hours you will get incorrect formatting.Emelineemelita
S
31

One way is to create a DateTime object and use it for formatting:

new DateTime(myTimeSpan.Ticks).ToString(myCustomFormat)

// or using String.Format:
String.Format("{0:HHmmss}", new DateTime(myTimeSpan.Ticks))

This is the way I know. I hope someone can suggest a better way.

Sosthena answered 22/2, 2009 at 12:58 Comment(2)
This is really only going to work if the TimeSpan is less than a day. That might not be a such a terrible restriction, but it keeps it from being a general solution.Forzando
Does it return correct value ? Dim ts As New TimeSpan(11, 22, 30, 30):Dim sss As String = New DateTime(ts.Ticks).ToString("dd.hh:mm:ss")Ardithardme
O
11

Simple. Use TimeSpan.ToString with c, g or G. More information at MSDN

Octal answered 9/8, 2010 at 18:26 Comment(1)
Thank you for your answer. This method is apparently new in .NET 4, and did not exist when the question was asked. It also does not support custom formats. Nevertheless, it's a valuable addition to the answers to this questions. Thanks again.Sosthena
D
9

I would go with

myTimeSpan.ToString("hh\\:mm\\:ss");
Damp answered 29/6, 2015 at 14:0 Comment(2)
Simple and clean! an alternative is @"hh\:mm\:ss"Berryman
Escape characters did the trick ! Surprisingly...Toilworn
O
8

Personally, I like this approach:

TimeSpan ts = ...;
string.Format("{0:%d}d {0:%h}h {0:%m}m {0:%s}s", ts);

You can make this as custom as you like with no problems:

string.Format("{0:%d}days {0:%h}hours {0:%m}min {0:%s}sec", ts);
string.Format("{0:%d}d {0:%h}h {0:%m}' {0:%s}''", ts);
Oram answered 22/11, 2013 at 19:27 Comment(0)
W
6
Dim duration As New TimeSpan(1, 12, 23, 62)

DEBUG.WriteLine("Time of Travel: " + duration.ToString("dd\.hh\:mm\:ss"))

It works for Framework 4

http://msdn.microsoft.com/en-us/library/ee372287.aspx

Wartow answered 18/11, 2012 at 20:14 Comment(0)
A
5

This is awesome one:

string.Format("{0:00}:{1:00}:{2:00}",
               (int)myTimeSpan.TotalHours,
               myTimeSpan.Minutes,
               myTimeSpan.Seconds);
Arron answered 14/4, 2010 at 17:11 Comment(1)
You need to cast the myTimeSpan.TotalHours to an int - otherwise it might get rounded-up. See JohannesH's answerRecital
A
3

You can also go with:

Dim ts As New TimeSpan(35, 21, 59, 59)  '(11, 22, 30, 30)    '
Dim TimeStr1 As String = String.Format("{0:c}", ts)
Dim TimeStr2 As String = New Date(ts.Ticks).ToString("dd.HH:mm:ss")

EDIT:

You can also look at Strings.Format.

    Dim ts As New TimeSpan(23, 30, 59)
    Dim str As String = Strings.Format(New DateTime(ts.Ticks), "H:mm:ss")
Ardithardme answered 18/11, 2012 at 22:58 Comment(0)
S
3
if (timeSpan.TotalDays < 1)
    return timeSpan.ToString(@"hh\:mm\:ss");

return timeSpan.TotalDays < 2
    ? timeSpan.ToString(@"d\ \d\a\y\ hh\:mm\:ss")
    : timeSpan.ToString(@"d\ \d\a\y\s\ hh\:mm\:ss");

All literal characters must be escaped.

Speiss answered 1/7, 2016 at 10:35 Comment(0)
W
3

This is the approach I used my self with conditional formatting. and I post it here because I think this is clean way.

$"{time.Days:#0:;;\\}{time.Hours:#0:;;\\}{time.Minutes:00:}{time.Seconds:00}"

example of outputs:

00:00 (minimum)

1:43:04 (when we have hours)

15:03:01 (when hours are more than 1 digit)

2:4:22:04 (when we have days.)

The formatting is easy. time.Days:#0:;;\\ the format before ;; is for when value is positive. negative values are ignored. and for zero values we have;;\\ in order to hide it in formatted string. note that the escaped backslash is necessary otherwise it will not format correctly.

Welt answered 24/12, 2016 at 11:17 Comment(1)
What language is :#0:;;\\ ? Where can I read about it?Birdseed
J
3

Here is my extension method:

public static string ToFormattedString(this TimeSpan ts)
{
    const string separator = ", ";

    if (ts.TotalMilliseconds < 1) { return "No time"; }

    return string.Join(separator, new string[]
    {
        ts.Days > 0 ? ts.Days + (ts.Days > 1 ? " days" : " day") : null,
        ts.Hours > 0 ? ts.Hours + (ts.Hours > 1 ? " hours" : " hour") : null,
        ts.Minutes > 0 ? ts.Minutes + (ts.Minutes > 1 ? " minutes" : " minute") : null,
        ts.Seconds > 0 ? ts.Seconds + (ts.Seconds > 1 ? " seconds" : " second") : null,
        ts.Milliseconds > 0 ? ts.Milliseconds + (ts.Milliseconds > 1 ? " milliseconds" : " millisecond") : null,
    }.Where(t => t != null));
}

Example call:

string time = new TimeSpan(3, 14, 15, 0, 65).ToFormattedString();

Output:

3 days, 14 hours, 15 minutes, 65 milliseconds
Jaclynjaco answered 19/7, 2017 at 7:20 Comment(0)
B
1

I used the code below. It is long, but still it is one expression, and produces very friendly output, as it does not outputs days, hours, minutes, or seconds if they have value of zero.

In the sample it produces output: "4 days 1 hour 3 seconds".

TimeSpan sp = new TimeSpan(4,1,0,3);
string.Format("{0}{1}{2}{3}", 
        sp.Days > 0 ? ( sp.Days > 1 ? sp.ToString(@"d\ \d\a\y\s\ "): sp.ToString(@"d\ \d\a\y\ ")):string.Empty,
        sp.Hours > 0 ? (sp.Hours > 1 ? sp.ToString(@"h\ \h\o\u\r\s\ ") : sp.ToString(@"h\ \h\o\u\r\ ")):string.Empty,
        sp.Minutes > 0 ? (sp.Minutes > 1 ? sp.ToString(@"m\ \m\i\n\u\t\e\s\ ") :sp.ToString(@"m\ \m\i\n\u\t\e\ ")):string.Empty,
        sp.Seconds > 0 ? (sp.Seconds > 1 ? sp.ToString(@"s\ \s\e\c\o\n\d\s"): sp.ToString(@"s\ \s\e\c\o\n\d\s")):string.Empty);
Baleful answered 27/3, 2014 at 15:54 Comment(3)
Now there is a much better way to write this! Try to refactor all the common operations, and you can make this code look much, much better.Sosthena
@Hosam Aly; I'm learning all the time, do you care to post your improved code?Baleful
String timeComponent(int value, String name) { return value > 0 ? value + " " + name + (value > 1 ? "s" : ""); } Call that for each component (e.g. timeComponent(sp.Days, "day")), then use String.join to insert the spaces.Sosthena
G
1

I use this method. I'm Belgian and speak dutch so plural of hours and minutes is not just adding 's' to the end but almost a different word than singular.

It may seem long but it is very readable I think:

 public static string SpanToReadableTime(TimeSpan span)
    {
        string[] values = new string[4];  //4 slots: days, hours, minutes, seconds
        StringBuilder readableTime = new StringBuilder();

        if (span.Days > 0)
        {
            if (span.Days == 1)
                values[0] = span.Days.ToString() + " dag"; //day
            else
                values[0] = span.Days.ToString() + " dagen";  //days

            readableTime.Append(values[0]);
            readableTime.Append(", ");
        }
        else
            values[0] = String.Empty;


        if (span.Hours > 0)
        {
            if (span.Hours == 1)
                values[1] = span.Hours.ToString() + " uur";  //hour
            else
                values[1] = span.Hours.ToString() + " uren";  //hours

            readableTime.Append(values[1]);
            readableTime.Append(", ");

        }
        else
            values[1] = string.Empty;

        if (span.Minutes > 0)
        {
            if (span.Minutes == 1)
                values[2] = span.Minutes.ToString() + " minuut";  //minute
            else
                values[2] = span.Minutes.ToString() + " minuten";  //minutes

            readableTime.Append(values[2]);
            readableTime.Append(", ");
        }
        else
            values[2] = string.Empty;

        if (span.Seconds > 0)
        {
            if (span.Seconds == 1)
                values[3] = span.Seconds.ToString() + " seconde";  //second
            else
                values[3] = span.Seconds.ToString() + " seconden";  //seconds

            readableTime.Append(values[3]);
        }
        else
            values[3] = string.Empty;


        return readableTime.ToString();
    }//end SpanToReadableTime
Griego answered 26/6, 2014 at 8:53 Comment(1)
If you write software that needs to be translated, then this is pretty much the way to go. The standard TimeSpan.ToString() is just too clunky for normal end users to understand, especially when the span if over a day.Kary
C
1

This is a pain in VS 2010, here's my workaround solution.

 public string DurationString
        {
            get 
            {
                if (this.Duration.TotalHours < 24)
                    return new DateTime(this.Duration.Ticks).ToString("HH:mm");
                else //If duration is more than 24 hours
                {
                    double totalminutes = this.Duration.TotalMinutes;
                    double hours = totalminutes / 60;
                    double minutes = this.Duration.TotalMinutes - (Math.Floor(hours) * 60);
                    string result = string.Format("{0}:{1}", Math.Floor(hours).ToString("00"), Math.Floor(minutes).ToString("00"));
                    return result;
                }
            } 
        }
Cake answered 20/2, 2018 at 0:31 Comment(0)
R
1

The Substring method works perfectly when you only want the Hours:Minutes:Seconds. It's simple, clean code and easy to understand.

    var yourTimeSpan = DateTime.Now - DateTime.Now.AddMinutes(-2);

    var formatted = yourTimeSpan.ToString().Substring(0,8);// 00:00:00 

    Console.WriteLine(formatted);
Robtrobust answered 23/1, 2019 at 15:54 Comment(2)
Why not just .ToString(@"hh\:mm\:ss")? Isn't that simpler to understand?Estonian
@Arad That does seem much easier! Wonder why I wrote this answer...Robtrobust
R
1

No one has shown approach with decimal format specifier which is my favorite one, especially when used with string interpolation - https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings?redirectedfrom=MSDN#decimal-format-specifier-d

For example:

$"{time.Hours:D2}:{time.Minutes:D2}:{time.Seconds:D2}.{time.Milliseconds:D3}"
// Sample output: 00:00:09.200

You can of course wrap it up in some helper method.

Retrocede answered 1/6, 2021 at 14:14 Comment(0)
K
0

Here is my version. It shows only as much as necessary, handles pluralization, negatives, and I tried to make it lightweight.

Output Examples

0 seconds
1.404 seconds
1 hour, 14.4 seconds
14 hours, 57 minutes, 22.473 seconds
1 day, 14 hours, 57 minutes, 22.475 seconds

Code

public static class TimeSpanExtensions
{
    public static string ToReadableString(this TimeSpan timeSpan)
    {
        int days = (int)(timeSpan.Ticks / TimeSpan.TicksPerDay);
        long subDayTicks = timeSpan.Ticks % TimeSpan.TicksPerDay;

        bool isNegative = false;
        if (timeSpan.Ticks < 0L)
        {
            isNegative = true;
            days = -days;
            subDayTicks = -subDayTicks;
        }

        int hours = (int)((subDayTicks / TimeSpan.TicksPerHour) % 24L);
        int minutes = (int)((subDayTicks / TimeSpan.TicksPerMinute) % 60L);
        int seconds = (int)((subDayTicks / TimeSpan.TicksPerSecond) % 60L);
        int subSecondTicks = (int)(subDayTicks % TimeSpan.TicksPerSecond);
        double fractionalSeconds = (double)subSecondTicks / TimeSpan.TicksPerSecond;

        var parts = new List<string>(4);

        if (days > 0)
            parts.Add(string.Format("{0} day{1}", days, days == 1 ? null : "s"));
        if (hours > 0)
            parts.Add(string.Format("{0} hour{1}", hours, hours == 1 ? null : "s"));
        if (minutes > 0)
            parts.Add(string.Format("{0} minute{1}", minutes, minutes == 1 ? null : "s"));
        if (fractionalSeconds.Equals(0D))
        {
            switch (seconds)
            {
                case 0:
                    // Only write "0 seconds" if we haven't written anything at all.
                    if (parts.Count == 0)
                        parts.Add("0 seconds");
                    break;

                case 1:
                    parts.Add("1 second");
                    break;

                default:
                    parts.Add(seconds + " seconds");
                    break;
            }
        }
        else
        {
            parts.Add(string.Format("{0}{1:.###} seconds", seconds, fractionalSeconds));
        }

        string resultString = string.Join(", ", parts);
        return isNegative ? "(negative) " + resultString : resultString;
    }
}
Kwapong answered 26/1, 2017 at 20:27 Comment(0)
Z
0

If you want the duration format similar to youtube, given the number of seconds

int[] duration = { 0, 4, 40, 59, 60, 61, 400, 4000, 40000, 400000 };
foreach (int d in duration)
{
    Console.WriteLine("{0, 6} -> {1, 10}", d, d > 59 ? TimeSpan.FromSeconds(d).ToString().TrimStart("00:".ToCharArray()) : string.Format("0:{0:00}", d));
}

Output:

     0 ->       0:00
     4 ->       0:04
    40 ->       0:40
    59 ->       0:59
    60 ->       1:00
    61 ->       1:01
   400 ->       6:40
  4000 ->    1:06:40
 40000 ->   11:06:40
400000 -> 4.15:06:40
Zirconium answered 15/5, 2017 at 6:49 Comment(0)
S
0

I wanted to return a string such as "1 day 2 hours 3 minutes" and also take into account if for example days or minuttes are 0 and then not showing them. thanks to John Rasch for his answer which mine is barely an extension of

TimeSpan timeLeft = New Timespan(0, 70, 0);
String.Format("{0}{1}{2}{3}{4}{5}",
    Math.Floor(timeLeft.TotalDays) == 0 ? "" : 
    Math.Floor(timeLeft.TotalDays).ToString() + " ",
    Math.Floor(timeLeft.TotalDays) == 0 ? "" : Math.Floor(timeLeft.TotalDays) == 1 ? "day " : "days ",
    timeLeft.Hours == 0 ? "" : timeLeft.Hours.ToString() + " ",
    timeLeft.Hours == 0 ? "" : timeLeft.Hours == 1 ? "hour " : "hours ",
    timeLeft.Minutes == 0 ? "" : timeLeft.Minutes.ToString() + " ",
    timeLeft.Minutes == 0 ? "" : timeLeft.Minutes == 1 ? "minute " : "minutes ");
Sinter answered 16/6, 2017 at 7:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.