Ответ 1
Ошибка - исправлено в Java 9
Эта проблема уже сообщалась в JDK-error-log. Стивен Коулборн упоминает об этом как решение:
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter();
Примечание: Это обходное решение не охватывает ваш прецедент только двух символов шаблона SS. Настройкой может быть только использование других полей, таких как MICRO_OF_SECOND (6 раз SSSSSS) или NANO_OF_SECOND (9 раз SSSSSSSSS). Для двухзначных цифр см. Мое обновление ниже.
@PeterLawrey О значении символа шаблона "S" см. эта документация:
Фракция: выводит поле nano-of-second как долю секунды. Значение nano-of-second имеет девять цифр, поэтому количество шаблонов буквы от 1 до 9. Если оно меньше 9, то nano-of-second значение усекается, причем только самые значащие цифры являются вывод. При синтаксическом анализе в строгом режиме количество проанализированных цифр должно совпадают с количеством букв шаблонов. При разборе в режиме смягчения количество проанализированных цифр должно быть как минимум числом букв шаблонов, до 9 цифр.
Итак, мы видим, что S обозначает любую долю секунды (включая наносекунду), а не только миллисекунды. Кроме того, дробная часть в данный момент не очень хорошо подходит для смежного разбора значений, к сожалению.
EDIT:
В качестве фона можно привести некоторые замечания о смежном анализе значений. До тех пор, пока поля разделяются литералами, такими как десятичные точки или разделители временной части (двоеточие), интерпретация полей в анализируемом тексте не является сложной задачей, потому что синтаксический анализатор тогда легко знает, когда останавливаться, т.е. когда часть поля заканчивается и когда начинается следующее поле. Поэтому парсер JSR-310 может обрабатывать текстовую последовательность, если вы указываете десятичную точку.
Но если у вас есть последовательность смежных цифр, охватывающих несколько полей, возникают некоторые трудности с реализацией. Чтобы позволить парсеру знать, когда поле останавливается в тексте, необходимо заранее проинструктировать парсер, что данное поле представлено фиксированной шириной символов цифр. Это работает со всеми appendValue(...)
-методами, которые принимают численные представления.
К сожалению, JSR-310 не сумел сделать это также с дробной частью (appendFraction(...)
). Если вы ищете ключевое слово "смежное" в javadoc класса DateTimeFormatterBuilder
, вы обнаружите, что эта функция ТОЛЬКО реализована с помощью appendValue(...)
-methods. Обратите внимание, что спецификация для буквы шаблона S немного отличается, но внутренне делегируется методу appendFraction()
. Я предполагаю, что мы, по крайней мере, должны будем связываться до Java 9 (как сообщается в JDK-журнале ошибок или позже) до тех пор, пока фракционные части не смогут управлять смежным анализом значений.
Обновление с 2015-11-25:
Следующий код, использующий две цифры разряда, не работает и выдает DateTimeParseException
.
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmssSS")
.appendValue(ChronoField.MILLI_OF_SECOND, 2)
.toFormatter();
String input = "2011120312345655";
LocalDateTime.parse(input, dtf); // abort
Обходной путь
String input = "2011120312345655";
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSS");
Date d = sdf.parse(input);
System.out.println(d.toInstant()); // 2011-12-03T12:34:56.055Z
не работает, потому что SimpleDateFormat
интерпретирует дробь неправильно (см. вывод, 55 мс вместо 550 мс).
Что остается в качестве решения - либо ждать долгое время, дольше, чем Java 9 (или позже?), или писать собственный взлом или использовать сторонние библиотеки в качестве решения.
Решение, основанное на грязном взломе:
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
int len = input.length();
LocalDateTime ldt = LocalDateTime.parse(input.substring(0, len - 2), dtf);
int millis = Integer.parseInt(input.substring(len - 2)) * 10;
ldt = ldt.plus(millis, ChronoUnit.MILLIS);
System.out.println(ldt); // 2011-12-03T12:34:56.550
Решение с использованием Joda-Time:
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
Решение с использованием моей библиотеки Time4J:
String input = "2011120312345655";
ChronoFormatter<PlainTimestamp> f =
ChronoFormatter.ofTimestampPattern("yyyyMMddHHmmssSS", PatternType.CLDR, Locale.ROOT);
System.out.println(f.parse(input)); // 2011-12-03T12:34:56.550
Обновление с 2016-04-29:
Как люди могут видеть через проблему JDK, упомянутую выше, теперь она помечена как разрешенная - для Java 9.