计算给定星期几和月份中星期几的特定日期

Calculate specific date given a day of week and instance of week in the month

我有一种情况需要在公式字段中计算下个月的特定日期,我必须计算的值是:

-星期几(M、T、W 等) -实例周(每月的第 2 或第 3 周)

我建议了一个 Apex 解决方案,我们将提前一个月设置一个日期变量,计算该月的第一天是星期几,然后循环直到我们选择 "Day of week".到那时,我们可以将 7 天或 14 天添加到该值,这将为我们提供特定日期的每月第二周或第三周所需的内容。

唯一的问题是大约有 2000 条记录需要每晚检查和更新,我的公司不愿意让每晚的工作来检查和更新这个字段。

我一直在查找我可以在公式字段中设置的方程式,该公式字段将能够向我显示我们将调用它的 "Next Service Date",公式字段将能够随时计算该日期该页面被查看。 (或为此导出报告)。

我完全陷入了一个数学方程式,该方程式将使用今天的日期来查找下个月一周中特定日期的第 2 个或第 3 个实例。我觉得从研究中可以只用一个方程式来做到这一点,但我正在删除。

有没有人做过这样的事情?任何帮助将不胜感激。

令人惊讶的是,仅使用数学公式就可以找到给定工作日的第 n 次出现。下面我包含了 Java 代码来逐步进行计算。通过将公式代入其他公式,可以将公式序列简化为一个公式。我已经输出了很多调试信息,所以我可以密切关注计算的各个阶段。注意我只是简单地测试了程序;它应该针对广泛的输入进行测试,包括从 1995 年到 2017 年的日期。

import java.util.Calendar;

public class NextServiceDate {

  public static int // Jan = 1, Dec = 12, Sun = 1, Sat = 7
  nextServiceDate(int year, int month, int dayOfMonth, int dayOfWeek,
                  int targetDayOfWeek, int occurrenceOfDayOfWeek) {

    // We have to do all date calculations with mathematical formulas
    // all the formulas could be mashed together, if desired

    // Want March = 1, April = 2, May = 3, ..., January = 11, February = 12
    int monthMarchIs1 = (month+9)%12 + 1;
    System.out.println("Current month, where March is 1: " + monthMarchIs1);

    // want to know whether month is February
    int isMonthFeb = monthMarchIs1 / 12; // integer division
    System.out.println("Is month February: " + isMonthFeb);

    // want to know whether year is leap year
    // is year divisible by 4?
    int isYearDivBy4 = (1-(year%4+3)/4); // 0 = false, 1 = true
    System.out.println("Is year div by 4: " + isYearDivBy4);
    // is year divisible by 100?
    int isYearDivBy100 = (1-(year%100+99)/100);
    System.out.println("Is year div by 100: " + isYearDivBy100);
    // is year divisible by 400?
    int isYearDivBy400 = (1-(year%400+399)/400);
    System.out.println("Is year div by 400: " + isYearDivBy400);
    // Boolean logic to determine whether year is leap year
    // note year div by 400 implies year div by 100 so the result is never 2
    int isLeapYear = isYearDivBy4*(1-isYearDivBy100) + isYearDivBy400;
    System.out.println("Is year leap: " + isLeapYear);

    // calculate days in month using Curtis McEnroe's formula
    // http://cmcenroe.me/2014/12/05/days-in-month-formula.html
    // corrected for leap year using Boolean logic
    int daysInMonth = 28 + (month+month/8)%2 + 2%month + 2*(1/month)
      + isMonthFeb*isLeapYear;
    System.out.println("Days in month: " + daysInMonth);

    // number of days to the first of next month
    int daysToFirst = daysInMonth - dayOfMonth + 1;
    System.out.println("Days to the first of next month: " + daysToFirst);

    // weekday of first of next month
    int weekdayOfFirst = (dayOfWeek + daysToFirst) % 7; // Sat = 0
    System.out.println("Weekday of first of next month: " + weekdayOfFirst);

    // additional days to target day of week
    int dateOfTargetWeekday = (targetDayOfWeek-weekdayOfFirst+7)%7 + 1;
    System.out.println("Date of target day of week: " + dateOfTargetWeekday);

    // final answer: day of month of target date
    int dateOfTarget = dateOfTargetWeekday + (occurrenceOfDayOfWeek-1)*7;
    System.out.println("Day of month of target date: " + dateOfTarget);
    System.out.println();

    return dateOfTarget;
  }

  public static void main(String[] args) {
    final String[] MONTHABBR = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
                                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
    Calendar rightNow = Calendar.getInstance();
    int year = rightNow.get(Calendar.YEAR);
    int month = rightNow.get(Calendar.MONTH) + 1; // Java has Jan = 0
    int dayOfMonth = rightNow.get(Calendar.DAY_OF_MONTH);
    int dayOfWeek = rightNow.get(Calendar.DAY_OF_WEEK); // Sun = 1, Sat = 7

    System.out.println("Using as current year: " + year);
    System.out.println("Using as current month: " + month);
    System.out.println("Using as current day of month: " + dayOfMonth);
    System.out.println("Using as current day of week: " + dayOfWeek);
    System.out.println();

    System.out.println("Second Tuesday of next month is " +
                       MONTHABBR[(month+1)%12] + " " +
                       nextServiceDate(year,month,dayOfMonth,dayOfWeek,
                                       3,2)); // 2nd Tuesday(=3) of next month
  }

}

使用问题评论中提供的参考中描述的语言特征,看起来计算并不难。要查找下个月第一天的日期值,请使用

DATE(YEAR(TODAY()), MONTH(TODAY()) + 1, 1)

要查找下个月第一天的星期几,请使用

MOD( DATE(YEAR(TODAY()), MONTH(TODAY()) + 1, 1) - DATE(1900,1,7), 7)

其中 Sun = 0, ... Sat = 6。要查找下个月第一个 day_of_week 的日期,请使用

DATE(YEAR(TODAY()), MONTH(TODAY())+1, 1)
+ day_of_week 
- MOD( DATE(YEAR(TODAY()), MONTH(TODAY())+1, 1) - DATE( 1900, 1, 7 ), 7 )
+
IF( 
  MOD(DATE(YEAR(TODAY()),MONTH(TODAY())+1,1)-DATE(1900,1,7),7) >= day_of_week,
  7,
  0
)

最后,要移动到所需 day_of_week 的第 2 次或第 3 次出现,请使用

DATE(YEAR(TODAY()), MONTH(TODAY())+1, 1)
+ day_of_week 
- MOD( DATE(YEAR(TODAY()), MONTH(TODAY())+1, 1) - DATE( 1900, 1, 7 ), 7 )
+
IF( 
  MOD(DATE(YEAR(TODAY()),MONTH(TODAY())+1,1)-DATE(1900,1,7),7) >= day_of_week,
  7,
  0
)
+ (occurrence - 1) * 7

这比我的纯数学答案更容易,因为该语言有一种增加月份的内置方法 (MONTH(TODAY())+1)。