生成时间段标签、最小时间跨度以递增日期时间格式
我提出了一个我认为很好的问题:如何获取指定日期格式的两个日期时间之间的每个标签。 想知道是否有人有比我想出的更优雅的解决方案。 (除了在编译时知道格式之外)或任何优化它的方法。
我使用它来生成一个图表,其中并非所有时间段都有值(因此 .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");
}
I came up with what I thought was a good question: How do I get every label between two DateTimes for a specified Date Format.
Wondered if anyone had a more elegant solution than what I came up with. (Other than knowing the format at compile time) Or any ways to optimize this.
I'm using this to generate a graph where not all time segments have a value (so .GroupBy(date => date.ToString(dateFormat))
results in skipping any time period that has zero elements)
For example: Output every label between 2011/01/01 and 2011/03/21 with the date format "yyyyMM"GetTimeSegmentLables(new DateTime(2011,1,1), new DateTime(2011,3,21), "yyyyMM").Dump();
This should also supports a format such as "yyyy MM 'Month' dddd\\'\\s"
This is What I came up with:
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");
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论