Как определить дату прохождения номера недели и номера дня (недели) в java

Мне нужно получить дату, передав эти параметры

  1. год
  2. номер недели (в месяце) т.е. 1,2,3,4,5
  3. номер дня (в неделе) от 0 (воскресенье) до 6 (суббота)
  4. Месяц

Я искал конструктор в классе Calendar, но не содержит этих параметров.


person Newb    schedule 03.07.2017    source источник
comment
Вы не можете получить дату без месяца.   -  person shmosel    schedule 03.07.2017
comment
Я не мог бы более настоятельно рекомендовать не использовать класс java.util.Calendar, если вы можете избежать его. Вы должны посмотреть на пакет java.time и использовать класс, соответствующий вашему варианту использования.   -  person Joe C    schedule 04.07.2017
comment
Как считаются недели месяца? Новая неделя начинается в воскресенье, или 8-го числа месяца, или?   -  person Ole V.V.    schedule 04.07.2017
comment
Классы Calendar уже давно вытеснены классами java.time. Избегайте этих проблемных, плохо спроектированных и ошибочных устаревших классов даты и времени.   -  person Basil Bourque    schedule 04.07.2017


Ответы (3)


Чтобы создать дату из года, номера недели и дня недели, используйте экземпляр java.util.Calendar:

Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, 2017);
        cal.set(Calendar.WEEK_OF_YEAR, 26);
        cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);

Чтобы преобразовать из Calendar в java.util.Date :

Date date = cal.getTime();

Чтобы преобразовать Date в java.time.LocalDateTime, используйте:

LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
person Jay Smith    schedule 03.07.2017
comment
Я заменил НЕДЕЛЮ ГОДА на НЕДЕЛЮ МЕСЯЦА И МЕСЯЦА, и все было в порядке. Те же результаты. Спасибо! - person Newb; 04.07.2017
comment
При преобразовании в java.time используйте Instant и ZonedDateTime, а не LocalDateTime. Нет смысла намеренно отказываться от информации о часовом поясе/смещении. Кроме того, вам следует избегать проблемных старых устаревших классов даты и времени, поскольку java.time был добавлен, чтобы полностью их заменить. В этом 2017 году другие Ответы Оле В.В. или Гюго должен был быть принят, а не этот. - person Basil Bourque; 04.07.2017
comment
У меня была та же проблема, что и у @Newb, и этот пример не решает эту большую проблему! - person ; 27.12.2017

Несмотря на ваши теги, я согласен с комментарием Joe C, вам следует предпочесть современный API даты и времени Java (также известный как java.time или JSR-310) давно устаревшему Calendar и друзьям. Современные классы более удобны для программиста и с ними приятнее работать, они преподносят меньше сюрпризов, приводят к более ясному и естественному коду (по крайней мере, когда вы привыкнете к свободному стилю) и их легче отлаживать, если вы допустили ошибку.

Я не знаю, как вы считаете недели в месяце. Я предположил, что первая неделя начинается первого числа месяца и длится 7 дней (поэтому вторая неделя начинается 8-го, а 5-я неделя — 29-го числа месяца). Если вы считаете иначе, см. ответ Хьюго.

Я предлагаю этот метод:

public static LocalDate weekNoAndDayToDate(Year year, Month month, int weekOfMonth, int dayOfWeek) {
    // find day of week: convert from 0 (Sunday) to 6 (Saturday)
    // to DayOfWeek, from 1 (Monday) to 7 (Sunday)
    DayOfWeek dow;
    if (dayOfWeek == 0) { // Sunday
        dow = DayOfWeek.SUNDAY;
    } else {
        dow = DayOfWeek.of(dayOfWeek);
    }
    return year.atMonth(month)
            .atDay(1)
            .with(TemporalAdjusters.nextOrSame(dow))
            .with(ChronoField.ALIGNED_WEEK_OF_MONTH, weekOfMonth);
}

С этим мы можем сделать, например

    LocalDate today = weekNoAndDayToDate(Year.of(2017), Month.JULY, 1, 1);

Это дает

2017-07-03

Если вам нужен Date или GregorianCalendar, например, для старого API, который вы не можете изменить, выполните одно из следующих действий:

    Date oldfashiondDateObject
            = Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());
    GregorianCalendar oldfashionedCalendarObject
            = GregorianCalendar.from(today.atStartOfDay(ZoneId.systemDefault()));

В обоих случаях результат будет разным в разных часовых поясах (одна из присущих старым классам бед). На моем компьютере первый дает Date из

Mon Jul 03 00:00:00 CEST 2017

Второй дает GregorianCalendar, равный тому же моменту времени.

person Ole V.V.    schedule 03.07.2017

Как @Оле В.В. объяснил, вам нужно определить, в какой день начинается неделя, и сколько дней она должна считаться первой неделей.

Например, стандарт ISO-8601 рассматривает:

  • Понедельник будет первым днем ​​недели.
  • Минимальное количество дней в первую неделю: стандарт считает, что первая неделя требует не менее 4 дней.

Месяц делится на периоды, каждый из которых начинается в определенный первый день недели. Самый ранний период в том же месяце называется неделей 0, если он имеет меньше минимального количества дней, и неделей 1, если он имеет хотя бы минимальное количество дней.

В зависимости от того, как вы их определяете, вы можете получить разные результаты. Рассмотрим календарь на июль 2017 года:

     July 2017
