Номера недель между двумя датами в Java - PullRequest
0 голосов
/ 29 августа 2018

Мое намерение - получить номера недель между двумя диапазонами дат. Дата 24-го падения на неделе № 34 и 26-го падения на неделе № 35. Теперь проблема в том, что если я поставлю 2018-08-22T12:18:06,166 в качестве даты начала, я получу 34,35,36. Я не ожидаю 36 здесь, потому что дата окончания не попадает в неделю 36. Может кто-нибудь помочь мне. Этот вопрос отличается от решения, представленного здесь Номера недель от даты начала до даты окончания Java . Решение имеет проблему, которую я обнаружил недавно

Ниже приведен код для его получения:

public static void main(String[] args) {
    DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss','SSS");
    LocalDateTime startDate = LocalDateTime.parse("2018-08-24T12:18:06,166", format);
    LocalDateTime endDate = LocalDateTime.parse("2018-08-26T12:19:06,188", format);

    numberOfWeeks(startDate, endDate);

}

public static void numberOfWeeks(LocalDateTime startDate, LocalDateTime endDate) {
    int addWeek = 0;

    TemporalField tf = WeekFields.SUNDAY_START.weekOfYear();
    if (startDate.get(tf) < endDate.get(tf)) {
        addWeek = 1;
    }
    long weeks = WEEKS.between(startDate, endDate) + addWeek;
    List<String> numberWeeks = new ArrayList<>();
    if (weeks >= 0) {
        int week = 0;
        do {
            //Get the number of week
            LocalDateTime dt = startDate.plusWeeks(week);
            int weekNumber = dt.get(tf);
            numberWeeks.add(String.format("%d-W%d", dt.getYear(), weekNumber));
            week++;
        } while (week <= weeks);
    }
    System.out.println(numberWeeks);
}

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

ТЛ; др

LocalDateTime.parse( "2018-08-24T12:18:06,166".replace( "," , "." ) ).toLocalDate()
.with( TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) )
.datesUntil(
    LocalDateTime.parse( "2018-08-26T12:19:06,188".replace( "," , "." ) ).toLocalDate()
    .with( TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) )
    .plusWeeks( 1 ) 
    ,
    Period.ofWeeks( 1 )
)
.map( localDate -> localDate.get( WeekFields.SUNDAY_START.weekOfWeekBasedYear() ) )
.collect( Collectors.toList() )
.toString()

[34, 35]

Streams

Давайте возьмем идею WeekFields, показанную в правильный ответ Ole V.V. , но сократим код, используя технологию Java Stream. Хотя это интересно, я не обязательно рекомендую этот подход.

Сначала проанализируйте ваши входные строки, чтобы получить LocalDate объекты. Класс LocalDate представляет значение только для даты без времени суток и без часового пояса.

К сожалению, классы java.time не поддерживают запятую как дробный второй разделитель и вместо этого ожидают точку (FULL STOP). Это противоречит стандарту ISO 8601 , который допускает и 1025 * и предпочитает запятую. Это один из немногих недостатков, которые я обнаружил в отличных классах java.time , предположительно из-за предвзятости программистов из Соединенных Штатов. Чтобы обойти этот недостаток, мы заменяем запятую ПОЛНОЙ ОСТАНОВКОЙ.

LocalDate inputDateStart = 
    LocalDateTime.parse( 
        "2018-08-24T12:18:06,166".replace( "," , "." )  // Unfortunately, the *java.time* classes fail to support the comma and instead only period. This runs contrary to the ISO 8601 standard which allows both and prefers comma.
    )
    .toLocalDate()
;  
LocalDate inputDateStop = 
    LocalDateTime.parse( 
        "2018-08-26T12:19:06,188".replace( "," , "." ) 
    )
    .toLocalDate()
;

Вы хотите работать с неделями, определенными как начинающиеся с воскресенья. Поэтому откорректируйте ваши даты ввода до воскресенья или до этой даты.

