Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I'm looking to achieve a time format that looks like this: 2d 4h remaining; or 1h 36m remaining; or 35s remaining, etc. So that it only displays the two largest values of time (this is how Clash of Clans and some other mobile games format wait times.)

Currently my code is below, but I'm wondering if there might be a shorter way to format the timespan in this manner? I'm not fully sure if String.Format will work…

public string FormatRushTime ( System.TimeSpan span )
{
    string t1;
    string t2;
    if ( span.Days > 0 )
    {
        t1 = span.ToString("%d") + "d";
        t2 = span.Hours > 0 ? span.ToString("%h") + "h" : String.Empty;
    }
    else if ( span.Hours > 0 )
    {
        t1 = span.ToString("%h") + "h";
        t2 = span.Minutes > 0 ? span.ToString("%m") + "m" : String.Empty;
    }
    else if ( span.Minutes > 0 )
    {
        t1 = span.ToString("%m") + "m";
        t2 = span.Seconds > 0 ? span.ToString("%s") + "s" : String.Empty;
    }
    else if ( span.Seconds > 0 )
    {
        t1 = span.ToString("%s") + "s";
        t2 = String.Empty;
    }
    else
    {
        t1 = String.Empty;
        t2 = String.Empty;
    }

    return t1+" "+t2;
}
share|improve this question

3 Answers 3

up vote 10 down vote accepted

What you want to do, is use early-returns (FTW). Also, let the formatter do the string work for you. Right tool for the job.

Consider your code, rewritten in Ideone...:

static string FormatRushTime (TimeSpan span)
{
    if ( span.Days != 0 )
    {
        return String.Format("{0:d}d {1:d}h", span.Days, Math.Abs(span.Hours));
    }
    if ( span.Hours != 0 )
    {
        return String.Format("{0:d}h {1:d}m", span.Hours, Math.Abs(span.Minutes));
    }
    if ( span.Minutes != 0 )
    {
        return String.Format("{0:d}m {1:d}s", span.Minutes, Math.Abs(span.Seconds));
    }
    return String.Format("{0:d}s", span.Seconds);
}
share|improve this answer
    
Thanks @rolfl — This is nice and easy to read. Although I would like to ignore the second value if it's 0 (e.g., 2h 0m -> 2h). Is this possible, perhaps with a ternary operator? –  Essential Jun 6 at 15:53
3  
Yes, it can be ignored with the ternary, but I would actually advise you against removing the 0-values (and I intentionally did it this way). The reason is precision, in a count-down, 5m 2s, 5m 1s, 5m is 'just wrong' because 5m could be a lot more than 5m 0s. Still, the ternary is your friend. –  rolfl Jun 6 at 15:56
    
You're right. It would look odd. Unfortunately I am not always counting down… sometimes I just want to display the wait time, and then a 0-value looks odd. I re-posted as a new question: codereview.stackexchange.com/questions/52635/… –  Essential Jun 6 at 16:31

I love LINQ so I present two solutions.

public static string FormatRushTime(System.TimeSpan span)
{
    var time = new[] 
        {
            new { Unit = "d", Value = span.Days },
            new { Unit = "h", Value = span.Hours},
            new { Unit = "m", Value = span.Minutes},
            new { Unit = "s", Value = span.Seconds}
        };

    var filtred = time.SkipWhile(t => t.Value == 0)
                      .Take(2)
                      .Where(t => t.Value != 0)
                      .Select(t => String.Format("{0}{1} ", t.Value, t.Unit));

    return String.Concat(filtred);
}

I'm not sure if it's readable beacause I'm pretty tired. :)

public static string FormatRushTime(System.TimeSpan span)
{
    string[] units = { "d", "h", "m", "s" };

    var r = units.Select(unit => new { Unit = unit, Value = span.ToString("%" + unit) })
                 .SkipWhile(time => time.Value == "0")
                 .Take(2)
                 .Where(time => time.Value != "0")
                 .Select(time => String.Format("{0}{1} ", time.Value, time.Unit));

    return string.Concat(r);
}
share|improve this answer
1  
The trailing space isn't nice. I would instead drop the space from the Format() and used string.Join() instead of Concat(). –  svick Jun 7 at 12:40

You can take advantage of custom format strings for TimeSpan. Modifying rolfl's answer to use those, it could look something like:

public static string FormatRushTime(TimeSpan span)
{
    var result = span.ToString(GetRushTimeFormat(span));

    if (span < TimeSpan.Zero)
        result = "-" + result;

    return result;
}

private static string GetRushTimeFormat(TimeSpan span)
{
    if (span.Days != 0)
        return @"d\d\ h\h";

    if (span.Hours != 0)
        return @"h\h\ m\m";

    if (span.Minutes != 0)
        return @"m\m\ s\s";

    return @"s\s";
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.