使用 java.time 包从给定的星期和年份获取星期一日期
getting Monday date from given week and year using java.time package
我想使用 Java 8 包 java.time 从给定的周和年中获取星期一日期。
但在某些时候我遇到了问题,因为它没有返回正确的日期。
private LocalDate getDateFromWeekAndYear(final String week,final String year){
LocalDate date = LocalDate.now();
date = date.with(WeekFields.ISO.dayOfWeek(), 1);
date = date.with(WeekFields.ISO.weekOfWeekBasedYear(), Long.parseLong(week));
date = date.with(WeekFields.ISO.weekBasedYear(), Long.parseLong(year));
return date;
}
例如:
- 如果我通过 week=1 和 year=2013 那么日期是:2012-12-31。
- 但是如果我通过 week=53 和 year=2015 那么日期是:2014-12-29。我预计 2014-12-28.
我是否犯了任何逻辑错误或其他问题?
首先,您需要计算一年中第一周的第一个星期一,
然后简单地加上 7 的倍数到日期。
public static LocalDate firstMonday(int week, int year) {
LocalDate firstMonOfFirstWeek = LocalDate.now()
.with(IsoFields.WEEK_BASED_YEAR, year) // year
.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 1) // First week of the year
.with(ChronoField.DAY_OF_WEEK, 1); // Monday
// Plus multi of 7
return firstMonOfFirstWeek.plusDays( (week - 1) * 7);
}
public static void main(String[] args) {
System.out.println(firstMonday(1, 2013)); // 2012-12-31
System.out.println(firstMonday(53 ,2015 )); // 2015-12-28
}
tl;博士
YearWeek.of ( 2013 , 1 ).atDay ( DayOfWeek.MONDAY )
不正确的期望
您的期望不正确。 2015-W53
的星期一是 2015-12-28
,而不是 2014-12-28
,是 2015 年而不是 2014 年。没有理由期待 2014 年。如果您需要更多解释,请编辑您的问题以解释您的想法.
您可能会对 calendar-year 年与 week-based 年感到困惑。在 ISO 8601 definition of a week based year 中,第一个 周包含 calendar-year 的第一个星期四。这意味着我们在这些年之间有一些重叠。 calendar-year 的最后几天可能在接下来的 week-based 年。而 vice-versa,calendar-year 的前几天可能在前 week-based 年。
例如,您可以在下面的屏幕截图中看到,2012 年日历的最后一天(12 月 31 日)恰好在以下 week-based-2013 年第 1 周的第一周。并且在另一个屏幕截图,我们有相反的情况,2016 年日历的前三天(1 月 1 日、2 日和 3 日)落在 2015 年 week-based 年的第 53 周。
2013-W01
的星期一是 2012-12-31
。
2015-W53
的星期一是 2015-12-28
。
YearWeek
我建议添加 ThreeTen-Extra library to your project to make use of of the YearWeek
class。与其传递年和周的纯整数,不如传递这个 class 的对象。这样做会使您的代码更 self-documenting,提供 type-safety,并确保有效值。
// Pass ( week-based-year-number, week-number ). *Not* calendar year! See the ISO 8601 standard.
YearWeek yw = YearWeek.of( 2013 , 1 );
您可以从该周的任何一天提取。
LocalDate ld = yw.atDay( DayOfWeek.MONDAY );
让我们试试这种代码。
YearWeek yw1 = YearWeek.of ( 2013 , 1 );
LocalDate ld1 = yw1.atDay ( DayOfWeek.MONDAY );
YearWeek yw2 = YearWeek.of ( 2015 , 53 );
LocalDate ld2 = yw2.atDay ( DayOfWeek.MONDAY );
System.out.println ( "yw1: " + yw1 + " Monday: " + ld1 );
System.out.println ( "yw2: " + yw2 + " Monday: " + ld2 );
yw1: 2013-W01 Monday: 2012-12-31
yw2: 2015-W53 Monday: 2015-12-28
提示:要在 Calendar.app 中查看 Mac 上的那些 ISO 8601 standard week 数字,请设置 System Preferences
> Language & Region
> Calendar
> ISO 8601
。然后在Calendar.app中设置Preferences
> Advanced
> Show week numbers
。
这比 OP 和大多数答案显示的部分无效期望要困难得多。
先说:定义week-based-manipulations的正确顺序很重要。OP先申请了day-manipulation,然后[=47] =] 操纵。正确的做法是反过来!我将展示正确的辅助方法实现:
public static void main(String... args) {
System.out.println(
getDateFromWeekAndYear("53", "2015")); // 2015-12-28, NOT 2014-12-28
System.out.println(
getDateFromWeekAndYear("53", "2015").get(WeekFields.ISO.weekOfWeekBasedYear())); // 53
System.out.println(
getDateFromWeekAndYear("53", "2014")); // 2014-12-29
System.out.println(
getDateFromWeekAndYear("53", "2014").get(WeekFields.ISO.weekOfWeekBasedYear())); // 1
}
private static LocalDate getDateFromWeekAndYear(final String week,final String year) {
int y = Integer.parseInt(year);
LocalDate date = LocalDate.of(y, 7, 1); // safer than choosing current date
// date = date.with(WeekFields.ISO.weekBasedYear(), y); // no longer necessary
date = date.with(WeekFields.ISO.weekOfWeekBasedYear(), Long.parseLong(week));
date = date.with(WeekFields.ISO.dayOfWeek(), 1);
return date;
}
如果您不遵守此特定顺序,那么您有时确实会得到输入 2015-W53 的 2014 年日期(取决于当前日期)。
第二个问题:我也避免从当前日期开始,以免接近日历年的开始或结束(日历年!= week-based-year)而是选择年中作为起点。
第三个问题是2014(week-based)年第53周的宽处理。不存在因为2014只有52周!!!严格的算法应该识别并拒绝这样的输入。因此,我建议不要在 2015 年的第一周使用 YearWeek.of(2014, 53)
(在外部库 Threeten-Extra 中),另请参阅其 javadoc。比这样宽松的处理更好
YearWeek yw = YearWeek.of(2014, 52);
if (yw.is53WeekYear()) {
yw = YearWeek.of(2014, 53);
}
或使用我自己的时间库中的代码 Time4J (whose class CalendarWeek 与 YearWeek
相比,具有额外的国际化功能和额外的周算术):
CalendarWeek.of(2014, 53); // throws an exception
System.out.println(CalendarWeek.of(2014, 1).withLastWeekOfYear()); // 2014-W52
仅使用 java.time
-package:
使用这样的外部库至少可以帮助以透明的方式解决第一个问题。如果您不愿意添加额外的依赖项,那么如果无效,您可以这样做来处理第 53 周:
如果应用于辅助方法结果的表达式 WeekFields.ISO.weekOfWeekBasedYear()
产生值 1,则您知道第 53 周无效。然后您可以决定是接受宽松处理还是抛出异常。但是静默调整这样一个无效的输入是恕我直言的糟糕设计。
我想要大约 6 个月前的一周的星期一 - 具体来说是 26 周前.. 下面的代码给了我所需的日期:
LocalDate.now().minusWeeks(26).with(WeekFields.ISO.dayOfWeek(), DayOfWeek.MONDAY.getValue())
我想使用 Java 8 包 java.time 从给定的周和年中获取星期一日期。 但在某些时候我遇到了问题,因为它没有返回正确的日期。
private LocalDate getDateFromWeekAndYear(final String week,final String year){
LocalDate date = LocalDate.now();
date = date.with(WeekFields.ISO.dayOfWeek(), 1);
date = date.with(WeekFields.ISO.weekOfWeekBasedYear(), Long.parseLong(week));
date = date.with(WeekFields.ISO.weekBasedYear(), Long.parseLong(year));
return date;
}
例如:
- 如果我通过 week=1 和 year=2013 那么日期是:2012-12-31。
- 但是如果我通过 week=53 和 year=2015 那么日期是:2014-12-29。我预计 2014-12-28.
我是否犯了任何逻辑错误或其他问题?
首先,您需要计算一年中第一周的第一个星期一, 然后简单地加上 7 的倍数到日期。
public static LocalDate firstMonday(int week, int year) {
LocalDate firstMonOfFirstWeek = LocalDate.now()
.with(IsoFields.WEEK_BASED_YEAR, year) // year
.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 1) // First week of the year
.with(ChronoField.DAY_OF_WEEK, 1); // Monday
// Plus multi of 7
return firstMonOfFirstWeek.plusDays( (week - 1) * 7);
}
public static void main(String[] args) {
System.out.println(firstMonday(1, 2013)); // 2012-12-31
System.out.println(firstMonday(53 ,2015 )); // 2015-12-28
}
tl;博士
YearWeek.of ( 2013 , 1 ).atDay ( DayOfWeek.MONDAY )
不正确的期望
您的期望不正确。 2015-W53
的星期一是 2015-12-28
,而不是 2014-12-28
,是 2015 年而不是 2014 年。没有理由期待 2014 年。如果您需要更多解释,请编辑您的问题以解释您的想法.
您可能会对 calendar-year 年与 week-based 年感到困惑。在 ISO 8601 definition of a week based year 中,第一个 周包含 calendar-year 的第一个星期四。这意味着我们在这些年之间有一些重叠。 calendar-year 的最后几天可能在接下来的 week-based 年。而 vice-versa,calendar-year 的前几天可能在前 week-based 年。
例如,您可以在下面的屏幕截图中看到,2012 年日历的最后一天(12 月 31 日)恰好在以下 week-based-2013 年第 1 周的第一周。并且在另一个屏幕截图,我们有相反的情况,2016 年日历的前三天(1 月 1 日、2 日和 3 日)落在 2015 年 week-based 年的第 53 周。
2013-W01
的星期一是2012-12-31
。2015-W53
的星期一是2015-12-28
。
YearWeek
我建议添加 ThreeTen-Extra library to your project to make use of of the YearWeek
class。与其传递年和周的纯整数,不如传递这个 class 的对象。这样做会使您的代码更 self-documenting,提供 type-safety,并确保有效值。
// Pass ( week-based-year-number, week-number ). *Not* calendar year! See the ISO 8601 standard.
YearWeek yw = YearWeek.of( 2013 , 1 );
您可以从该周的任何一天提取。
LocalDate ld = yw.atDay( DayOfWeek.MONDAY );
让我们试试这种代码。
YearWeek yw1 = YearWeek.of ( 2013 , 1 );
LocalDate ld1 = yw1.atDay ( DayOfWeek.MONDAY );
YearWeek yw2 = YearWeek.of ( 2015 , 53 );
LocalDate ld2 = yw2.atDay ( DayOfWeek.MONDAY );
System.out.println ( "yw1: " + yw1 + " Monday: " + ld1 );
System.out.println ( "yw2: " + yw2 + " Monday: " + ld2 );
yw1: 2013-W01 Monday: 2012-12-31
yw2: 2015-W53 Monday: 2015-12-28
提示:要在 Calendar.app 中查看 Mac 上的那些 ISO 8601 standard week 数字,请设置 System Preferences
> Language & Region
> Calendar
> ISO 8601
。然后在Calendar.app中设置Preferences
> Advanced
> Show week numbers
。
这比 OP 和大多数答案显示的部分无效期望要困难得多。
先说:定义week-based-manipulations的正确顺序很重要。OP先申请了day-manipulation,然后[=47] =] 操纵。正确的做法是反过来!我将展示正确的辅助方法实现:
public static void main(String... args) {
System.out.println(
getDateFromWeekAndYear("53", "2015")); // 2015-12-28, NOT 2014-12-28
System.out.println(
getDateFromWeekAndYear("53", "2015").get(WeekFields.ISO.weekOfWeekBasedYear())); // 53
System.out.println(
getDateFromWeekAndYear("53", "2014")); // 2014-12-29
System.out.println(
getDateFromWeekAndYear("53", "2014").get(WeekFields.ISO.weekOfWeekBasedYear())); // 1
}
private static LocalDate getDateFromWeekAndYear(final String week,final String year) {
int y = Integer.parseInt(year);
LocalDate date = LocalDate.of(y, 7, 1); // safer than choosing current date
// date = date.with(WeekFields.ISO.weekBasedYear(), y); // no longer necessary
date = date.with(WeekFields.ISO.weekOfWeekBasedYear(), Long.parseLong(week));
date = date.with(WeekFields.ISO.dayOfWeek(), 1);
return date;
}
如果您不遵守此特定顺序,那么您有时确实会得到输入 2015-W53 的 2014 年日期(取决于当前日期)。
第二个问题:我也避免从当前日期开始,以免接近日历年的开始或结束(日历年!= week-based-year)而是选择年中作为起点。
第三个问题是2014(week-based)年第53周的宽处理。不存在因为2014只有52周!!!严格的算法应该识别并拒绝这样的输入。因此,我建议不要在 2015 年的第一周使用 YearWeek.of(2014, 53)
(在外部库 Threeten-Extra 中),另请参阅其 javadoc。比这样宽松的处理更好
YearWeek yw = YearWeek.of(2014, 52);
if (yw.is53WeekYear()) {
yw = YearWeek.of(2014, 53);
}
或使用我自己的时间库中的代码 Time4J (whose class CalendarWeek 与 YearWeek
相比,具有额外的国际化功能和额外的周算术):
CalendarWeek.of(2014, 53); // throws an exception
System.out.println(CalendarWeek.of(2014, 1).withLastWeekOfYear()); // 2014-W52
仅使用 java.time
-package:
使用这样的外部库至少可以帮助以透明的方式解决第一个问题。如果您不愿意添加额外的依赖项,那么如果无效,您可以这样做来处理第 53 周:
如果应用于辅助方法结果的表达式 WeekFields.ISO.weekOfWeekBasedYear()
产生值 1,则您知道第 53 周无效。然后您可以决定是接受宽松处理还是抛出异常。但是静默调整这样一个无效的输入是恕我直言的糟糕设计。
我想要大约 6 个月前的一周的星期一 - 具体来说是 26 周前.. 下面的代码给了我所需的日期:
LocalDate.now().minusWeeks(26).with(WeekFields.ISO.dayOfWeek(), DayOfWeek.MONDAY.getValue())