2015-01-13 1 views
3

В старой Яве я могу это сделать таким образом:Как разобрать дату из строки с годом и неделей, используя java.time

System.out.println(new SimpleDateFormat("yyyy w", Locale.UK).parse("2015 1")); 
// shows Mon Dec 29 00:00:00 CET 2014 

System.out.println(new SimpleDateFormat("yyyy w", Locale.US).parse("2015 1")); 
// shows Mon Dec 28 00:00:00 CET 2014 

Я хотел бы использовать java.time в Java 8.

System.out.println(LocalDate.parse("2015 1", DateTimeFormatter.ofPattern("yyyy w", Locale.US))); 

Результат:

java.time.format.DateTimeParseException: Текст '2015 1' не может быть разобран: Невозможно получить LocalDate из TemporalAccessor: {WeekOfWeekBasedYear [WeekFields [вОСКРЕСЕНЬЕ, 1]] = 1, год = 2015}, ISO типа java.time.format . Размещено

Как это сделать в java.time?

Кроме того, я не удовлетворен тем, что должен пройти Локаль, чтобы определить первый день недели: понедельник против воскресенья. Это не функция страны, а функция календаря. Я хотел бы использовать что-то вроде java.time.temporal.WeekFields.ISO, чтобы показать миру, что недели начинаются с понедельника

Я нашел подобный случай: https://stackoverflow.com/questions/3941700/how-to-get-dates-of-a-week-i-know-week-number

но не java.time в Java 8. Кроме того , решение, которое сначала создало объект даты, а позже установило правильную неделю, не изящно. Я хочу создать окончательную дату одним выстрелом.

ответ

5

прямого ответа и решения:

System.out.println( 
    LocalDate.parse("2015 1", 
    new DateTimeFormatterBuilder().appendPattern("YYYY w") 
    .parseDefaulting(WeekFields.ISO.dayOfWeek(), 1) 
    .toFormatter())); 
// output: 2014-12-29 

Пояснения:

а) Вы должны использовать Y вместо у, потому что вы заинтересованы в ISO-8601-будний день не год-оф-эры.

б) Календарная дата не может быть сформирована путем предоставления (недельного) года и недельного номера. День недели имеет значение для определения дня в течение указанной календарной недели. Для предопределенного formatter for week-dates требуется отсутствующий день недели. Поэтому вам нужно построить специализированный синтаксический анализатор, используя шаблон builder. Затем необходимо сказать парсеру, какой день недели нужен - по методу parseDefaulting().

c) Я настаиваю (и защищаю JSR-310 здесь), говоря, что вопрос, когда начинается неделя, не является вопросом календаря, а проблемой, зависящей от страны. США и Франция (например) используют один и тот же календарь, но имеют разные представления, как определить неделю. Стандарт ISO-8601 может применяться с использованием явно поля ISO-WeekFields.ISO.dayOfWeek(). Внимание: Тестирование показало, что использование ChronoField.DAY_OF_WEEK вместе с Locale.ROOT не всегда гарантирует ISO-недельное поведение, как указано в моей первой версии этого ответа (причины еще не ясны для меня - близкий взгляд на источники кажется чтобы было необходимо просветить неинтуитивное поведение).

d) Пакет java-time делает это хорошо - за исключением того, что в понедельник только что указано как номер 1. Я бы предпочел перечисление. Или используйте перечисление и его метод getValue().

e) Сторона уведомления: SimpleDateFormat ведет себя снисходительно по умолчанию. Пакет java-time является более строгим и отказывается изобретать отсутствующий день недели из воздуха - даже в мягком режиме (что, на мой взгляд, довольно хорошо). Программное обеспечение не должно так много догадываться, вместо этого программист должен больше думать о том, какой день недели является правильным.Здесь снова: Требования к приложениям, вероятно, будут отличаться в США и Франции относительно правильной настройки по умолчанию.

+0

объявления c) Я не был точным. Я имел в виду, что, когда я хочу использовать пользовательские настройки понедельника/воскресенья, я не должен использовать Locale, где день недели определен как я хочу, но explicite определяет, что такое первый день недели. Предложенный ответ (с Locale.ROOT) составляет 100%, что я спросил – michaldo