Su Mo Tu We Th Fr Sa
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

Если мы рассмотрим определение ISO, мы имеем:

  • нулевая неделя - с 01.07.2017 по 02.07.2017
  • неделя 1: с 03.07.2017 по 09.07.2017
  • неделя 2: с 10.07.2017 по 16.07.2017
  • неделя 3: с 17 июля 2017 г. по 22 июля 2017 г.
  • неделя 4: с 23 июля 2017 г. по 30 июля 2017 г.
  • неделя 5: 2017-07-31

Если мы рассмотрим первый день недели как воскресенье и минимальное количество дней в первой неделе как 1, мы имеем:

  • неделя 1: 01.07.2017
  • неделя 2: с 2 июля 2017 г. по 08 июля 2017 г.
  • неделя 3: с 09.07.2017 по 15.07.2017
  • неделя 4: с 16 июля 2017 г. по 21 июля 2017 г.
  • неделя 5: с 22 июля 2017 г. по 29 июля 2017 г.
  • неделя 6: с 30 июля 2017 г. по 31 июля 2017 г.

Поскольку решение с Calendar уже было опубликовано, я напишу его, используя новый API. Если вы используете Java 8, рассмотрите возможность использования нового java. API времени. Это проще, меньше ошибок и менее подвержено ошибкам, чем старые API .

Если вы используете Java ‹= 7, вы можете использовать ThreeTen Backport, отличный бэкпорт для новых классов даты/времени Java 8. А для Android есть ThreeTenABP (подробнее о том, как его использовать здесь).

Код ниже работает для обоих. Единственная разница заключается в именах пакетов (в Java 8 — java.time, а в ThreeTen Backport (или ThreeTenABP для Android) — org.threeten.bp), но имена классов и методов одинаковы.

Я использую DateTimeFormatter, потому что он берет все поля (месяц, год, день недели и неделя месяца) и соответствующим образом разрешает день, создавая LocalDate (который имеет поля дня/месяца/года). Я также использую класс WeekFields, который можно настроить для использования разных определений недели (первый день и минимальное количество дней в первую неделю).

Также есть небольшая корректировка, чтобы считать воскресенье нулевым:

public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
    // you can customize your week definition (first day of week and mininum days in first week)
    WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);
    DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        // week of month, using week definition from WeekFields
        .appendValue(wf.weekOfMonth()).appendPattern(" ")
        // day of week
        .appendValue(ChronoField.DAY_OF_WEEK)
        // month and year
        .appendPattern(" M/yyyy")
        // create formatter
        .toFormatter();

    return LocalDate.parse(weekOfMonth + " " +
           // Sunday is 0, adjusting value
           (dayOfWeek == 0 ? 7 : dayOfWeek) + " " + month + "/" + year, fmt);
}

Используя этот код (неделя начинается в воскресенье, и 2 дня должны считаться первой неделей - иначе неделя будет нулевой, как в первом примере выше):

LocalDate d = getDate(1, 6, 7, 2017);

d будет 2017-07-08 (суббота на неделе 1 июля 2017 г.).

Если вы хотите использовать определение ISO 8601, используйте константу WeekFields.ISO вместо метода WeekFields.of().


Как @Оле В.В. предложили в комментариях, это можно сделать и без создания форматтера: получить первое dayOfWeek месяца и настроить его на нужное weekOfMonth:

public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
    // you can customize your week definition (first day of week and mininum days in first week)
    WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);

    // Sunday is 0, adjusting value
    DayOfWeek dow = DayOfWeek.of(dayOfWeek == 0 ? 7 : dayOfWeek);

    // get the first weekday of the month
    LocalDate first = LocalDate.of(year, month, 1).with(TemporalAdjusters.nextOrSame(dow));

    // check in which week this date is
    int week = first.get(wf.weekOfMonth());

    // adjust to weekOfMonth
    return first.plusWeeks(weekOfMonth - week);
}

Это работает точно так же, но без использования средства форматирования — я тестировал даты с 1600 по 2100 год и получил те же результаты.


PS: Calendar также имеет аналогичную конфигурацию через методы setFirstDayOfWeek() и setMinimalDaysInFirstWeek(). Вы можете проверить конфигурацию по умолчанию, вызвав соответствующие методы get.

person Community    schedule 03.07.2017
comment
Ах, WeekFields класс, конечно. Спасибо. Нельзя ли это сделать без построения строки и ее разбора? Я думаю, что я бы попытался получить номер недели первого четверга месяца (например, какой день недели требовался), используя WeekFields.weekOfMonth(). Затем добавьте соответствующее количество недель, используя LocalDate.plusWeeks(). - person Ole V.V.; 04.07.2017
comment
@ОлеВ.В. Без форматтера еще не пробовал. Посмотрим, сработает ли, я обновлю ответ, если найду способ. Спасибо! - Я использовал форматер, потому что он уже определяет день на основе других полей, но давайте посмотрим, можно ли это сделать по-другому... - person ; 04.07.2017
comment
@ОлеВ.В. Готово! Действительно, это намного лучше, чем использовать форматер, большое спасибо! - person ; 04.07.2017
comment
Мне очень нравится наше с тобой сотрудничество, Хьюго. Два разума думают лучше, чем один. - person Ole V.V.; 04.07.2017