我想出了一个我认为是个好问题:我如何获得指定日期格式的两个DateTimes之间的每个标签。 想知道是否有人比我想出的更优雅的解决方案。 (除了在编译时知道格式)或任何优化它的方法。
我正在使用它生成一个图表,其中并非所有时间段都有值(因此.GroupBy(date => date.ToString(dateFormat))
会导致跳过任何零元素的时间段)
例如:输出2011/01/01和2011/03/21之间的每个标签,日期格式为“yyyyMM”
GetTimeSegmentLables(new DateTime(2011,1,1), new DateTime(2011,3,21), "yyyyMM").Dump();
这也应该支持"yyyy MM 'Month' dddd\\'\\s"
这就是我想出的:
public static List<string> GetTimeSegmentLabels(DateTime startDate, DateTime endDate, string dateFormat)
{
DateTime incrementedDate = startDate;
TimeSpan increment = GetSmallestUnitInDateFormat(dateFormat);
int estimatedCount = (int)((endDate - startDate).TotalMilliseconds / increment.TotalMilliseconds + 1);
List<string> Formats = new List<string>(estimatedCount);
string LastFormat = "";
while (incrementedDate < endDate)
{
string next = incrementedDate.ToString(dateFormat);
if (next != LastFormat)
{
Formats.Add(next);
LastFormat = next;
}
incrementedDate = incrementedDate.Add(increment);
}
if (LastFormat != endDate.ToString(dateFormat))
Formats.Add(endDate.ToString(dateFormat));
return Formats;
}
public static TimeSpan GetSmallestUnitInDateFormat(string dateFormat)
{
//Remove escaped characters
string stripped = Regex.Replace(dateFormat, "(\\\\.|'[^']+'|\"[^\"]+\")", "");
//Check for format strings in increasing order
if (stripped.Contains("F") || stripped.Contains("F"))
{
//TODO find longest [fF]+ string
}
if (stripped.Contains("s"))
{
return new TimeSpan(0, 0, 1);
}
if (stripped.Contains("m"))
{
return new TimeSpan(0, 1, 0);
}
if (stripped.Contains("h") || stripped.Contains("H"))
{
return new TimeSpan(1, 0, 0);
}
if (stripped.Contains("d"))
{
return new TimeSpan(1, 0, 0, 0);
}
if (stripped.Contains("M"))
{
//30 is the average month (365.25/12) but there is a chance it would skip Feburary...
//So 28 should hit every month for each year, hitting one twice
return new TimeSpan(28, 0, 0, 0);
}
if (stripped.Contains("y"))
{
return new TimeSpan(365, 0, 0, 0);
}
throw new ArgumentOutOfRangeException("Unable to find any supported Format Specifier");
}