【问题标题】:How to check if the current day is the Nth or Nth from last business day of the month?如何检查当前日期是每月最后一个工作日的第 N 天还是第 N 天?
【发布时间】:2014-07-28 17:01:39
【问题描述】:

我知道有人问过类似的问题,但我觉得这个问题有不同的要求。

我需要检查当前日期是该月最后一个工作日的第 N 天还是第 N 天。如果是,我开始另一个任务,否则我退出。

类似:

void doTask()
{
    if (NthFirstOrNthLastBusinessDay())
    {  // do stuff  }
    else 
    {  // do nothing  }
}

bool NthFirstOrNthLastBusinessDay()
{}

到目前为止,我想我可以按照答案here 来获取当月的最后一个工作日(同样是当月的第一个工作日),然后应用一堆条件来计算第 N 个工作日那个日期。

有没有更好的办法?

例如,如果 N 为 4,我的任务应该仅在今天是该月的第 4 个工作日或该月最后一个工作日的第 4 个工作日时运行。

本质上,我每天都会调用这个方法,它检查当前日期是否满足这个条件,如果满足则运行任务,否则不运行。

所以,我的任务应该在 2014 年 7 月 28 日和 2014 年 8 月 6 日运行,然后是 2014 年 8 月 26 日,依此类推。

编辑: 这是我在 N=4 的情况下编写的一些代码:

    /// <summary>
    /// Checks if the current day is the 4th business day or the 4th last business day of the month
    /// </summary>
    /// <returns> True if current day satisfies condition, false otherwise </returns>
    private bool NthFirstOrNthLastBusinessDay()
    {
        return (IsFourthBusinessDay() || IsFourthFromLastBusinessDay());
    }

    /// <summary>
    /// Checks if today is the fourth business day of the month
    /// </summary>
    /// <returns> True if today satisfies condition, else false </returns>
    private bool IsFourthBusinessDay()
    {
        var dateToday = DateTime.Today;

        var firstDayOfThisMonth = new DateTime(dateToday.Year, dateToday.Month, 1);

        switch (firstDayOfThisMonth.DayOfWeek)
        {
            case DayOfWeek.Sunday:
                firstDayOfThisMonth = firstDayOfThisMonth.AddDays(4);
                break;
            case DayOfWeek.Monday:
                firstDayOfThisMonth = firstDayOfThisMonth.AddDays(3);
                break;
            case DayOfWeek.Tuesday:
                firstDayOfThisMonth = firstDayOfThisMonth.AddDays(3);
                break;
            default:
                firstDayOfThisMonth = firstDayOfThisMonth.AddDays(5);
                break;
        }  // firstDayOfMonth now contains first business day of month

        return dateToday == firstDayOfThisMonth;
    }

    /// <summary>
    /// Checks if today is the fourth from last business day of the month
    /// </summary>
    /// <returns> True if today satisfies condition, else false </returns>
    private bool IsFourthFromLastBusinessDay()
    {
        var dateToday = DateTime.Today;

        var lastDayOfThisMonth = new DateTime(dateToday.Year, dateToday.Month, DateTime.DaysInMonth(dateToday.Year, dateToday.Month));

        switch (lastDayOfThisMonth.DayOfWeek)
        {
            case DayOfWeek.Saturday:
                lastDayOfThisMonth = lastDayOfThisMonth.AddDays(-4);
                break;
            case DayOfWeek.Friday:
                lastDayOfThisMonth = lastDayOfThisMonth.AddDays(-3);
                break;
            case DayOfWeek.Thursday:
                lastDayOfThisMonth = lastDayOfThisMonth.AddDays(-3);
                break;
            default:
                lastDayOfThisMonth = lastDayOfThisMonth.AddDays(-5);
                break;
        }  // firstDayOfMonth now contains first business day of month

        return dateToday == lastDayOfThisMonth;
    }

现在我需要调整一般 N 的值。有更好的方法吗?

【问题讨论】:

  • 我不明白你所说的第 n 个第一个或第 n 个最后一个是什么意思……对我来说,一个例子应该是:“本月的第三个第一个工作日是什么?”这没有意义
  • 我认为这意味着Nth或Nth from last...?为什么不找到所有工作日并将它们放入List。然后days[3]每月第三个工作日。
  • @Tom 很抱歉造成混乱。添加了用例。是不是更清楚了?
  • @crashmstr,你是对的。英语不是我的母语,抱歉。另外,我觉得应该有比你更好的解决方案,因为我应该能够检查当天,而不是每次运行任务时计算该月的所有工作日。你怎么看?
  • 要检查一天是否是第 N 个(或倒数第 N 个)工作日,您需要知道之前(或之后)有多少个工作日。因此,您仍然需要检查其他日子,对吗?

标签: c# .net


【解决方案1】:

根据我原来回答的 OP cmets,我决定编写另一个函数作为练习。

在这里(再次 - 未完全测试):

// Nth day should not be larger than 14 - I have tested it
bool Nth_BusinessDay(int NthDay, DateTime current)
{
    // check we have NthDay larger than ZERO
    if (NthDay < 1)
        return false;

    // make sure the taget date is just a date - no time component
    DateTime target = current.Date;

    // make sure the specialDay is not larger than the current days in the month
    if (DateTime.DaysInMonth(target.Year, target.Month) > NthDay)
        return false;

    // start at the beginning
    DateTime NthStart = new DateTime(target.Year, target.Month, 1);
    // check if start date falls on the weekend
    if (NthStart.DayOfWeek == DayOfWeek.Saturday)
        NthStart = NthStart.AddDays(2); // jump TWO days to Monday
    if (NthStart.DayOfWeek == DayOfWeek.Sunday)
        NthStart = NthStart.AddDays(1); // jump ONE day to Monday

    // now add our Nth day
    NthStart = NthStart.AddDays(NthDay - 1);

    // if we land on weekend after we adjusted for the Nth - push it by two days
    if (NthStart.DayOfWeek == DayOfWeek.Saturday || NthStart.DayOfWeek == DayOfWeek.Sunday)
        NthStart = NthStart.AddDays(2);

    // start at the end and minus the Nth day
    DateTime NthEnd = new DateTime(target.Year, target.Month, DateTime.DaysInMonth(target.Year, target.Month));
    // check if the end date falls on the weekend - the difference here is the adjustment
    if (NthEnd.DayOfWeek == DayOfWeek.Saturday)
        NthEnd = NthEnd.AddDays(-1); // jump ONE day back to Friday
    if (NthEnd.DayOfWeek == DayOfWeek.Sunday)
        NthEnd = NthEnd.AddDays(-2); // jump TWO days back to Friday

    // now subject our Nth day from the End
    NthEnd = NthEnd.AddDays((NthDay - 1) * -1);

    // again if we land on weekend - adjust by two days
    if (NthStart.DayOfWeek == DayOfWeek.Saturday || NthStart.DayOfWeek == DayOfWeek.Sunday)
        NthStart = NthStart.AddDays(2);


    // check if our target day is one of the Nth day
    if (NthStart == target || NthEnd == target)
        return true;

    return false;
}

并使用上面的功能:

void doTaskNthDay()
{
    if (Nth_BusinessDay(5, DateTime.Now))
    {
        // do stuff
    }
}

以下是我留下的原始答案,仅供参考。

我从.NET Date Compare: Count the amount of working days since a date? 获取了以下代码。 我还没有测试过这段代码,但你可以试试这样的:

int BuinessDayCount(DateTime targetDay)
{
    // check if the target day is a weekend
    if (targetDay.DayOfWeek == DayOfWeek.Saturday)
        return -1;
    if (targetDay.DayOfWeek == DayOfWeek.Sunday)
        return -2;

    // get the start of the month
    DateTime start = new DateTime(targetDay.Year, targetDay.Month, 1);

    // create a target without any time component
    DateTime end = targetDay.Date;

    int workingDays = 0;
    while (start <= targetDay)
    {
        if (start.DayOfWeek != DayOfWeek.Saturday
         && start.DayOfWeek != DayOfWeek.Sunday)
        {
            workingDays++;
        }
        start = start.AddDays(1);
    }

    return workingDays;
}

然后在您的代码中我会执行以下操作:

void doTask()
{
    int businessDay = BuinessDayCount(DateTime.Now);
    if (businessDay == 5 || businessDay == 25)
    {  
        // do stuff
    }
}

我认为它会更好读。

【讨论】:

  • 这看起来比我想要的要好。谢谢。我分别验证了这两个条件。
  • 建议进行修改 - 第 25 个工作日可能不是当月最后一个工作日的第 5 个工作日(在某些月份甚至可能不存在)。其他人建议 DateTime.DaysInMonth 属性在这里很有用。
  • 如果当月的第一天是星期日(取 N=4)会怎样?然后: DateTime NthStart = new DateTime(target.Year, target.Month, 1).AddDays(NthDay - 1) ... 这给了我们一个星期三。而本月的第一个工作日应该是星期一,因此正确的 NthStart 应该是星期四。我理解正确吗?
  • 你是对的。我没有考虑到这个月的第一天和最后一天可能是周末。我会做出调整的。
  • 相当不错,但是一周只有5个工作日。如果您正在寻找一个月的第 11 个工作日,那里有 2 个周末,所以实际上是在该月的第 14 天左右。我不确定你的逻辑是否能正确处理这种情况。
【解决方案2】:

我会这样做:

int numOfOccurredBusinessDays = 0; // this will include current business day
int businessDaysLeft = 0;
var currentDate = DateTime.Now;
var monthIterator = new DateTime(DateTime.Year, DateTime.Month, 1);

for (int i = 0; i < DateTime.DaysInMonth; ++i, monthIterator.AddDay(1))
{
    if (monthIterator <= currentDate &&
     !monthIterator.Date.ToString("D").Contains("Sat") &&
     !monthIterator.Date.ToString("D").Contains("Sun"))
          numOfOccurredBusinessDays++;
    else if (monthIterator > currentDate &&
     !monthIterator.Date.ToString("D").Contains("Sat") &&
     !monthIterator.Date.ToString("D").Contains("Sun"))
          businessDaysLeft++;
}

【讨论】:

    【解决方案3】:
    1. 创建一个新的 System.DateTime 对象。用当前年份和当前月份(可以从 DateTime.Now 获取)对其进行初始化。如果是 9 月/4 月/6 月/11 月,或者 31、28 等,请将日期设置为 30。

    2. 现在您有一个代表该月最后一天的 DateTime 对象。您可以使用 DateTime.DayOfWeek 来检查这一天是星期六还是星期日(或节假日)。

    3. 您可以使用 DateTime.AddDays(-N) 将 DateTime 对象“倒退”一天,然后再次检查它是周六还是周日(或节假日)。

    4. 在每个月的最后 N 天重复您需要进行的任何检查。

    【讨论】:

    • 谁说这个问题是家庭作业?无论如何,如果一个问题写得很好并且切合主题,那么家庭作业问题就没有问题。
    • @crashmstr 感谢您的反馈,但在您发表评论之前我已经重新格式化。至于功课,这只是一个合理的猜测。 See here 元讨论
    • DateTime struct 有一个方法可以告诉您特定年份的特定月份有多少天。你不需要写那个逻辑。另一方面,假期需要一个日期列表来测试是否包含。
    • @TonyVitabile 好电话。我忘记了 DateTime.DaysInMonth 属性。
    • @cjcurrie,感谢您的提示。这是我现在正在遵循的方法(如果需要,可以在此处发布我的代码)。我发布问题的原因是看看是否有人已经知道执行此操作的最佳方法,或者是否有我可以使用的 .NET 函数。 (如果我想要代码审查,我会在 codereview.stackexchange 中发布。FWIW,这不是作业)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-22
    • 1970-01-01
    • 2020-02-24
    • 1970-01-01
    • 2015-07-03
    • 1970-01-01
    相关资源
    最近更新 更多