Java:如何获得一个月中x天的日期(例如2012年2月的第三个星期一)

我有点挣扎于此。

我想设置我的日历让我们说: 2012年2月的第三个星期一。我没有找到任何使用Java的方法。

例如,如果我想设置2011年圣诞节的日历,我可以通过这种方式轻松完成:

Calendar when = Calendar.getInstance(); when.set (Calendar.MONTH, Calendar.DECEMBER); when.set (Calendar.DAY_OF_MONTH, 25) when.set (Calendar.YEAR, 2011); 

但我很遗憾如何设置它让我们说2012年阵亡将士纪念日,即5月的最后一个星期一。 这是我的代码,但这显然是错误的,因为我根本无法假设5月的最后一个星期一将在那年5月的第4周:

 Calendar when = Calendar.getInstance (); when.set (Calendar.DAY_OF_WEEK,Calendar.MONDAY); when.set (Calendar.MONTH, Calendar.MAY); when.set (Calendar.WEEK_OF_MONTH, 4) when.set (Calendar.YEAR, 2012); 

关于我如何以编程方式找出的任何建议,在2012年5月的哪个星期(例如上面的例子)是最后一个星期一? 假设我可以获得该信息,我应该能够使我的代码在上面工作。

我需要一些基本上适用于任何其他例子的东西。 可以为相同的场景提供确切日期的东西。 例子:

哪个日期是:

  • 2015年5月的第3个星期四
  • 2050年6月1日星期一
  • 2012年12月4日星期二
  • 2000年7月2日星期三

我真的需要这个用于我的项目,我确信它很简单,但是我在这个没有任何实际结果的情况下突破了我的头脑:)并且在网上也找不到任何东西。


添加:

好的,这是我一个月的最后一个星期一的地方:

 when.set (GregorianCalendar.MONTH, GregorianCalendar.MAY); when.set (GregorianCalendar.DAY_OF_WEEK, Calendar.MONDAY); when.set (GregorianCalendar.DAY_OF_WEEK_IN_MONTH, -1); when.set (Calendar.YEAR, 2012); 

但我不知道我会在同一个月的星期一如何做这样的事情,像这样?

 when.set (GregorianCalendar.DAY_OF_WEEK_IN_MONTH, 2); 

有什么建议么?

