【问题标题】:Get DateTime of the next nth day of the month获取该月下一个第 n 天的 DateTime
【发布时间】:2017-06-17 14:07:04
【问题描述】:

如果给定一个日期和一个变量 n,我如何计算该月的第 n 个日期的 DateTime?

例如,今天是 6 月 17 日。 我想要一个函数,当提供 15 时,它会返回 7 月 15 日的 DateTime。

还有几个例子:

  • 今天是 2 月 26 日:当提供 30 时,函数将在 3 月 30 日返回。
  • 今天是 12 月 28 日。提供 4 时,函数将返回 1 月 4 日。
  • 今天是 2 月 28 日。如果提供 29,函数将返回 3 月 29 日,除非是闰年,在这种情况下,它将返回 2 月 29 日。

【问题讨论】:

  • 为什么 1 月 15 日之后的第 15 天应该是 3 月 30 日? o.O
  • 应该是 30/01 吧?
  • @Sajeetharan 这取决于 OP 在第 15 天想要什么。可以是一样的,可以是下个月的15号,反正不会是30号,至少我希望
  • 功能规格不完整。您必须描述在请求时应该发生什么,例如 2 月 29 日或 3 月 31 日。
  • 也许将标题更改为“获取每月第 n 天的下一个 DateTime”或类似的内容,以便更容易找到?

标签: c# datetime


【解决方案1】:

为什么不干呢?

private DateTime GetNextDate(DateTime dt, int DesiredDay)
{
    if (DesiredDay >= 1 && DesiredDay <= 31)
    {
        do
        {
            dt = dt.AddDays(1);
        } while (dt.Day != DesiredDay);
        return dt.Date;
    }
    else
    {
        throw new ArgumentOutOfRangeException();
    }     
}

【讨论】:

  • 我喜欢这个答案的简单性,但会担心计算时间过长。
  • +1 检查 ~60 天可能比在我的回答中捕获一个异常更好。只需要防止无效输入的无限循环,这个答案很好。
  • @Amy B,3 年后,这段代码终于修改为您的确切解决方案。可读性更强,更容易理解。
【解决方案2】:

经过多次修改、更正和重写,这是我的最终答案:

下面的方法返回一个DateTime,表示下一次数字day 出现在日历中。它使用迭代方法,并以DateTime 对象的扩展方法的形式编写,因此不受今天日期的约束,但适用于任何日期。

代码执行以下步骤以获得所需的结果:

  1. 确保提供的天数有效(大于零且小于 32)。
  2. 进入一个一直持续的 while 循环(直到我们中断)。
  3. 检查cDate 的月份是否有效(这一天必须没有过去,并且该月必须有足够的天数)。
    • 如果是,请返回。
    • 如果不是,则将月份加一,将日期设置为 1,将 includeToday 设置为 true,以便包含新月份的第一天,然后再次执行循环。

代码:

static DateTime GetNextDate3(this DateTime cDate, int day, bool includeToday = false)
{
    // Make sure provided day is valid
    if (day > 0 && day <= 31)
    {
        while (true)
        {
            // See if day has passed in current month or is not contained in it at all
            if ((includeToday && day > cDate.Day || (includeToday && day >= cDate.Day)) && day <= DateTime.DaysInMonth(cDate.Year, cDate.Month))
            {
                // If so, break and return
                break;
            }

            // Advance month by one and set day to one
            // FIXED BUG HERE (note the order of the two calls)
            cDate = cDate.AddDays(1 - cDate.Day).AddMonths(1);

            // Set includeToday to true so that the first of every month is taken into account
            includeToday = true;
        }
        // Return if the cDate's month contains day and it hasn't passed
        return new DateTime(cDate.Year, cDate.Month, day);
    }

    // Day provided wasn't a valid one
    throw new ArgumentOutOfRangeException("day", "Day isn't valid");
}

【讨论】:

  • 当您将它们放入 DateTime 的扩展方法中时,它会更加灵活,并且不会与 Today 紧密绑定
  • 重写了所有内容,这一次头脑清醒。 @DavidB 感谢您的反馈,但事实证明该方法不是必需的。
  • @Sty,谢谢!实际上,大约 3 小时前,我将您的预编辑解决方案重写为一个函数。但是,我将使用您当前在上面发布的迭代版本。谢谢!我需要返回并对该解决方案进行单元测试,并在符合其准确性和精确度后立即将其标记为答案。
  • cDate.AddMonths(1).AddDays(1 - cDate.Day) ... 2017-01-31 的此操作产生 2017-01-29。我建议在增加月份之前设置日期。
  • @W.Scott 查看已修复错误的版本。我意识到,由于if 的编写方式。从未考虑过该月的第一天。在这个新版本中,我添加了一个 includeToday 参数,该参数在第一次循环执行后强制设置为 true。根据 David.B 的建议,我还修复了 cDate.AddDays().AddMonths();
【解决方案3】:

