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



我想根据 LocalDate.now(( 获取一周第一天的日期。JodaTime 可以执行以下操作,但似乎已从 Java 8 中的新 Date API 中删除。

LocalDate now = LocalDate.now();
System.out.println(now.withDayOfWeek(DateTimeConstants.MONDAY));

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

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

请注意,表达式 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)忽略了此本地化问题。

尝试

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

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

LocalDate 实现 Temporal

with(TemporalField field, long newValue)

返回与此对象

类型相同的对象,但指定字段已更改。

所以我们必须告诉它我们要更改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==%sn",
            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 mondayDate = LocalDate.now().with(WeekFields.of(Locale.FRANCE).getFirstDayOfWeek());

感谢来自这个问题。

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());

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

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

这将返回一周第一天的值,从 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 幕后的代码实际上无论如何都使用了日历类......

如果您讨厌使用用于 LocalDate 的 dayOfWeek(就像我一样(,并且您更喜欢与日历一起使用的那个,您可以使用这些简单的转换器:

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
    }
}

正如 Ray 的正确答案所说,您可以调用 with 并通过DayOfWeek枚举。

时区

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

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

如果未指定时区,那么将以静默方式应用 JVM 的当前缺省时区。注意:该默认值可以在运行时随时更改!最好指定您想要/预期的时区。

ZonedDateTime

您可以将时区(ZoneId(应用于LocalDate,以获得代表一周中第一个时刻的ZonedDateTime

ZonedDateTime thisWeekStart = firstDayOfThisWeek.atStartOfDay ( zoneId );
LocalDate.now( ZoneId.systemDefault()).with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) );

这返回本周的第一个星期一(考虑到今天是否是星期一(

有了乔达时间,你怎么看

now.toString("EEEE", locale)
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());
}

最新更新