在Java中进行日期算术(通常,除了最琐碎的事情之外,用日期时间做任何事情) Joda-Time就是答案:

 public static LocalDate getNDayOfMonth(int dayweek,int nthweek,int month,int year) { LocalDate d = new LocalDate(year, month, 1).withDayOfWeek(dayweek); if(d.getMonthOfYear() != month) d = d.plusWeeks(1); return d.plusWeeks(nthweek-1); } public static LocalDate getLastWeekdayOfMonth(int dayweek,int month,int year) { LocalDate d = new LocalDate(year, month, 1).plusMonths(1).withDayOfWeek(dayweek); if(d.getMonthOfYear() != month) d = d.minusWeeks(1); return d; } public static void main(String[] args) { // second wednesday of oct-2011 LocalDate d = getNDayOfMonth( DateTimeConstants.WEDNESDAY, 2, 10, 2011); System.out.println(d); // last wednesday of oct-2011 LocalDate dlast = getLastWeekdayOfMonth( DateTimeConstants.WEDNESDAY, 10, 2011); System.out.println(dlast); } 

编辑 :自Java 8(2014)以来, 应该首选新的Date API(包java.time ),它的灵感来自/类似于Jodatime。

以下代码已成功测试2013年和2014年的所有假期。我意识到这并没有真正回答原始问题,但我认为这对于遇到这篇文章的人来说可能是有用的,希望能找到如何使用假期使用日历。

 public static boolean isMajorHoliday(java.util.Date date) { Calendar cal = Calendar.getInstance(); cal.setTime(date); // check if New Year's Day if (cal.get(Calendar.MONTH) == Calendar.JANUARY && cal.get(Calendar.DAY_OF_MONTH) == 1) { return true; } // check if Christmas if (cal.get(Calendar.MONTH) == Calendar.DECEMBER && cal.get(Calendar.DAY_OF_MONTH) == 25) { return true; } // check if 4th of July if (cal.get(Calendar.MONTH) == Calendar.JULY && cal.get(Calendar.DAY_OF_MONTH) == 4) { return true; } // check Thanksgiving (4th Thursday of November) if (cal.get(Calendar.MONTH) == Calendar.NOVEMBER && cal.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 4 && cal.get(Calendar.DAY_OF_WEEK) == Calendar.THURSDAY) { return true; } // check Memorial Day (last Monday of May) if (cal.get(Calendar.MONTH) == Calendar.MAY && cal.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY && cal.get(Calendar.DAY_OF_MONTH) > (31 - 7) ) { return true; } // check Labor Day (1st Monday of September) if (cal.get(Calendar.MONTH) == Calendar.SEPTEMBER && cal.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 1 && cal.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) { return true; } // check President's Day (3rd Monday of February) if (cal.get(Calendar.MONTH) == Calendar.FEBRUARY && cal.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 3 && cal.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) { return true; } // check Veterans Day (November 11) if (cal.get(Calendar.MONTH) == Calendar.NOVEMBER && cal.get(Calendar.DAY_OF_MONTH) == 11) { return true; } // check MLK Day (3rd Monday of January) if (cal.get(Calendar.MONTH) == Calendar.JANUARY && cal.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 3 && cal.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) { return true; } return false; } 

我不知道“简单”的方式,但我可以建议你以下。

  1. 将日历设置为该月的第一天。
  2. 检索它的星期几
  3. 计算该月第一个星期一的日期。
  4. 使用calendar.add()方法添加14天。 你将获得第三个星期一。

你需要的只是一个循环:

 public class CalculateDate { public static void main( String ... args ) { Calendar c = Calendar.getInstance(); c.set( Calendar.YEAR, 2012 ); c.set( Calendar.MONTH , Calendar.MAY); c.set( Calendar.DAY_OF_MONTH, 0 ); c.add( Calendar.DAY_OF_MONTH, -1 ); System.out.println( c.getTime() ); int mondaysCount = 0; while ( mondaysCount != 4 ) { c.add( Calendar.DAY_OF_MONTH, 1 ); if ( c.get( Calendar.DAY_OF_WEEK ) == Calendar.MONDAY ) { mondaysCount++; } } System.out.printf( "The fourth monday of may is %s", c.getTime() ); } } 

TL;博士

 LocalDate thirdMondayInFebruary2012 = YearMonth.of( 2012 , Month.FEBRUARY ) .atDay( 1 ) .with( TemporalAdjusters.dayOfWeekInMonth( 3 , DayOfWeek.MONDAY ) ); 

…和…

 LocalDate lastMondayInMay2012 = YearMonth.of( 2012 , Month.MAY ) .atDay( 1 ); .with( TemporalAdjusters.lastInMonth( DayOfWeek.MONDAY ) ); 

java.time

使用java.time类很容易做到,这些类现在取代了Joda-Time和与最早版本的Java捆绑在一起的麻烦的旧日期时间类。

首先,我们需要指定所需的月份。 YearMonth类可以做到这一点。 从那里我们得到一个LocalDate ,一个没有时间和没有时区的日期。

 YearMonth ym = YearMonth.of( 2012 , Month.FEBRUARY ); // Or pass '2' for 'February'. LocalDate ld = ym.atDay( 1 ); 

TemporalAdjuster接口用于将日期时间值调整为另一个日期时间值。 TemporalAdjusters类提供的实现(注意复数的’s’)。 使用方便的DayOfWeek枚举指定星期几。

 int ordinal = 3 ; // Use '3' for 'third occurrence in month' such as '3rd Monday'. LocalDate thirdMondayInFebruary2012 = ld.with( TemporalAdjusters.dayOfWeekInMonth( ordinal , DayOfWeek.MONDAY ) ); 

提示:不是在代码库中传递年份和月份的整数,而是传递YearMonthYearMonthDayOfWeek 。 这样做可以消除歧义,使您的代码更加自我记录,提供类型安全性并确保有效值。


关于java.time

java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧遗留日期时间类,如java.util.DateCalendarSimpleDateFormat

现在处于维护模式的Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参阅Oracle教程 。 并搜索Stack Overflow以获取许多示例和解释。 规范是JSR 310 。

您可以直接与数据库交换java.time对象。 使用符合JDBC 4.2或更高版本的JDBC驱动程序 。 不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

  • Java SE 8Java SE 9及更高版本
    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小function和修复。
  • Java SE 6Java SE 7
    • 许多java.timefunction都被反向移植到ThreeTen-Backport中的 Java 6和7。
  • Android的
    • 更高版本的Android捆绑java.time类的实现。
    • 对于早期的Android(<26), ThreeTenABP项目采用ThreeTen-Backport (如上所述)。 请参见如何使用ThreeTenABP ….

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目是未来可能添加到java.time的试验场。 您可以在这里找到一些有用的课程,如IntervalYearWeekYearQuarter等。

您是否有机会使用Quartz调度程序 ?

 public Date date(String cronExpression) { return new org.quartz.CronExpression(cronExpression). getNextValidTimeAfter(new Date(0)); } System.out.println(date("0 0 0 ? May Thu#3 2015")); System.out.println(date("0 0 0 ? Jun Mon#1 2050")); System.out.println(date("0 0 0 ? Dec Tue#4 2012")); System.out.println(date("0 0 0 ? Jul Wed#2 2000")); 

这个简单的代码打印正确的(?)结果:

 Thu May 21 00:00:00 CEST 2015 Mon Jun 06 00:00:00 CEST 2050 Tue Dec 25 00:00:00 CET 2012 Wed Jul 12 00:00:00 CEST 2000 

所需的CronExpression与Quartz的其余部分没有任何依赖关系,因此您可以考虑将其复制到项目中(注意许可证!)

附注: getNextValidTimeAfter()的内部实现是400行代码…

 public String nDow(int year, int month, int nweek, int nday) { Calendar cdt = Calendar.getInstance(); cdt.set(year, month -1, 1); return year + "-" + month + "-" + (getPosOfWeekday(cdt.get(Calendar.DAY_OF_WEEK), nday) + ((nweek - 1) *7)); } private int getPosOfWeekday(int startday, int nday) { nday = weekDayValue(nday); return constructCircularArray(startday).indexOf(nday) + 1; } private ArrayList constructCircularArray(int weekday) { ArrayList circularArray = new ArrayList(); for(int i = 0; i < 7; i++) { circularArray.add(i, weekDayValue(weekday++)); } return circularArray; } private int weekDayValue(int x) { return ((x-1) % 7) + 1; } 

这是另一种选择。 它的作用是:获得你想要的那一周(n)和其他参数,并返回那一周的那一天的日期。 由于日历给出了上个月的日期(例如2月29日而不是3月7日,因为3月的第1周与2月的最后一周相撞),如果日期超过7或倍数,则函数计算第2周它每周都有。 希望有所帮助。

 public static int getNthWeekDay (int n, int day, int month, int year) { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.DAY_OF_WEEK, day); calendar.set(Calendar.MONTH, month); calendar.set(Calendar.WEEK_OF_MONTH,n); calendar.set(Calendar.YEAR, year); if (calendar.get(Calendar.DATE) > n * 7) { calendar.set(Calendar.DAY_OF_WEEK,day); calendar.set(Calendar.MONTH, month); calendar.set(Calendar.WEEK_OF_MONTH,day+1); } return calendar.get(Calendar.DATE); }