Как преобразовать недели года в 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