Обратите внимание, что здесь мы добавляем неделю к остановке, чтобы учесть потребности Вопроса. Чаще мы бы не делали это дополнение, чтобы следовать подходу Half-Open для определения промежутка времени, где начало включительно , а окончание эксклюзив . В отличие от Half-Open, Вопрос, очевидно, требует полностью закрытого подхода, когда начало и конец включаются (я не рекомендую это).

LocalDate start = inputDateStart.with( 
    TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) 
);
LocalDate stop = inputDateStop.with( 
    TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) 
)
.plusWeeks( 1 )  // Add one to suit the Question, whereas commonly in date-time work we would have used Half-Open logic.
;  

Определить серию дат как Stream< LocalDate >. Мы прыгаем неделю за раз, пропуская Period одной недели.

Stream< LocalDate > stream = 
    startDate
    .datesUntil( 
        stopDate , 
        Period.ofWeeks( 1 ) 
    )
;

Если хотите, вы можете увидеть эти даты, собрав их из потока в список. Но обратите внимание, что это истощает поток. Вам нужно будет заново установить поток, чтобы продолжить наш код дальше.

List< LocalDate > dates = stream.collect( Collectors.toList() );
System.out.println( dates );

[2018-08-19, 2018-08-26]

Выполнить эту серию дат в потоке. На каждом объекте LocalDate получите номер недели. Соберите номер каждой возвращенной недели как Integer объект, все собранные в List.

List< Integer > weekNumbers = 
    stream
    .map( 
        localDate -> localDate.get( WeekFields.SUNDAY_START.weekOfWeekBasedYear() ) 
    )
    .collect( Collectors.toList() )
;

Дамп на консоль.

System.out.println( weekNumbers );

[34, 35]

One-лайнер

Если вы действительно хотите сойти с ума от краткости, мы можем сделать все это в одной строке кода. Я не рекомендую это, но это интересно попробовать.

System.out.println(
    LocalDateTime.parse( "2018-08-24T12:18:06,166".replace( "," , "." ) ).toLocalDate()
    .with( TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) )
    .datesUntil(
        LocalDateTime.parse( "2018-08-26T12:19:06,188".replace( "," , "." ) ).toLocalDate()
        .with( TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) )
        .plusWeeks( 1 ) 
        ,
        Period.ofWeeks( 1 )
    )
    .map( localDate -> localDate.get( WeekFields.SUNDAY_START.weekOfWeekBasedYear() ) )
    .collect( Collectors.toList() )
);

[34, 35]

0 голосов
/ 29 августа 2018
public static void numberOfWeeks(LocalDateTime startDateTime, LocalDateTime endDateTime) {
    if (startDateTime.isAfter(endDateTime)) {
        throw new IllegalArgumentException("End date must not be before start date");
    }

    LocalDate endDate = endDateTime.toLocalDate();
    List<String> numberWeeks = new ArrayList<>();
    LocalDate currentDate = startDateTime.toLocalDate();
    while (currentDate.isBefore(endDate)) {
        numberWeeks.add(formatWeek(currentDate));
        currentDate = currentDate.plusWeeks(1);
    }
    // Now currentDate is on or after endDate, but are they in the same week?
    if (currentDate.get(WeekFields.SUNDAY_START.weekOfWeekBasedYear()) 
            == endDate.get(WeekFields.SUNDAY_START.weekOfWeekBasedYear())) {
        numberWeeks.add(formatWeek(currentDate));
    }

    System.out.println(numberWeeks);
}

public static String formatWeek(LocalDate currentDate) {
    return String.format("%d-W%d", 
            currentDate.get(WeekFields.SUNDAY_START.weekBasedYear()), 
            currentDate.get(WeekFields.SUNDAY_START.weekOfWeekBasedYear()));
}

С помощью вышеуказанных методов ваш main метод из вопроса печатает:

[2018-W34, 2018-W35]

Я вижу, что вы проигнорировали другой ответ в связанном вопросе, ответ с использованием YearWeek из библиотеки ThreeTen Extra. Так что я предположил, что ты не хотел этого делать. Поэтому я пользуюсь LocalDate неделями.

Хотя несколько пользователей не смогли воспроизвести вашу точную проблему, я согласен с тем, что ваш код в вопросе имеет недостатки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...