Шаблон DateTimeFormatter с литералом и без разделителя не работает

Синтаксический анализатор, созданный DateTimeFormatter.ofPattern, демонстрирует следующее интересное поведение, которое не позволяет мне написать шаблон для анализа строки, подобной 20150100:

System.out.println(DateTimeFormatter.ofPattern("yyyyMM").parse("201501", YearMonth::from)); // works
System.out.println(DateTimeFormatter.ofPattern("yyyyMM'aa'").parse("201501aa", YearMonth::from)); // works
System.out.println(DateTimeFormatter.ofPattern("yyyyMM'00'").parse("20150100", YearMonth::from));
// java.time.format.DateTimeParseException: Text '20150100' could not be parsed at index 0

Я отладил код, кажется, проблема вызвана разбором поля года за концом строки (максимальная ширина для трех y и более всегда равна 19). Однако я не понимаю, как это может работать для шаблона без литерала '00' в конце.

Есть ли способ исправить это с помощью построителя форматирования?

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

Поскольку Джаррод ниже подтвердил, что это ошибка, я погуглил и наконец нашел отчеты об ошибках:

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8031085

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8032491

Оба исправлены только в Java 9, хотя ......


person billc.cn    schedule 25.08.2016    source источник
comment
Почему вы не хотите использовать средство форматирования?   -  person Tunaki    schedule 25.08.2016
comment
Вы пробовали пошаговую отладку, чтобы увидеть, в чем разница?   -  person    schedule 25.08.2016
comment
@Tunaki Потому что это простой случай, который поддерживает старые инструменты, такие как SimpleDateFormat, и поведение похоже на ошибку ...   -  person billc.cn    schedule 25.08.2016
comment
SimpleDateFormat тоже не работает, потому что они оба полагаются на DateTimeFormatter, и именно в этом заключается ошибка.   -  person    schedule 25.08.2016
comment
@Tunaki Оказывается, в сборщике тоже есть ошибка ... См. вторую ссылку на ошибку.   -  person billc.cn    schedule 25.08.2016
comment
@JarrodRoberson Нет, SimpleDateFormat не выходит из строя так же. Это независимая реализация, которая НЕ полагается на DateTimeFormatter. Просто изучите старый исходный код SimpleDateFormat. Обе реализации терпят неудачу по разным внутренним причинам. Я бы не стал квалифицировать это как ошибку. Это просто отсутствующая функция в обеих библиотеках.   -  person Meno Hochschild    schedule 28.08.2016


Ответы (2)


В DateTimePrinterParser есть ошибка:

Я пошагово все отлаживал, видимо у вас не может быть цифр как литералов. Подобные тестовые коды доказывают это, если вы пройдёте отладку до метода DateTimeFormatterBuilder.parse(), вы увидите, что он делает неправильно.

По-видимому, синтаксический анализатор Value(YearOfEra,4,19,EXCEEDS_PAD) потребляет 00, где они останавливаются, если это не цифры, потому что он ищет число длиной от 4 до 19 цифр. DateTimeFormatter, встроенный в DateTimeParseContext, неверен.

Если вы поместите нецифровой символьный литерал, такой как xx, он работает, а цифровые литералы - нет.

Оба они терпят неудачу:

final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM'00'");
System.out.println(sdf.parse("20150100"));

Исключение в потоке "основной" java.text.ParseException: неразборчивая дата: "20150100" в java.text.DateFormat.parse(DateFormat.java:366)

final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM'00'");
System.out.println(dateTimeFormatter.parse("20150100", YearMonth::from));

Исключение в потоке «основной» java.time.format.DateTimeParseException: текст «20150100» не может быть проанализирован по индексу 0 в java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949) в java.time.format.DateTimeFormatter .parse(DateTimeFormatter.java:1851)

Оба они преуспевают:

final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM'xx'");
System.out.println(sdf.parse("201501xx"));

Чт, 01 января, 00:00:00 по восточному поясному времени 2015 г.

final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM'xx'");
System.out.println(dateTimeFormatter.parse("201501xx", YearMonth::from));

2015-01

person Community    schedule 25.08.2016
comment
Спасибо, что подтвердили это. - person billc.cn; 25.08.2016
comment
Пошаговая отладка метода в классе, который я упоминаю в ответе, слишком запутанная логика для меня, чтобы копаться, но я вижу, что он делает. - person ; 25.08.2016

Если вы не возражаете против использования сторонней библиотеки, вы можете попробовать мою библиотеку Time4J, новейшая версия v4.18 может делать то, что вы хотите:

import net.time4j.Month;
import net.time4j.range.CalendarMonth;
import net.time4j.format.expert.ChronoFormatter;
import net.time4j.format.expert.PatternType;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.text.ParseException;
import java.util.Locale;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@RunWith(JUnit4.class)
public class CalendarMonthTest {
    @Test
    public void parse2() throws ParseException {
      assertThat(
        ChronoFormatter.ofPattern(
            "yyyyMM'00'",
            PatternType.CLDR,
            Locale.ROOT,
            CalendarMonth.chronology()
        ).parse("20150100"),
        is(CalendarMonth.of(2015, Month.JANUARY)));
    }
}

Кстати, ссылки на журнал ошибок JDK не имеют отношения к вашей проблеме. Эти проблемы описывают только проблемы при применении синтаксического анализа соседних цифр в контексте долей секунды. Хотя эта проблема будет решена с помощью Java-9, ваша проблема не будет решена. Может быть, вы хотите открыть новый выпуск там? Но я сомневаюсь, что Oracle воспримет это как ошибку. Это скорее новая функция, до сих пор не поддерживаемая ни одной библиотекой, распространяемой Oracle. Ожидается, что литералы с (начальными) цифрами в JSR-310 (он же java.time-package) будут участвовать в синтаксическом анализе смежных значений (и в SimpleDateFormat тоже нет).

Боковое примечание: Time4J — это не просто ответ на эту деталь (цифровые литералы), но, как правило, предлагает более высокую производительность при синтаксическом анализе и может использоваться параллельно с JSR-310 из-за множества методов преобразования. Например: чтобы получить экземпляр YearMonth, просто вызовите calendarMonth.toTemporalAccessor() для проанализированного результата.

person Meno Hochschild    schedule 28.08.2016