【问题标题】:Get next date within a quarter在一个季度内获取下一个日期
【发布时间】:2010-01-13 15:59:39
【问题描述】:

我需要创建一个函数来返回给定项目的下一个处理日期。一个项目有一个数字表示处理它的一个季度内的月份,以及一个表示该月内处理它的星期的数字。因此,给定特定项目的创建日期,我需要获取该项目的下一个处理日期,这将是该项目在一个季度内指定的周和月的第一天。

请注意,无论一周中的哪一天,每周都会从月初开始以 7 天为间隔。因此,第一周的第一天可以从星期二开始,也可以从其他任何一天开始计算。

示例:
假设我有一个完成日期为 2010 年 1 月 8 日的项目。该项目的monthWithinQuarter 值为2。它的weekWithinMonth 值为3。因此,对于解析为2 月第三周的该项目,我希望该函数返回2010 年2 月15 日的日期。

函数应该如下所示:

var nextProcessingDate = GetNextProcessingDate(
                             itemCompletedDate,
                             monthWithinQuarter,
                             weekWithinMonth);

这个计算必须非常快,因为这个计算会发生很多,无论是实时显示在网站上,还是在处理项目时以批处理模式显示。

谢谢,

~贾斯汀

【问题讨论】:

  • GetNextProcessingDate(new DateTime(2010, 3, 31), 3, 1) 的结果是什么?假设3/31/2010 是第一季度的最后一天,如果不提供进一步的说明,我不知道您将如何解决这个问题。
  • 在给定日期 3/31 和第 3 个月和第 1 周的情况下询问下一个日期应该是 2010 年 6 月 1 日。这是该项目的下一个可用处理日期。 2010 年 3 月 1 日已经过去,所以不可用。
  • 好的,那么我认为我当前的版本应该可以为您完成这项工作。
  • 如果有人愿意将工作交给其他人,那就太酷了……

标签: c# .net


【解决方案1】:

好的,这应该为你做:

static DateTime GetNextProcessingDate(
    DateTime itemCompletedDate,
    int monthWithinQuarter,
    int weekWithinMonth
) {
        if (monthWithinQuarter < 1 || monthWithinQuarter > 3) {
            throw new ArgumentOutOfRangeException("monthWithinQuarter");
        }
        if (weekWithinMonth < 1 || weekWithinMonth > 5) {
            throw new ArgumentOutOfRangeException("weekWithinMonth");
        }
        int year = itemCompletedDate.Year;
        DateTime[] startOfQuarters = new[] {
            new DateTime(year, 1, 1),
            new DateTime(year, 4, 1),
            new DateTime(year, 7, 1),
            new DateTime(year, 10, 1)
        };
        DateTime startOfQuarter = startOfQuarters.Where(d => d <= itemCompletedDate)
                                                 .OrderBy(d => d)
                                                 .Last();
        int month = startOfQuarter.Month + monthWithinQuarter - 1;
        int day = (weekWithinMonth - 1) * 7 + 1;
        if (day > DateTime.DaysInMonth(year, month)) {
            throw new ArgumentOutOfRangeException("weekWithinMonth");
        }
        DateTime candidate = new DateTime(year, month, day);
        if (candidate < itemCompletedDate) {
            month += 3;
            if(month > 12) {
                year++;
                month -= 12;
            }
        }
        return new DateTime(year, month, day);
    }

就效率而言,我认为最有改进空间的地方是重复创建数组

DateTime[] startOfQuarters = new[] {
    new DateTime(year, 1, 1),
    new DateTime(year, 4, 1),
    new DateTime(year, 7, 1),
    new DateTime(year, 10, 1)
};

所以让我们把它卸载到一个方法中并记住它:

static Dictionary<int, DateTime[]> cache = new Dictionary<int, DateTime[]>();
public static DateTime[] StartOfQuarters(DateTime date) {
    int year = date.Year;
    DateTime[] startOfQuarters;
    if(!cache.TryGetValue(year, out startOfQuarters)) {
        startOfQuarters = new[] {
            new DateTime(year, 1, 1),
            new DateTime(year, 4, 1),
            new DateTime(year, 7, 1),
            new DateTime(year, 10, 1)
        };
        cache.Add(year, startOfQuarters);
    }
    return startOfQuarters;
}

如果您不需要可能在不寻常的日子开始的季度灵活性,您可以替换

DateTime[] startOfQuarters = new[] {
    new DateTime(year, 1, 1),
    new DateTime(year, 4, 1),
    new DateTime(year, 7, 1),
    new DateTime(year, 10, 1)
};
DateTime startOfQuarter = startOfQuarters.Where(d => d <= itemCompletedDate).OrderBy(d => d).Last();
int month = startOfQuarter.Month + monthWithinQuarter - 1;

int month = 3 * ((itemCompletedDate.Month - 1) / 3) + monthWithinQuarter;

【讨论】:

  • 与我的解决方案非常相似,在大多数情况下......检测季度的方法可能是矫枉过正或者是 OP 想要的。无法确定当前信息。
  • 是的,这取决于。我来自一个季度开始通常不是1/14/17/110/1 的世界,所以我总是为他们不是这样的可能性做准备。
  • @Jason:这是一个疯狂尝试投反对票的人 :))
  • 对实际问题进行了一些小的修改,这非常有效。谢谢!
【解决方案2】:

据我了解,这应该可以完成工作:

public static DateTime GetNextProcessingDate(DateTime itemCreationDate, int monthWithinQuarter,
    int weekWithinMonth)
{
    var quarter = (itemCreationDate.Month - 1) / 4; // Assumes quarters are divided by calendar year.
    var month = quarter * 4 + monthWithinQuarter; // First quarter of month plus month within quarter
    var dayInMonth = (weekWithinMonth - 1) * 7 + 1; // Weeks are counted from first day, regardless of day of week (as you mention).
    return new DateTime(itemCreationDate.Year, month, dayInMonth);
}

如果有不清楚的地方请告诉我。

【讨论】:

  • 这不适用于itemCreationDate = new DateTime(2010, 3, 31), monthWithinQuarter = 3weekWithinMonth = 1
  • 这也不适用于itemCreationDate = new DateTime(2010, 1, 1), monthWithinQuarter = 1 and weekWithinMonth = 1
  • @Jason。当然可以。我不明白为什么不...这真的值得投反对票吗?
  • 哦 hrmm...小错误,刚刚修复。
【解决方案3】:

你的计算,我想应该减少

    DateTime dt;
    dt.AddDays(daysToAdd);
    dt.AddMonths(monthsToAdd);
    dt.AddHours(hoursToAdd);
    dt.AddYears(yearsToAdd);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多