我想根据LocalDate.now()获取一周中第一天的日期。 JodaTime可以进行以下操作,但似乎已从Java 8的新Date API中删除。
LocalDate now = LocalDate.now();
System.out.println(now.withDayOfWeek(DateTimeConstants.MONDAY));
我无法调用" withDayOfWeek()",因为它不存在。
所以我的问题是:如何基于某些LocalDate获取星期几的日期?
请注意,表达式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)忽略了此本地化问题。
仅供参考,WeekFields.of(Locale.getDefault()).getFirstDayOfWeek()返回当前语言环境的配置的一周的第一天(例如SUNDAY,MONDAY等)。不是日期。
@MenoHochschild为什么枚举DayOfWeek??在某些地区(例如美国)不起作用?有什么问题
@BasilBourque如果今天是星期日,并且我们在美国(星期天作为一周的第一天),则today.with(MONDAY)将转到第二天(+1),但是如果我们在欧洲,则需要返回6天(-6) 。表达式with(day-of-week)表示要在SAME日历周内进入星期几,这取决于星期几。
@BasilBourque为澄清起见,我之前的评论与WeekFields的使用有关(希望说with(day-of-week, 1))。如果您使用首选方法(基于enum-with(MONDAY)),则该操作仅基于ISO样式,假定始终将星期一作为一周的第一天。不幸的是,WeekFields不能直接用于枚举,而只能用于较长的数字(错误的设计),因此它比应使用的便捷性和秘密性更高(today.with(WeekFields.of(Locale.US).dayOfWeek(), MONDAY)无法编译)。
@MenoHochschild我对您的评论和声明now.with(DayOfWeek.MONDAY)在美国无法使用感到有些困惑。我有一个使用with(DayOfWeek.SUNDAY)的测试用例,并在今天(星期五)使用en_US区域设置和PST时区(-Duser.language=en -Duser.country=US -Duser.timezone=AmericaLos_Angeles)或欧洲区域设置和时区运行它,这给我带来了始终相同的结果-在两种情况下,我都越来越下一个星期日(8月20日),我想您要说的是美国时区(因为它是本周的一部分),所以我会在上个星期日得到。你能澄清一下吗?
@barthand首先,时区在这里根本不相关,仅与语言环境相关。但是,如果仅应用with(DayOfWeek.SUNDAY),那么语言环境也会被忽略,并且日期(如果不是星期日)将始终向前跳转,因为星期日是ISO-8601中的一周的最后一天。
@MenoHochschild感谢您的回答!因此,由于with(DayOfWeek)始终使用ISO-8601并忽略语言环境,因此这意味着with(DayOfWeek.MONDAY)始终会向后跳到上一个星期一(如果是星期一以外的任何一天),或者将保留在同一天(如果日期指向已经是星期一),对吗?
@barthand确实如此。
@MenoHochschild因此,也许有必要在您的答案中对其进行解释-请注意,表达式System.out.println(now.with(DayOfWeek.MONDAY));在美国或其他一些国家或地区将不起作用。有点误导,因为实际上它在美国和其他任何国家都可以以相同的方式(只是非本地化)工作。
@barthand感谢您的提示。我现在已经更新了第一句话/句子,希望现在可以更清楚。
您如何准确地获取当前语言环境的firstDayOfWeek而不是指定它?
@androiddeveloper参见javadoc
@MenoHochschild您的意思是:WeekFields.of(Locale.getDefault())。firstDayOfWeek吗?如果是这样,为什么不将其发布为真实答案?另外,我认为幕后的代码实际上仍然使用Calendar类,所以最好改用Calendar类...
@androiddeveloper是的,根据您的评论,我的意思是WeekFields.of(Locale.getDefault()).getFirstDayOfWeek(),但这不是OP的问题的答案,OP要求提供本周第一天的完整日历日期。
@MenoHochschild好了,所以我实际上在这里创建了一个答案:stackoverflow.com/a/48150270/878126
尝试
System.out.println(now.with(DayOfWeek.MONDAY));
我只是想补充一点,并不是地球上的所有地方都具有同一周的开始日期。如果需要,可以通过Calendar#getFirstDayOfWeek获得
我们能否以某种方式将星期更改为从星期日开始,并使用相同的命令获取星期的第一天?
无法正确处理区域设置,并非所有区域设置都以星期一作为一周的第一天开始。
尽管有所有先前的答案,我仍然必须深入研究Java8在做什么,所以这是我发现最直观的方法:
LocalDate implements Temporal
with(TemporalField field, long newValue)
Returns an object of the same type as this object with the specified field altered.
因此,我们必须告诉它要更改(DAY_OF_WEEK)的日期部分并更改为哪个值。
如果您怀疑一周中的天数可能是从0到6或从1到7:
System.out.printf("first day of week (0 or 1) == %d
",
ChronoField.DAY_OF_WEEK.range().getMinimum());
first day of week (0 or 1) == 1
我必须弄清楚我的JDK提供的默认值-YMMV:
System.out.printf("default zone offset==[%s]
",
ZoneId.systemDefault());
System.out.printf("1st day of week==%s
",
WeekFields.of(Locale.getDefault()).getFirstDayOfWeek());
default zone offset==[Europe/London]
1st day of week==MONDAY
因此,如果我根据这些默认值执行一些代码,如下所示:
LocalDate localDate = LocalDate.now();
System.out.printf("localDate == %s
", localDate);
System.out.printf("localdate first day of week == %s (%s)
",
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)
",
localDate.with(myWeek, 1),
localDate.with(myWeek, 1).getDayOfWeek());
configured localdate first day of week == 2017-10-22 (SUNDAY)
要了解更多信息,请查看LocalDate.with()中的代码,这很有趣。
感谢来自这个问题。
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的Java 8方法。
如果我想将星期一作为当前一周的第一天,它将对我有用:
LocalDate mondayDate = LocalDate.now().with(WeekFields.of(Locale.FRANCE).getFirstDayOfWeek());
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幕后的代码实际上仍然使用Calendar类。
如果您讨厌使用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
}
}
正如Ray的正确答案所说,您可以调用with并传递DayOfWeek枚举。
时区
请注意,时区对于确定"今天"的日期至关重要。在任何时候,日期都会因您在地球上的位置而异。
ZoneId zoneId = ZoneId.of ("America/Montreal" );
LocalDate firstDayOfThisWeek = LocalDate.now ( zoneId ).with ( DayOfWeek.MONDAY );
如果您未指定时区,则将静默应用JVM的当前默认时区。当心:该默认值可以在运行时随时更改!最好指定您想要的/预期的时区。
ZonedDateTime
您可以将时区(a ZoneId)应用于LocalDate,以获得表示一周中第一时刻的ZonedDateTime。
ZonedDateTime thisWeekStart = firstDayOfThisWeek.atStartOfDay ( zoneId );
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());
}
有了Joda Time,您如何看待
now.toString("EEEE", locale)
我在javadoc中没有看到这样的方法。 docs.oracle.com/javase/8/docs/api/java/time/
我不好的是,这是一个乔达时间方法,我应该精确