根据 Java 中的 LocalDate.now() 获取一周第一天的日期 8

Get date of first day of week based on LocalDate.now() in Java 8

我想根据 LocalDate.now() 获取一周中第一天的日期。 JodaTime 可以实现以下内容,但似乎已从 Java 8.

中的新日期 API 中删除
LocalDate now = LocalDate.now();
System.out.println(now.withDayOfWeek(DateTimeConstants.MONDAY));

我不能调用'withDayOfWeek()',因为它不存在。

所以我的问题是: 如何根据一些 LocalDate 获取一周的第一天的日期?

尝试

System.out.println(now.with(DayOfWeek.MONDAY));

感谢come from in this question

Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, 0); // ! clear would not reset the hour of day !
        cal.clear(Calendar.MINUTE);
        cal.clear(Calendar.SECOND);
        cal.clear(Calendar.MILLISECOND);

        // get start of this week in milliseconds
        cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
        System.out.println("Start of this week:       " + cal.getTime());
        System.out.println("... in milliseconds:      " + cal.getTimeInMillis());

请注意,表达式 System.out.println(now.with(DayOfWeek.MONDAY)) 与区域设置无关,因为它使用 ISO-8601,因此它总是向后跳到上周一(或者停留在周一,以防日期已经指向周一)。

因此在美国或其他一些国家/地区 - 一周从星期日开始 - 它可能不会像您预期的那样工作 - now.with(DayOfWeek.MONDAY) 不会向前跳 到星期一,以防日期指向星期日。

如果您需要解决这些问题,最好使用本地化字段 WeekFields.dayOfWeek():

LocalDate now = LocalDate.now();
TemporalField fieldISO = WeekFields.of(Locale.FRANCE).dayOfWeek();
System.out.println(now.with(fieldISO, 1)); // 2015-02-09 (Monday)

TemporalField fieldUS = WeekFields.of(Locale.US).dayOfWeek();
System.out.println(now.with(fieldUS, 1)); // 2015-02-08 (Sunday)

另一个例子来自下面的评论:

LocalDate ld = LocalDate.of(2017, 8, 18); // Friday as original date

System.out.println(
    ld.with(DayOfWeek.SUNDAY)); // 2017-08-20 (2 days later according to ISO)

// Now let's again set the date to Sunday, but this time in a localized way...
// the method dayOfWeek() uses localized numbering (Sunday = 1 in US and = 7 in France)

System.out.println(ld.with(WeekFields.of(Locale.US).dayOfWeek(), 1L)); // 2017-08-13
System.out.println(ld.with(WeekFields.of(Locale.FRANCE).dayOfWeek(), 7L)); // 2017-08-20

美国的例子非常清楚地表明,居住在美国的人希望去到最后一个而不是下个星期日,因为星期日在美国被认为是一周的第一天。基于 ISO 的简单表达式 with(DayOfWeek.SUNDAY) 忽略了这个本地化问题。

作为 by Ray says, you can call with and pass the DayOfWeek 枚举。

时区

请注意,时区对于确定 "today" 的日期至关重要。在任何时候,日期都会因您在地球上所处的位置而异。

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
LocalDate firstDayOfThisWeek = LocalDate.now ( zoneId ).with ( DayOfWeek.MONDAY );

如果您不指定时区,则会静默应用 JVM 当前的默认时区。当心:在运行时 期间,默认值可以随时更改! 最好指定 your desired/expected time zone.

ZonedDateTime

您可以应用时区(ZoneId) to your LocalDate to get a ZonedDateTime 代表一周的第一时刻。

ZonedDateTime thisWeekStart = firstDayOfThisWeek.atStartOfDay ( zoneId );

有了Joda Time,你觉得怎么样

now.toString("EEEE", locale)

尽管前面有所有答案,但我仍然不得不四处挖掘以弄清楚 Java8 在做什么,所以这是我发现最直观的方法:

LocalDate implements Temporal

with(TemporalField field, long newValue)

Returns an object of the same type as this object with the specified field altered.

所以我们必须告诉它我们要更改 LocalDate 的日期部分 (DAY_OF_WEEK) 并更改为什么值。

如果您对一周中的天数可能从 0 到 6 或从 1 到 7 有疑问:

    System.out.printf("first day of week (0 or 1) == %d \n",
            ChronoField.DAY_OF_WEEK.range().getMinimum());
first day of week (0 or 1) == 1 

我必须确定我的 JDK 提供的默认值 - YMMV:

    System.out.printf("default zone offset==[%s]\n",
            ZoneId.systemDefault());
    System.out.printf("1st day of week==%s\n",
            WeekFields.of(Locale.getDefault()).getFirstDayOfWeek());
default zone offset==[Europe/London]
1st day of week==MONDAY

因此,如果我根据这些默认值执行一些代码,如下所示:

    LocalDate localDate = LocalDate.now();
    System.out.printf("localDate == %s \n", localDate);
    System.out.printf("localdate first day of week == %s (%s) \n",
            localDate.with(ChronoField.DAY_OF_WEEK, 1),
            localDate.with(ChronoField.DAY_OF_WEEK, 1).getDayOfWeek());
localDate == 2017-10-24 
localdate first day of week == 2017-10-23 (MONDAY) 

然后 Java 与 ChronoField.DAY_OF_WEEK 一起使用,它不仅定义了我们要更改日期的哪一部分,还定义了如何更改它。

因此,如果我们希望我们的代码处理用户指定为一周的第一天的任何内容,我们可以使用 WeekFields.of() 创建自己的基于周的计算方式的定义工厂方法。

