Получить дату первого дня недели на основе LocalDate.now() в Java 8
Я хотел бы получить дату первого дня недели на основе LocalDate.now(). Следующее было возможно с JodaTime, но, похоже, удалено из нового API даты в Java 8.
LocalDate now = LocalDate.now();
System.out.println(now.withDayOfWeek(DateTimeConstants.MONDAY));
Я не могу вызвать 'withDayOfWeek()', потому что он не существует.
Итак, мой вопрос: как получить дату первого дня недели на основе некоторой LocalDate?
10 ответов
Обратите внимание, что выражение 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)
игнорирует эту проблему локализации.
Несмотря на все предыдущие ответы, мне все еще приходилось копаться, чтобы понять, что делает 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==%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 mondayDate = LocalDate.now().with(WeekFields.of(Locale.FRANCE).getFirstDayOfWeek());
Спасибо, что COME FROM в этом вопросе.
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 (который от API Java-8) имеет ( здесь). Так что вы можете сделать это:
WeekFields.of(Locale.getDefault()).firstDayOfWeek.value
Возвращает значение первого дня недели, начиная с 1 в понедельник и заканчивая 7 в воскресенье.
Пример использования (в Kotlin) для получения новой LocalDate, для которой задан dayOfWeek, с обратным ходом по времени, а не вперед:
/**@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 в любом случае...
Если вы ненавидите использовать dayOfWeek, который используется для LocalDate (как я), и вы предпочитаете тот, который используется с 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.now( ZoneId.systemDefault()).with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) );
Это возвращает первый понедельник текущей недели (учитывая, что сегодня понедельник)
Как говорит правильный ответ Рэй, вы можете позвонить with
и передать DayOfWeek
ENUM.
Часовой пояс
Обратите внимание, что часовой пояс имеет решающее значение при определении даты "сегодня". В любой момент дата меняется в зависимости от того, где вы стоите на земном шаре.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
LocalDate firstDayOfThisWeek = LocalDate.now ( zoneId ).with ( DayOfWeek.MONDAY );
Если вы не укажете часовой пояс, текущий часовой пояс JVM по умолчанию будет применен автоматически. Осторожно: это значение по умолчанию может измениться в любой момент во время выполнения! Лучше указать желаемый / ожидаемый часовой пояс.
ZonedDateTime
Вы можете применить часовой пояс (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());
}