views:

164

answers:

7

Hi,

I have the following statemnt.

  timespan = timespan.FromSeconds(236541)
  formattedTimeSpan = String.Format("{0} hr {1} mm {2} sec", Math.Truncate(timespan.TotalHours), timespan.Minutes, timespan.Seconds)

I have to have it formatted as "hrs mn sec" if there are more than one hour. I want to check this within the String.Format above.

Thanks.

+4  A: 

One possibility is to make the plural part of the format string, and write:

formattedTimeSpan = String.Format("{0} hr{1} {2} mm {3} sec",
    Math.Truncate(timespan.TotalHours),
    Math.Truncate(timespan.TotalHours) == 1 ? "" : "s",
    timespan.Minutes,
    timespan.Seconds);

This will insert a "s" into the output if the output says anything other than "1 hr".

Note that this is not friendly to localization: other languages form plurals differently than English.

Bradley Grainger
I take this as the answer. For cleaner code, I think we should take suggestion from Kobi's answer.
Narazana
+3  A: 

No magic here. Why would you code in whatever condition format may offer, when you can code in C#?

To avoid duplicated code, you might as promote some expressions to variables, and use the condition on them:

timespan = timespan.FromSeconds(236541);
int hours = Math.Truncate(timespan.TotalHours);
string hoursUnit = hours == 1 ? "hr" : "hrs";
formattedTimeSpan = String.Format("{0}{1} {2} mm {3} sec",
                        hours, hoursUnit, timespan.Minutes, timespan.Seconds);
Kobi
Thanks for reply. I know this method. My question is that "Can I do it within String.Format?".
Narazana
@Kobi, I think you need to include "hours" in the String.Format as well.
Angkor Wat
@Angkor Wat - Fixed, Thanks! Must have missed that one when I added the new line `:P`
Kobi
A: 

There is no "simple" way of doing this.

You can either do it like this

string templateSingleHour = "{0} hr {1} mm {2} sec";
string templateMultiHour = "{0} hrs {1} mm {2} sec";
string template = Math.Truncate(timespan.TotalHours) > 1 ? templateMultiHour :templateSingleHour;
formattedTimeSpan = String.Format(template ...)

or like this

formattedTimeSpan = String.Format("{0} {3} {1} mm {2} sec", Math.Truncate(timespan.TotalHours), timespan.Minutes, timespan.Seconds, Math.Truncate(timespan.TotalHours) > 1 ? "hrs" : "hr");
PiRX
Why write `Math.Truncate(timespan.TotalHours) > 1` when you can use `timespan.TotalHours >= 2`?
Gabe
for ease of writing I just copied OP's code :)
PiRX
A: 

You should use the various time format specifiers. It helps simplify the call to Format() immensely.

var timespan = TimeSpan.FromSeconds(236541);
var formattedTimeSpan = String.Format("{0:hh} hr{1} {0:mm} mm {0:ss} sec",
                                      timespan, timespan.Hours > 1 ? "s" : "");
Jeff M
+2  A: 

I use a few extension methods which help me with this sort of thing:

public static string Inflect(this string noun, int count)
{
 return (count == 1 || noun.EndsWith("s", StringComparison.InvariantCultureIgnoreCase)) ? noun : noun + "s";
}

public static string Of(this int count, string noun)
{
 return string.Format("{0} {1}", count, noun.Inflect(count));
}

e.g.:

"Hour".Inflect(1) : Hour

"Hour".Inflect(2) : Hours

0.Of("hour") : "0 hours"

1.Of("hour") : "1 hour"

5.Of("hour") : "5 hours"

Ani
I actually have a similar method in use. In fact, I wrote a whole control for that. In Hebrew, it is common to have different nouns for 1 2 or more (think "once, twice, 3 times"). Even for English, it would be more useful with two words: `(2).Of("Memory", "Memories")` - you don't want to rewrite the English Grammar in your code...
Kobi
+1 @Kobi: Yes, that would be really useful. I normally use this method for just the sort of issue the OP is facing, when I know the noun in question, and that it inflects 'normally'.
Ani
A: 

FWIW, if your app is going to be used my non-english speakers ever then this is something that's not trivial. There is a blog post that expounds on the problem at http://blogs.msdn.com/b/michkap/archive/2007/07/24/4022881.aspx

obelix
+2  A: 

Probably the cleanest way to do this is to write your own ICustomFormatter. Here's an example of a convenient pluralizer format:

using System;

public class PluralFormatter : IFormatProvider, ICustomFormatter {

   public object GetFormat(Type formatType) {
      if (formatType == typeof(ICustomFormatter))
         return this;
      else
         return null;
   }

   public string Format(string format, object arg, 
                          IFormatProvider formatProvider)
   {   
      if (! formatProvider.Equals(this)) return null;

      if (! format.StartsWith("^")) return null;

      String[] parts = format.Split(new char[] {'^'});
      int choice = ((int) arg) == 1 ? 1 : 2;
      return String.Format("{0} {1}", arg, parts[choice]);
   }

   public static void Main() {
      Console.WriteLine(String.Format(
         new PluralFormatter(),
         "{0:^puppy^puppies}, {1:^child^children}, and {2:^kitten^kittens}", 
         13, 1, 42
      ));
   }
}

The output is, as one might've guessed (and as seen on ideone.com):

13 puppies, 1 child, and 42 kittens

MSDN links

polygenelubricants