我想出了一个我认为很好的问题:如何获取指定日期格式的两个日期时间之间的每个标签。想知道是否有人有比我想出的更优雅的解决方案。(除了在编译时知道格式)或任何优化它的方法。
我正在使用它来生成一个图表,其中并非所有时间段都有一个值(因此 .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");
}