作为@Ole V.V. explained,您需要定义一周从哪一天开始,以及必须将其视为第一周的天数。
例如,ISO-8601 标准考虑:
- 星期一是一周中的第一天。
- 第一周最少天数:标准将第一周算作至少需要4天
每月分为多个时段,每个时段从定义的一周中的第一天开始。同一月中最早的时间段称为周0,如果它少于最小天数,则称为周1,如果它至少有最小天数。
根据您如何定义这些,您可能会得到不同的结果。考虑一下 2017 年 7 月的日历:
July 2017
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
如果我们考虑 ISO 的定义,我们有:
- 第零周 - 2017-07-01 到 2017-07-02
- 第 1 周:从 2017-07-03 到 2017-07-09
- 第 2 周:从 2017 年 7 月 10 日到 2017 年 7 月 16 日
- 第 3 周:从 2017 年 7 月 17 日到 2017 年 7 月 22 日
- 第 4 周:从 2017 年 7 月 23 日到 2017 年 7 月 30 日
- 第 5 周:2017-07-31
如果我们将一周的第一天视为星期日,将第一周的最少天数视为 1,我们有:
- 第 1 周:2017-07-01
- 第 2 周:从 2017-07-02 到 2017-07-08
- 第 3 周:从 2017-07-09 到 2017-07-15
- 第 4 周:从 2017 年 7 月 16 日到 2017 年 7 月 21 日
- 第 5 周:从 2017 年 7 月 22 日到 2017 年 7 月 29 日
- 第 6 周:从 2017 年 7 月 30 日到 2017 年 7 月 31 日
Calendar 的解决方案已经发布,我将使用新的 API 编写一个解决方案。如果您使用的是 Java 8,请考虑使用 new java.time API。更简单,less bugged and less error-prone than the old APIs。
如果您使用的是 Java ,则可以使用 ThreeTen Backport,它是 Java 8 新日期/时间类的出色反向移植。对于Android,还有ThreeTenABP(更多关于如何使用它here)。
下面的代码适用于两者。
唯一的区别是 包名(在 Java 8 中是 java.time,而在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp),但类和方法 names是一样的。
我使用的是DateTimeFormatter,因为它需要所有字段(月、年、星期几和星期几)并相应地解析日期,创建一个LocalDate(有日/月/年字段)。我也在使用WeekFields 类,它可以配置为使用不同的周定义(第一天和第一周的最少天数)
还有一点调整可以将星期日视为零:
public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
// you can customize your week definition (first day of week and mininum days in first week)
WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// week of month, using week definition from WeekFields
.appendValue(wf.weekOfMonth()).appendPattern(" ")
// day of week
.appendValue(ChronoField.DAY_OF_WEEK)
// month and year
.appendPattern(" M/yyyy")
// create formatter
.toFormatter();
return LocalDate.parse(weekOfMonth + " " +
// Sunday is 0, adjusting value
(dayOfWeek == 0 ? 7 : dayOfWeek) + " " + month + "/" + year, fmt);
}
使用此代码(星期从星期日开始,第一周需要 2 天 - 否则星期将为零,如上面的第一个示例):
LocalDate d = getDate(1, 6, 7, 2017);
d 将是 2017-07-08(2017 年 7 月第 1 周的星期六)。
如果您想使用 ISO 8601 定义,请使用常量 WeekFields.ISO 而不是使用 WeekFields.of() 方法。
作为@Ole V.V. suggested in the comments,也可以不创建格式化程序:获取当月的第一个dayOfWeek,调整为想要的weekOfMonth:
public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
// you can customize your week definition (first day of week and mininum days in first week)
WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);
// Sunday is 0, adjusting value
DayOfWeek dow = DayOfWeek.of(dayOfWeek == 0 ? 7 : dayOfWeek);
// get the first weekday of the month
LocalDate first = LocalDate.of(year, month, 1).with(TemporalAdjusters.nextOrSame(dow));
// check in which week this date is
int week = first.get(wf.weekOfMonth());
// adjust to weekOfMonth
return first.plusWeeks(weekOfMonth - week);
}
它的工作方式完全相同,但不需要格式化程序 - 我已经测试了从 1600 年到 2100 年的日期,它得到了相同的结果。
PS:Calendar 也有类似的配置,通过方法 setFirstDayOfWeek() 和 setMinimalDaysInFirstWeek()。您可以调用相应的get 方法检查默认配置。