当今天是 dayOfMonth 时,规范有点不清楚。我认为它是返回相同的。否则只会改为

public DateTime FindNextDate(int dayOfMonth, DateTime today)
{
    var nextMonth = new DateTime(today.Year, today.Month, 1).AddMonths(1);
    if(dayOfMonth < today.Day){ 
      nextMonth = nextMonth.AddMonths(1);
    }
    while(nextMonth.AddDays(-1).Day < dayOfMonth){
       nextMonth = nextMonth.AddMonths(1);
    }
    var month = nextMonth.AddMonths(-1);
    return new DateTime(month.Year, month.Month, dayOfMonth);

}

【讨论】:

    【解决方案4】:

    今天在尝试解决同样的问题时偶然发现了这个帖子。

    从我的测试来看,以下似乎运行良好,循环只需要两次(我认为?也许 3 max(?))就可以得到答案:

    public static DateTime GetNearestSpecificDay(DateTime start, int dayNum)
    {
        if (dayNum >= 1 && dayNum <= 31)
        {
            DateTime result = start;
            while (result.Day != dayNum)
                result = dayNum > result.Day ? result.AddDays(dayNum - result.Day) : new DateTime(result.Month == 12 ? result.Year + 1 : result.Year, (result.Month % 12) + 1, 1);
            return result;
        }
        else
            return DateTime.Today;
    }
    

    编辑:根据要求,这里有一个不太紧凑的版本,逐步介绍了逻辑。我还更新了原始代码,以考虑到 12 月所需的年份变化。

    public static DateTime GetNearestSpecificDay(DateTime start, int dayNum)
    {
        // Check if target day is valid in the Gregorian calendar
        if (dayNum >= 1 && dayNum <= 31)
        {
            // Declare a variable which will hold our result & temporary results
            DateTime result = start;
    
            // While the current result's day is not the desired day
            while (result.Day != dayNum)
            {
                // If the desired day is greater than the current day
                if (dayNum > result.Day)
                {
                    // Add the difference to try and skip to the target day (if we can, if the current month does not have enough days, we will be pushed into the next month and repeat)
                    result = result.AddDays(dayNum - result.Day);
                }
                // Else, get the first day of the next month, then try the first step again (which should get us where we want to be)
                else
                {
                    // If the desired day is less than the current result day, it means our result day must be in the next month (it obviously can't be in the current)
                    // Get the next month by adding 1 to the current month mod 12 (so when we hit december, we go to january instead of trying to use a not real 13th month)
                    // If result.Month is November, 11%12 = 11; 11 + 1 = 12, which rolls us into December
                    // If result.Month is December, 12%12 = 0; 0 + 1 = 1, which rolls us into January
                    var month = (result.Month % 12) + 1;
    
                    // Get current/next year.
                    // Since we are adding 1 to the current month, we can assume if the previous month was 12 that we must be entering into January of next year
                    // Another way to do this would be to check if the new month is 1. It accomplishes the same thing but I chose 12 since it doesn't require an extra variable in the original code.
                    // Below can be translated as "If last result month is 12, use current year + 1, else, use current year"
                    var year = result.Month == 12 ? result.Year + 1 : result.Year;
    
                    // Set result to the start of the next month in the current/next year
                    result = new DateTime(year, month, 1);
                }
            }
    
            // Return result
            return result;
        }
        else
            // If our desired day is invalid, just return Today. This can be an exception or something as well, just using Today fit my use case better.
            return DateTime.Today;
    }
    

    【讨论】:

    • 不错。或许你可以多解释一下逻辑。
    • @GertArnold 没问题。更新了帖子以解决问题并添加了代码的分解版本,并逐步说明了它在做什么。
    【解决方案5】:

    有趣的小谜题。我生成了 100 个日期时间,它们代表每个月的开始日期,然后每个月检查它是否有我们想要的日期。它很懒,所以当我们找到一个好的时我们就停下来。

    public DateTime FindNextDate(int dayOfMonth, DateTime today)
    {
      DateTime yesterday = today.AddDays(-1);
      DateTime currentMonthStart = new DateTime(today.Year, today.Month, 1);
      var query = Enumerable.Range(0, 100)
        .Select(i => currentMonthStart.AddMonths(i))
        .Select(monthStart => MakeDateOrDefault(
          monthStart.Year, monthStart.Month, dayOfMonth,
          yesterday)
        .Where(date => today <= date)
        .Take(1);
    
      List<DateTime> results = query.ToList();
      if (!results.Any())
      {
        throw new ArgumentOutOfRangeException(nameof(dayOfMonth))
      }
      return results.Single();
    }
    
    public DateTime MakeDateOrDefault(
      int year, int month, int dayOfMonth,
      DateTime defaultDate)
    {
      try
      {
        return new DateTime(year, month, dayOfMonth);
      }
      catch
      {
        return defaultDate;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-05
      • 2013-08-30
      • 2014-08-06
      • 2013-02-14
      相关资源
      最近更新 更多