/// <summary> /// <para>Turns a TimeSpan into a human-readable text.</para> /// <para>Uses the specified timeSpanFormatOptions.</para> /// <para>For example: "31.23:59:00.555" = "31 days 23 hours 59 minutes 0 seconds 555 milliseconds"</para> /// </summary> /// <param name="FromTime"></param> /// <param name="options"> /// <para>A combination of flags that determine the formatting options.</para> /// <para>These will be combined with the default timeSpanFormatOptions.</para> /// </param> /// <param name="timeTextInfo">An object that supplies the text to use for output</param> public static string ToTimeString(this TimeSpan FromTime, TimeSpanFormatOptions options, TimeTextInfo timeTextInfo) { // If there are any missing options, merge with the defaults: // Also, as a safeguard against missing DefaultFormatOptions, let's also merge with the AbsoluteDefaults: options = options.Merge(DefaultFormatOptions).Merge(AbsoluteDefaults); // Extract the individual options: var rangeMax = options.Mask(TimeSpanFormatOptions._Range).AllFlags().Last(); var rangeMin = options.Mask(TimeSpanFormatOptions._Range).AllFlags().First(); var truncate = options.Mask(TimeSpanFormatOptions._Truncate).AllFlags().First(); var lessThan = options.Mask(TimeSpanFormatOptions._LessThan) != TimeSpanFormatOptions.LessThanOff; var abbreviate = options.Mask(TimeSpanFormatOptions._Abbreviate) != TimeSpanFormatOptions.AbbreviateOff; var round = lessThan ? (Func <double, double>)Math.Floor : Math.Ceiling; switch (rangeMin) { case TimeSpanFormatOptions.RangeWeeks: FromTime = TimeSpan.FromDays(round(FromTime.TotalDays / 7) * 7); break; case TimeSpanFormatOptions.RangeDays: FromTime = TimeSpan.FromDays(round(FromTime.TotalDays)); break; case TimeSpanFormatOptions.RangeHours: FromTime = TimeSpan.FromHours(round(FromTime.TotalHours)); break; case TimeSpanFormatOptions.RangeMinutes: FromTime = TimeSpan.FromMinutes(round(FromTime.TotalMinutes)); break; case TimeSpanFormatOptions.RangeSeconds: FromTime = TimeSpan.FromSeconds(round(FromTime.TotalSeconds)); break; case TimeSpanFormatOptions.RangeMilliSeconds: FromTime = TimeSpan.FromMilliseconds(round(FromTime.TotalMilliseconds)); break; } // Create our result: var textStarted = false; var result = new StringBuilder(); for (var i = rangeMax; i >= rangeMin; i = (TimeSpanFormatOptions)((int)i >> 1)) { // Determine the value and title: int value; switch (i) { case TimeSpanFormatOptions.RangeWeeks: value = (int)Math.Floor(FromTime.TotalDays / 7); FromTime -= TimeSpan.FromDays(value * 7); break; case TimeSpanFormatOptions.RangeDays: value = (int)Math.Floor(FromTime.TotalDays); FromTime -= TimeSpan.FromDays(value); break; case TimeSpanFormatOptions.RangeHours: value = (int)Math.Floor(FromTime.TotalHours); FromTime -= TimeSpan.FromHours(value); break; case TimeSpanFormatOptions.RangeMinutes: value = (int)Math.Floor(FromTime.TotalMinutes); FromTime -= TimeSpan.FromMinutes(value); break; case TimeSpanFormatOptions.RangeSeconds: value = (int)Math.Floor(FromTime.TotalSeconds); FromTime -= TimeSpan.FromSeconds(value); break; case TimeSpanFormatOptions.RangeMilliSeconds: value = (int)Math.Floor(FromTime.TotalMilliseconds); FromTime -= TimeSpan.FromMilliseconds(value); break; default: // This code is unreachable, but it prevents compile-errors. throw new ArgumentException("TimeSpanUtility"); } //Determine whether to display this value var displayThisValue = false; var breakFor = false; // I wish C# supported "break for;" (like how VB supports "Exit For" from within a "Select Case" statement) switch (truncate) { case TimeSpanFormatOptions.TruncateShortest: if (textStarted) { breakFor = true; break; } if (value > 0) { displayThisValue = true; } break; case TimeSpanFormatOptions.TruncateAuto: if (value > 0) { displayThisValue = true; } break; case TimeSpanFormatOptions.TruncateFill: if (textStarted || value > 0) { displayThisValue = true; } break; case TimeSpanFormatOptions.TruncateFull: displayThisValue = true; break; } if (breakFor) { break; } //we need to display SOMETHING (even if it's zero) if (i == rangeMin && textStarted == false) { displayThisValue = true; if (lessThan && value < 1) { // Output the "less than 1 unit" text: var unitTitle = timeTextInfo.GetUnitText(rangeMin, 1, abbreviate); result.Append(timeTextInfo.GetLessThanText(unitTitle)); displayThisValue = false; } } // Output the value: if (displayThisValue) { if (textStarted) { result.Append(" "); } var unitTitle = timeTextInfo.GetUnitText(i, value, abbreviate); result.Append(unitTitle); textStarted = true; } } return(result.ToString()); }
/// <summary> /// <para>Turns a TimeSpan into a human-readable text.</para> /// <para>Uses the specified timeSpanFormatOptions.</para> /// <para>For example: "31.23:59:00.555" = "31 days 23 hours 59 minutes 0 seconds 555 milliseconds"</para> /// </summary> /// <param name="FromTime"></param> /// <param name="options"> /// <para>A combination of flags that determine the formatting options.</para> /// <para>These will be combined with the default timeSpanFormatOptions.</para> /// </param> /// <param name="timeTextInfo">An object that supplies the text to use for output</param> public static string ToTimeString(this TimeSpan FromTime, TimeSpanFormatOptions options, TimeTextInfo timeTextInfo) { // If there are any missing options, merge with the defaults: // Also, as a safeguard against missing DefaultFormatOptions, let's also merge with the AbsoluteDefaults: options = options.Merge(DefaultFormatOptions).Merge(AbsoluteDefaults); // Extract the individual options: var rangeMax = options.Mask(TimeSpanFormatOptions._Range).AllFlags().Last(); var rangeMin = options.Mask(TimeSpanFormatOptions._Range).AllFlags().First(); var truncate = options.Mask(TimeSpanFormatOptions._Truncate).AllFlags().First(); var lessThan = options.Mask(TimeSpanFormatOptions._LessThan) != TimeSpanFormatOptions.LessThanOff; var abbreviate = options.Mask(TimeSpanFormatOptions._Abbreviate) != TimeSpanFormatOptions.AbbreviateOff; var round = (lessThan ? (Func<double, double>)Math.Floor : Math.Ceiling); switch (rangeMin) { case TimeSpanFormatOptions.RangeWeeks: FromTime = TimeSpan.FromDays(round(FromTime.TotalDays / 7) * 7); break; case TimeSpanFormatOptions.RangeDays: FromTime = TimeSpan.FromDays(round(FromTime.TotalDays)); break; case TimeSpanFormatOptions.RangeHours: FromTime = TimeSpan.FromHours(round(FromTime.TotalHours)); break; case TimeSpanFormatOptions.RangeMinutes: FromTime = TimeSpan.FromMinutes(round(FromTime.TotalMinutes)); break; case TimeSpanFormatOptions.RangeSeconds: FromTime = TimeSpan.FromSeconds(round(FromTime.TotalSeconds)); break; case TimeSpanFormatOptions.RangeMilliSeconds: FromTime = TimeSpan.FromMilliseconds(round(FromTime.TotalMilliseconds)); break; } // Create our result: bool textStarted = false; var result = new StringBuilder(); for (var i = rangeMax; i >= rangeMin; i=(TimeSpanFormatOptions)((int)i>>1)) { // Determine the value and title: int value; switch (i) { case TimeSpanFormatOptions.RangeWeeks: value = (int)Math.Floor(FromTime.TotalDays / 7); FromTime -= TimeSpan.FromDays(value * 7); break; case TimeSpanFormatOptions.RangeDays: value = (int)Math.Floor(FromTime.TotalDays); FromTime -= TimeSpan.FromDays(value); break; case TimeSpanFormatOptions.RangeHours: value = (int)Math.Floor(FromTime.TotalHours); FromTime -= TimeSpan.FromHours(value); break; case TimeSpanFormatOptions.RangeMinutes: value = (int)Math.Floor(FromTime.TotalMinutes); FromTime -= TimeSpan.FromMinutes(value); break; case TimeSpanFormatOptions.RangeSeconds: value = (int)Math.Floor(FromTime.TotalSeconds); FromTime -= TimeSpan.FromSeconds(value); break; case TimeSpanFormatOptions.RangeMilliSeconds: value = (int)Math.Floor(FromTime.TotalMilliseconds); FromTime -= TimeSpan.FromMilliseconds(value); break; default: // This code is unreachable, but it prevents compile-errors. throw new ArgumentException("TimeSpanUtility"); } //Determine whether to display this value bool displayThisValue = false; bool breakFor = false; // I wish C# supported "break for;" (like how VB supports "Exit For" from within a "Select Case" statement) switch (truncate) { case TimeSpanFormatOptions.TruncateShortest: if (textStarted) { breakFor = true; break; } if (value > 0) displayThisValue = true; break; case TimeSpanFormatOptions.TruncateAuto: if (value > 0) displayThisValue = true; break; case TimeSpanFormatOptions.TruncateFill: if (textStarted || value > 0) displayThisValue = true; break; case TimeSpanFormatOptions.TruncateFull: displayThisValue = true; break; } if (breakFor) break; //we need to display SOMETHING (even if it's zero) if (i == rangeMin && textStarted == false) { displayThisValue = true; if (lessThan && value < 1) { // Output the "less than 1 unit" text: var unitTitle = timeTextInfo.GetUnitText(rangeMin, 1, abbreviate); result.Append(timeTextInfo.GetLessThanText(unitTitle)); displayThisValue = false; } } // Output the value: if (displayThisValue) { if (textStarted) result.Append(" "); var unitTitle = timeTextInfo.GetUnitText(i, value, abbreviate); result.Append(unitTitle); textStarted = true; } } return result.ToString(); }