使用这个我们可以将我们自己的 dayOfWeek 参数传递给 with() 以我们想要的方式进行日期计算:

    TemporalField myWeek = WeekFields.of(DayOfWeek.SUNDAY, 1).dayOfWeek();
    System.out.printf("configured localdate first day of week == %s (%s) \n",
            localDate.with(myWeek, 1),
            localDate.with(myWeek, 1).getDayOfWeek());
configured localdate first day of week == 2017-10-22 (SUNDAY) 

如需更深入的了解,请查看 LocalDate.with() 中的代码,它非常有趣。

LocalDate 似乎没有这个,但是 WeekFields(来自 java-8 API)有 (here)。所以你可以这样做:

WeekFields.of(Locale.getDefault()).firstDayOfWeek.value

这个returns一周第一天的值,从1开始是星期一,到7结束是星期日。

使用示例(在 Kotlin 中),获取设置了 dayOfWeek 的新 LocalDate,及时向后而不是向前:

/**@param targetDayOfWeek day of week to go to, starting from 1 as Monday (and 7 is Sunday) */
fun LocalDate.minusDaysToDayOfWeek(targetDayOfWeek: Int = WeekFields.of(Locale.getDefault()).firstDayOfWeek.value): LocalDate {
    //conversion so that Sunday is 0, Monday is 1, etc:
    val diffDays = (dayOfWeek.value % 7) - (targetDayOfWeek % 7)
    val result = when {
        diffDays == 0 -> this
        diffDays < 0 -> minusDays((7 + diffDays).toLong())
        else -> minusDays(diffDays.toLong())
    }
    return result
}

示例输入输出:

2017-12-31 -> 2017-12-31
2018-01-01 -> 2017-12-31
2018-01-02 -> 2017-12-31
2018-01-03 -> 2017-12-31
2018-01-04 -> 2017-12-31
2018-01-05 -> 2017-12-31
2018-01-06 -> 2017-12-31
2018-01-07 -> 2018-01-07
2018-01-08 -> 2018-01-07
2018-01-09 -> 2018-01-07
2018-01-10 -> 2018-01-07
2018-01-11 -> 2018-01-07
2018-01-12 -> 2018-01-07
2018-01-13 -> 2018-01-07
2018-01-14 -> 2018-01-14
2018-01-15 -> 2018-01-14

下面是一个示例函数,可以及时转到目标星期几:

fun LocalDate.plusDaysToDayOfWeek(targetDayOfWeek: Int = getLastDayOfWeek()): LocalDate {
    val diffDays = (targetDayOfWeek % 7) - (dayOfWeek.value % 7)
    val result = when {
        diffDays == 0 -> this
        diffDays < 0 -> plusDays((7 + diffDays).toLong())
        else -> plusDays(diffDays.toLong())
    }
    return result
}

/**@return the  last day of week, when 1 is Monday ... 7 is Sunday) */
@JvmStatic
fun getLastDayOfWeek(firstDayOfWeek: DayOfWeek = WeekFields.of(Locale.getDefault()).firstDayOfWeek): Int {
    return when (firstDayOfWeek) {
        DayOfWeek.MONDAY -> DayOfWeek.SUNDAY.value
        else -> firstDayOfWeek.value - 1
    }
}

顺便说一句,奇怪的是我认为新 API 的幕后代码实际上使用日历 class 无论如何...

如果您不喜欢使用用于 LocalDate 的 dayOfWeek(就像我一样),而您更喜欢用于 Calendar 的那个,您可以使用这些简单的转换器:

fun DayOfWeek.toCalendarDayOfWeek(): Int {
    return when (this) {
        DayOfWeek.SATURDAY -> Calendar.SATURDAY
        else -> (this.value + 1) % 7
    }
}

@JvmStatic
fun convertLocalDateDayOfWeekToCalendarDayOfWeek(localDateDayOfWeek: Int): Int {
    return when (localDateDayOfWeek) {
        DayOfWeek.SATURDAY.value -> Calendar.SATURDAY
        else -> (localDateDayOfWeek + 1) % 7
    }
}

@JvmStatic
fun convertFromCalendarDayOfWeekToLocalDateDayOfWeek(calendarDayOfWeek: Int): Int {
    return when (calendarDayOfWeek) {
        Calendar.SUNDAY -> DayOfWeek.SUNDAY.value
        else -> calendarDayOfWeek - 1
    }
}

如果我想将 星期一 作为本周的第一天,它对我有用:

LocalDate mondayDate = LocalDate.now().with(WeekFields.of(Locale.FRANCE).getFirstDayOfWeek());
public void getWeekFromADateOfAMonth(){
    String date = "2019-01-02T18:25:43.511Z";
    TemporalField fieldISO = WeekFields.of(Locale.US).dayOfWeek();
    ZonedDateTime dateTime = ZonedDateTime.parse(date);

    int week = dateTime.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
    int weekYear = dateTime.get ( IsoFields.WEEK_BASED_YEAR );

    System.out.println ( "now: " + dateTime + " is week: " + week + " of weekYear: " + weekYear );

    int startDate = dateTime.with(fieldISO,1).getDayOfMonth();
    int endDate = dateTime.with(fieldISO,7).getDayOfMonth();

    String startMonth = String.valueOf(dateTime.with(fieldISO,1).getMonth());
    String endMonth = String.valueOf(dateTime.with(fieldISO,7).getMonth());
}
LocalDate.now( ZoneId.systemDefault()).with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) );

这return本周的第一个星期一(考虑今天是否是星期一)