Как преобразовать недели года в LocalDate

У меня есть строка с форматами год-неделя, например "2015-40" и "год-месяц", например, "2015-08", которые хотелось бы преобразовать в LocalDate в Scala.

Я пробовал использовать

val date = "2015-40"
val formatter = DateTimeFormatter.ofPattern("yyyy-ww") 
LocalDate.parse(date, formatter)

но в итоге возникает ошибка DateTimeParseException. Был бы признателен за любую помощь о том, как это сделать

заранее спасибо

2 ответа

    String date = "2015-40";
    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendPattern("YYYY-ww")
            .parseDefaulting(ChronoField.DAY_OF_WEEK, DayOfWeek.MONDAY.getValue())
            .toFormatter(Locale.FRANCE);
    LocalDate ld = LocalDate.parse(date, formatter);
    
    System.out.println(ld);

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

2015-09-28

Почему было исключение? Нам не хватало пары вещей, чтобы разобрать2015-40 в LocalDate:

  • А LocalDate- календарная дата, а 40 неделя состоит из 7 дней. Java не знает, какой из этих 7 дней вы хотите, и отказывается делать выбор за вас. В моем коде выше я указал понедельник. Любой другой день недели должен работать.
  • Немного тоньше. В то время как для человека 2015 год и 40 неделя однозначны, то же самое не всегда верно в отношении Нового года, когда неделя 1 может начаться до Нового года или неделя 52 или 53 продлить после Нового года. Таким образом, календарный год и номер недели не всегда определяют одну конкретную неделю. Вместо этого нам нужна концепция недельного года или недельного года. Неделя в году длится с недели 1 включительно, независимо от того, начинается ли это за несколько дней до или после Нового года. И это длится до последнего дня последней недели, чаще всего за несколько дней до или после Нового года. СказатьDateTimeFormatter что мы хотим разобрать (или распечатать) год недели, нам нужно использовать верхний регистр YYYY вместо нижнего регистра yyyy (или uuuu).

Кроме того, если вы можете повлиять на формат, рассмотрите 2015-W40 с W. Это формат ISO 8601 для года и недели. В ISO2015-12обозначает год и месяц, и многие прочтут это так. Таким образом, чтобы устранить неоднозначность и избежать неправильного прочтения.

Редактировать:

В своем объяснении я использовал недельную схему ISO (понедельник - это первый день недели, а неделя 1 определяется как первая неделя, имеющая как минимум 4 дня в новом году). Вы можете передать другой язык в конструктор форматтера, чтобы получить другую недельную схему.

Если вы уверены, что ваши недели следуют за ISO, предложение Андреаса в комментарии достаточно хорошее, и мы хотим, чтобы он был частью ответа:

В качестве альтернативы добавьте библиотеку ThreeTen Extra, чтобы вы могли использовать YearWeek класс, у которого есть хороший atDay​(DayOfWeek dayOfWeek) метод получения LocalDate.

Ссылка: Статья в Википедии: ISO 8601

LocalDateсостоит из трех частей: года, месяца и числа месяца. Итак, в случаеyear-month строка, вам нужно будет получить LocalDateв определенный день согласно вашему требованию. Как показано в демонстрационном коде, синтаксический анализ строки год-месяц выполняется просто.

На случай, если year-week строка, вам нужно будет получить LocalDate в определенный день недели, например, понедельник или сегодняшний день и т. д. Кроме того, вместо того, чтобы анализировать строку напрямую, мне было проще получить год и неделю, а затем использовать методы LocalDate чтобы получить требуемый экземпляр LocalDate.

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;

public class Main {
    public static void main(String[] args) {
        //#################### Year-Month #######################
        // Given year-month string
        var yearMonthStr = "2015-08";

        // LocalDate parsed from yearMonthStr and on the 1st day of the month
        LocalDate date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atDay(1);
        System.out.println(date2);

        // LocalDate parsed from yearMonthStr and on the last day of the month
        date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atEndOfMonth();
        System.out.println(date2);

        // LocalDate parsed from yearMonthStr and on specific day of the month
        date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atDay(1).withDayOfMonth(10);
        System.out.println(date2);

        
        //#################### Year-Week #######################
        // Given year-week string
        var yearWeekStr = "2015-40";

        // Split the string on '-' and get year and week values
        String[] parts = yearWeekStr.split("-");
        int year = Integer.parseInt(parts[0]);
        int week = Integer.parseInt(parts[1]);

        // LocalDate with year, week and today's day e.g. Fri
        LocalDate date1 = LocalDate.now()
                            .withYear(year)
                            .with(WeekFields.ISO.weekOfYear(), week);
        System.out.println(date1);

        // LocalDate with year, week and next Mon (or same if today is Mon)
        date1 = LocalDate.now()
                .withYear(year)
                .with(WeekFields.ISO.weekOfYear(), week)
                .with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
        System.out.println(date1);

        // LocalDate with year, week and today's day previous Mon (or same if today is Mon)
        date1 = LocalDate.now()
                .withYear(year)
                .with(WeekFields.ISO.weekOfYear(), week)
                .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
        System.out.println(date1);
    }
}

Выход:

2015-08-01
2015-08-31
2015-08-10
2015-10-02
2015-10-05
2015-09-28
Другие вопросы по тегам