using System; using System.Collections.Generic; namespace InABox.Core { /// /// Custom string formatter for TimeSpan that allows easy retrieval of Total segments. /// /// /// TimeSpan myTimeSpan = new TimeSpan(27, 13, 5); /// string.Format("{0:th,###}h {0:mm}m {0:ss}s", myTimeSpan) -> "27h 13m 05s" /// string.Format("{0:TH}", myTimeSpan) -> "27.2180555555556" /// NOTE: myTimeSpan.ToString("TH") does not work. See Remarks. /// /// /// Due to a quirk of .NET Framework (up through version 4.5.1), /// TimeSpan.ToString(format, new TimeSpanFormatter()) will not work; it will always call /// TimeSpanFormat.FormatCustomized() which takes a DateTimeFormatInfo rather than an /// IFormatProvider/ICustomFormatter. DateTimeFormatInfo, unfortunately, is a sealed class. /// public class TimeSpanFormatter : IFormatProvider, ICustomFormatter { /// /// Used to create a wrapper format string with the specified format. /// private const string DefaultFormat = "{{0:{0}}}"; /// /// Determines whether the specified format is looking for a total, and formats it accordingly. /// If not, returns the default format for the given /// format /// of a TimeSpan. /// /// /// The formatted string for the given TimeSpan. /// /// /// ICustomFormatter.Format implementation. /// public string Format(string format, object arg, IFormatProvider formatProvider) { // only apply our format if there is a format and if the argument is a TimeSpan if (string.IsNullOrWhiteSpace(format) || formatProvider != this || // this should always be true, but just in case... !(arg is TimeSpan) || arg == null) // return the default for whatever our format and argument are return GetDefault(format, arg); var span = (TimeSpan)arg; var formatSegments = format.Split(new[] { ',' }, 2); var tsFormat = formatSegments[0].ToLower(); if (!string.IsNullOrWhiteSpace(tsFormat)) { var results = new List(); var segments = tsFormat.Replace("\\", "").Split(':'); try { foreach (var segment in segments) if (segment.Equals("d")) results.Add(span.Days.ToString()); else if (segment.Equals("dd")) results.Add(span.Days.ToString("D2")); else if (segment.Equals("h")) results.Add(span.Hours.ToString()); else if (segment.Equals("hh")) results.Add(span.Hours.ToString("D2")); else if (segment.Equals("hhh")) results.Add(((int)span.TotalHours).ToString()); else if (segment.Equals("mm")) results.Add(span.Minutes.ToString("D2")); else if (segment.Equals("ss")) results.Add(span.Seconds.ToString("D2")); else if (segment.Equals("zzz")) results.Add(span.Milliseconds.ToString("D3")); return string.Join(":", results); } catch { return GetDefault(format, arg); } } return span.ToString(); } /// /// IFormatProvider.GetFormat implementation. /// public object GetFormat(Type formatType) { // Determine whether custom formatting object is requested. if (formatType == typeof(ICustomFormatter)) return this; return null; } /// /// Returns the formatted value when we don't know what to do with their specified format. /// private string GetDefault(string format, object arg) { return string.Format(string.Format(DefaultFormat, format), arg); } } }