使用java.time包获取给定周和年的星期一日期

时间:2017-02-02 11:48:31

标签: java date java-8 java-time

我希望使用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;
}

例如:

  • 如果我通过周= 1和年= 2013,则日期为:2012-12-31。
  • 但如果我通过周= 53和年= 2015,那么日期是:2014-12-29。我期待2014-12-28。

我是否存在任何逻辑错误或其他问题?

3 个答案:

答案 0 :(得分:3)

首先,您需要计算一年中第一周的第一个星期一, 然后简单地加上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
}

答案 1 :(得分:3)

这比OP的部分无效期望更难以解决,大多数答案都显示出来。

首先要说的是:定义基于周的操作的正确顺序非常重要。 OP首先应用日操纵,然后是基于年的操纵。正确的方法是相反的!我将展示正确的帮助方法实现:

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年日期(取决于当前日期)。

第二个问题:我也避免从当前日期开始,以便不接近日历年的开始或结束(日历年!=基于周的年份),而是选择midth一年作为起点。

第三个问题是在2014年(基于工作日)的第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中的代码(其类CalendarWeek具有额外的i18n功能和额外周算术与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周无效。然后您可以决定是否要接受宽松处理或抛出异常。但默默调整这样一个无效的输入是恕我直言的糟糕设计。

答案 2 :(得分:1)

TL;博士

YearWeek.of ( 2013 , 1 ).atDay ( DayOfWeek.MONDAY )

预期不正确

您的期望不正确。 2015-W53的星期一是2015-12-28,而不是2014-12-28,2015年而不是2014年。没有理由期待2014年。如果您需要更多解释,请编辑您的问题以解释您的想法

您可能会对日历年与基于周的年份感到困惑。在ISO 8601 definition of a week based year周第一个包含日历年的第一个星期四。这意味着我们在这些年间有一些重叠。日历年的最后几天可能存在于下一周的年份中。反之亦然,日历年的前几天可能存在于上一周的基础上。

例如,您可以在下面的屏幕截图中看到,2012年日历(12月31日)的最后一天属于2013年第1周的第一周的第一周。在另一个屏幕截图中,我们有相反的情况,即2016年(1月1日,2日和3日)的前三天在2015年的第59周登陆。

  • 2013-W01的星期一是2012-12-31
  • 2015-W53的星期一是2015-12-28

2013-W01 calendar screen shot

2015-W53 calendar screen shot

YearWeek

我建议将ThreeTen-Extra库添加到您的项目中以使用YearWeek类。而不是仅传递年和周的整数,而是传递此类的对象。这样做可以使您的代码更加自我记录,提供类型安全性并确保有效的值。

// 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星期一:2012-12-31

     

yw2:2015-W53星期一:2015-12-28

提示:要在Calendar.app中查看Mac上的ISO 8601 standard week个号码,请设置System Preferences> Language & Region> Calendar> ISO 8601。然后在Calendar.app中设置Preferences> Advanced> Show week numbers