views:

71

answers:

4

I want to format TimeSpans in C# in this way:

xxx day(s) yyy hours(s) zzz minute(s)

Conditions:

  1. Extra seconds should be truncated

  2. day(s) is the largest unit I want. I want 34 days to appear as 34 days rather than 1 month 4 days etc.

  3. If timespan is less than one day, I dont want the day part to show up. Likewise if span is less than 1 hour, I want only the mins part to show up.

Is there any way I can do this using built-in format strings or there is no way other than writing my own function?

Edit: Currently using my own function for this. It takes TimeSpan in minutes as input (TimeSpan.TotalMinutes) :

private static string GetTimeStringFromMinutes(double p)
        {
            var minutes = (int) p;
            int hours = minutes / 60;
            minutes = minutes % 60;
            int days = hours/24;
            hours = hours%24;
            string dayPart = days + " day(s) ";
            string hoursPart = hours + " hour(s) ";
            string minutesPart = minutes + " minute(s)";
            if (days != 0)
                return (dayPart + hoursPart + minutesPart);
            if (hours != 0)
                return (hoursPart + minutesPart);
            return (minutesPart);
        }
+3  A: 

In .NET 3.5 and earlier you need to write your own function.

In .NET 4 support was added for formatting TimeSpan, see TimeSpan.ToString(string) for details.

Richard
+2  A: 

TimeSpan has no formatting options at all before .NET 4.0, you'd have to convert it to DateTime through the Ticks property. Nothing remotely close in DateTime.String(format) formatting options though, you'll have to write it yourself.

In .NET 4.0, TimeSpan acquired a ToString(format) override. Custom formatting strings are described here. Your 3rd requirement is going to need code.

Hans Passant
+2  A: 

Unfortunately there is nothing in .Net directly available. For myself i had resolved the problem this way:

public static class TimeSpanExtensions
{
    public static string ToDetailedString(this TimeSpan timeSpan)
    {
        if (timeSpan == null)
            throw new ArgumentNullException("timeSpan");

        var sb = new StringBuilder(30);

        var current = timeSpan.ToDaysString();

        if (!String.IsNullOrEmpty(current))
            sb.Append(current);

        current = timeSpan.ToHoursString();

        if (!String.IsNullOrEmpty(current))
        {
            if (sb.Length > 0)
                sb.Append(" ");

            sb.Append(current);
        }

        current = timeSpan.ToMinutesString();

        if (!String.IsNullOrEmpty(current))
        {
            if (sb.Length > 0)
                sb.Append(" ");

            sb.Append(current);
        }

        return sb.ToString();
    }

    public static string ToDaysString(this TimeSpan timeSpan)
    {
        if (timeSpan == null)
            throw new ArgumentNullException("timeSpan");

        int days = (int)timeSpan.TotalDays;

        switch (days)
        {
            case 0:
                return String.Empty;
            case 1:
                return "1 day";
            default:
                return days + " days";
        }
    }

    public static string ToHoursString(this TimeSpan timeSpan)
    {
        if (timeSpan == null)
            throw new ArgumentNullException("timeSpan");

        switch (timeSpan.Hours)
        {
            case 0:
                return String.Empty;
            case 1:
                return "1 hour";
            default:
                return timeSpan.Hours + " hours";
        }
    }

    public static string ToMinutesString(this TimeSpan timeSpan)
    {
        if (timeSpan == null)
            throw new ArgumentNullException("timeSpan");

        switch (timeSpan.Minutes)
        {
            case 0:
                return String.Empty;
            case 1:
                return "1 minute";
            default:
                return timeSpan.Minutes + " minutes";
        }
    }
}

Maybe it's not the most elegant solution and i think there can be done some improvements especially in the ToDetailedString() function, but it works absolutely fine.

Oliver
I'm also using my own method right now, which takes Timespan in minutes as input... edited to include that in my question
Raze2dust
+2  A: 

There isn't any built-in way of doing your requirement at least in .NET 3.5. Here's a class that extends the TimeSpan to provide your desired functionality.

public static class TimeSpanEx
{
    public static string FormattedString(this TimeSpan ts)
    {
        int days = (int)ts.TotalDays;
        int hrs = (int)ts.Hours;
        int mins = (int)ts.Minutes;
        StringBuilder sb = new StringBuilder();

        if (days > 0)
        {
            sb.Append(days.ToString() + (days == 1 ? " day, " : " days, "));
        }

        if (hrs > 0 || days > 0)
        {
            sb.Append(hrs.ToString() + (hrs == 1 ? " hour, " : " hours, "));
        }

        sb.Append(mins.ToString() + (mins == 1 ? " min" : " mins"));

        return sb.ToString();
    }
}
Alex Essilfie