Add a ToString extension method to the TimeSpan structure in C#

[ToString extension method]

Until .NET Framework version 4.0, you could not pass the TimeSpan structure’s ToString method a format string, so the result was always in the form hh:mm:ss.fffffff where hh is hours, mm is minutes, ss is seconds, and fffffff is 7 digits of fractional seconds. If you’re measuring time spans of only minutes or seconds, it seems silly to have to display hours. Similarly if you’re measuring a duration in hours, it may not make sense to show 7 digits of precision.

I write a lot of programs using older versions of the .NET Framework and I often need to display formatted TimeSpans showing elapsed time, so I wrote this extension method. If you are using the .NET Framework 4.0 or later, use the framework’s version of ToString to format TimeSpans, but if you’re using an earlier version, you can use the following extension method to add simple formatting to TimeSpan objects. (You may also find the parsing techniques that it uses interesting.)

// Add a ToString method to TimeSpan that accepts a format string.
public static string ToString(this TimeSpan ts, string format)
{
    string result = "";

    // Split the format string into
    // alphabetic and non-alphabetic pieces.
    Regex reg_exp = new Regex("[a-z]+|[^a-z]+");
    MatchCollection matches = reg_exp.Matches(format.ToLower());

    // Process the pieces.
    foreach (Match piece in matches)
    {
        // Make a format for the value.
        string piece_format = new string('0', piece.Value.Length);

        // Examine the piece's first character.
        switch (piece.Value[0])
        {
            case 'd':       // Days.
                result += ts.Days.ToString(piece_format);
                break;
            case 'h':       // Hours.
                result += ts.Hours.ToString(piece_format);
                break;
            case 'm':       // Minutes.
                result += ts.Minutes.ToString(piece_format);
                break;
            case 's':       // Seconds.
                result += ts.Seconds.ToString(piece_format);
                break;
            case 'f':       // Fractional seconds.
                // Get just the fractional seconds.
                double fraction = ts.TotalSeconds -
                    (int)ts.TotalSeconds;
                // Move digits to the left of the decimal point.
                fraction *= Math.Pow(10, piece.Value.Length);
                result += fraction.ToString(piece_format);
                break;
            default:        // A non-alphabetic piece. Use as is.
                result += piece.Value;
                break;
        }
    }

    return result;
}

The method uses a Regex regular expression object to parse the format string into alphabetic and non-alphabetic pieces. A typical format string might look like hh:mm:ss.f. In that case the pieces of the string would be hh/:/mm/:/ss/./f/.

The code gets the Regex object’s collection of matches and then loops through them. For each match, the code makes a piece_format string containing a 0 for each letter in the match. For example, if the piece of the format string is hhh, then piece_format is 000. (For non-alphabetic pieces like “:”, the code creates piece_format but doesn’t use it.)

The code then uses a switch statement to examine the first character of the format piece. (It assumes that all of the characters in an alphabetic piece are the same.) If the piece begins with d, h, m, or s, the code adds the TimeSpan‘s number of days, hours, minutes, or seconds to the result, using piece_format as the format string.

If the piece begins with f, the code subtracts the integer number of seconds elapsed from the total number of seconds elapsed to get the fractional number of seconds. To format this number as digits only without adding an extra decimal point, the code multiplies it by a power of ten to move all of the required digits to the left of the decimal point. It then formats the value as an integer that includes the desired number of digits.

If the piece is not alphabetic (like “:” or “,”), the code includes it in the result as it is.

After building the result string, the method simply returns it.

Note that this method is not locale aware so, for example, it won’t automatically change the hours/minutes separator from : to whatever is appropriate in the computer’s locale. You’ll have to do that in the format string you pass to ToString.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in extension methods, parsing and tagged , , , , , , , , , , . Bookmark the permalink.

2 Responses to Add a ToString extension method to the TimeSpan structure in C#

  1. E Anderson says:

    Another way to solve this would be to utilize the ToString() method of DateTime to parse the format string:

    public static string ToString(this TimeSpan ts, string format)
    {
        var dt = new DateTime(ts.Ticks);
        return dt.ToString(format);
    }

    Then just specify the 24-hour clock (“H”) in the format string to avoid the leading 12th hours:

    private void tmrClock_Tick(object sender, EventArgs e)
    {
        TimeSpan elapsed = DateTime.Now – StartTime;
        lblElapsed.Text = elapsed.ToString(“H:mm:ss.ffff”);
    }

Leave a Reply

Your email address will not be published. Required fields are marked *