生成时间段标签、最小时间跨度以递增日期时间格式

发布于 2024-10-25 05:01:56 字数 2456 浏览 4 评论 0原文

我提出了一个我认为很好的问题:如何获取指定日期格式的两个日期时间之间的每个标签。 想知道是否有人有比我想出的更优雅的解决方案。 (除了在编译时知道格式之外)或任何优化它的方法。

我使用它来生成一个图表,其中并非所有时间段都有值(因此 .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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文