Получить дату первого дня недели на основе LocalDate.now() в Java 8

Я хотел бы получить дату первого дня недели на основе LocalDate.now(). В JodaTime было возможно следующее, но, похоже, оно удалено из нового API дат в Java 8.

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

Я не могу назвать 'withDayOfWeek()', потому что его не существует.

Итак, мой вопрос: Как получить дату первого дня недели на основе некоторого LocalDate?

Ответ 1

Обратите внимание, что выражение 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 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) игнорирует эту проблему локализации.

Ответ 2

Try

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

Ответ 3

Несмотря на все предыдущие ответы, мне все еще приходилось копаться, чтобы понять, что делает 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(), он довольно интересный.

Ответ 4

Благодаря пришли из этого вопроса.

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

Ответ 5

Как говорит правильный ответ от Ray, вы можете позвонить with и передать DayOfWeek перечисление.

Часовой пояс

Обратите внимание, что часовой пояс имеет решающее значение для определения даты "сегодня". В любой момент дата зависит от того, где вы находитесь на земном шаре.

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

Если вы не укажете часовой пояс, то действующий часовой пояс JVMs используется по умолчанию. Остерегайтесь: это значение по умолчанию может измениться в любой момент во время выполнения! Лучше указать ваш желаемый/ожидаемый часовой пояс.

ZonedDateTime

Вы можете применить часовой пояс (ZoneId) к LocalDate, чтобы получить ZonedDateTime, представляющий первый момент недели.

ZonedDateTime thisWeekStart = firstDayOfThisWeek.atStartOfDay ( zoneId );

Ответ 6

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

Ответ 7

Это работает для меня, если я хочу, чтобы понедельник был первым днем текущей недели:

LocalDate mondayDate = LocalDate.now().with(WeekFields.of(Locale.FRANCE).getFirstDayOfWeek());

Ответ 8

С Joda Time, что вы думаете о

now.toString("EEEE", locale)

Ответ 9

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