+0

я применил решение 2016 года:. System.out.println ( LocalDate.parse ("2016 1", новый DateTimeFormatterBuilder() appendPattern (» ГГГГ w ") .parseDefaulting (ChronoField.DAY_OF_WEEK, 1) .toFormatter() .Locale (Locale.ROOT))); // выпуск: 2015-12-28 Почему я получил 2015-12-28? Я ожидаю получить 2016-01-04, потому что первая неделя 2016 года 4-10 января – michaldo

+0

@michaldo Большое спасибо за ваши отзывы. Я изменил свой ответ, предпочитая второе решение. которые все еще работают для '2016-1', что дает результат' 2016-01-04'. Похоже, мы поразили некоторую странность в JSR-310-API. –

2

Правильный номер accepted Answer by Meno Hochschild. Вот альтернативный маршрут.

YearWeek

Используйте YearWeek класс в ThreeTen-Extra библиотеке. Эта библиотека расширяет java.time с дополнительной функциональностью. Этот конкретный класс представляет год-неделю в ISO 8601 week date system.

Заводской метод YearWeek.of принимает пару целых аргументов, номер недели и номер недели.

YearWeek yw = YearWeek.of(2015 , 1); 

ISO 8601

В идеале вы должны использовать стандартные ISO 8601 форматы для строк, представляющих значения даты и времени. Для year-week that would be yyyy-Www номер недели по умолчанию, дефис, W и две цифры для номера недели с нулевым заполнением при необходимости.

2015-W01

В java.time классы и ThreeTen-Extra классы, используют стандарт ISO 8601 форматов по умолчанию при разборе и генерации строк. Так что жизнь много проще, если придерживаться стандарта.

YearWeek yw = YearWeek.parse("2015-W01"); 
String output = yw.toString(); // 2015-W01 

Разбор целых чисел.

У вас есть нестандартный формат чисел. Итак, давайте проанализируем вашу строку как две части, по одному числу, которые должны интерпретироваться как целые числа int.

String string = "2015 1"; 
String[] parts = string.split(" "); // SPACE character. 
String part1 = parts[0]; // 2015 
String part2 = parts[1]; // 1 

Integer класс преобразует такие строки в int примитивных значений.

int weekBasedYearNumber = Integer.parseInt(part1) ; 
int weekNumber = Integer.parseInt(part2) ; 

Теперь позвоните в этот заводской метод.

YearWeek yw = YearWeek.of(weekBasedYearNumber , weekNumber); 

LocalDate

В течение первого дня недели, здесь мы обсуждаем стандарт ISO 8601 определение недели. В этом определении понедельник - это всегда первый день, неделя с понедельника по воскресенье. Неделя № 1 содержит первый четверг календарного года. В случае 2015-W01 в четверг будет 1 января 2015 года.

Таким образом, нет Locale.

Я не совсем уверен в вашей цели, но, похоже, вы выбираете конкретные даты в течение нескольких дней в пределах вашей недели. Это довольно легко с классом YearWeek и перечислением DayOfWeek.

LocalDate monday = yw.atDay(DayOfWeek.MONDAY); // 2014-12-29 start-of-week. 
LocalDate friday = yw.atDay(DayOfWeek.FRIDAY); // 2015-01-02 
LocalDate sunday = yw.atDay(DayOfWeek.SUNDAY); // 2015-01-04 end-of-week. 

screen shot of calendar month January 2015 with ISO 8601 week number 1 running from 2014-12-29 Monday to 2015-01-04 Sunday

0

Это также может быть достигнут путем ввода значения по умолчанию для разбора дня недели, используя «ChronoField.Day_Of_Week» и установить значение, как «1» следующим образом:

System.out.println("Date From Year and Week : "+ 
       LocalDate.parse("2015 1", 
       new DateTimeFormatterBuilder().appendPattern("YYYY w") 
       .parseDefaulting(ChronoField.DAY_OF_WEEK, 1) 
       .toFormatter())); 
+0

Это дубликат принятого ответа ИМХО – michaldo