Не удается ли java.time анализировать долю секунды?

В первом выпуске Java 8 (b132) в Mac OS X (Mavericks) этот код использует новый пакет java.time работает:

String input = "20111203123456"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );

Рендеринг:

2011-12-03T12:34:56

Но когда я добавляю "SS" для доли секунды (и "55" в качестве ввода), как указано в DateTimeFormatter class doc, создается исключение:

java.time.format.DateTimeParseException: Text '2011120312345655' could not be parsed at index 0

В документе говорится, что по умолчанию используется строгий режим, и для него требуется такое же количество символов формата, как и для вводимых цифр. Поэтому я не понимаю, почему этот код не работает:

String input = "2011120312345655"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );

Другой пример, использующий пример из документации («978») (неуспешный):

String input = "20111203123456978"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );

Этот пример работает с добавлением десятичной точки (но я не нахожу такого требования в документе):

String input = "20111203123456.978"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );

Оказывает:

localDateTime: 2011-12-03T12:34:56.978

Отсутствие символа точки во входной строке или формата приводит к ошибке.

Не удается:

String input = "20111203123456.978"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );

Не удается:

String input = "20111203123456978"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );

person Basil Bourque    schedule 23.03.2014    source источник
comment
Эта доля составляет наносекунду. Можете попробовать с 9 цифрами? Хотя, читая документы, он должен был работать с менее чем 9 цифрами, если шаблон соответствует количеству цифр.   -  person anonymous    schedule 23.03.2014
comment
Перечитав документы, можете ли вы попробовать поставить десятичную точку после поля секунд? Нравится 20111203123456.978?   -  person anonymous    schedule 23.03.2014
comment
Добавление десятичной точки заставляет его работать. См. Добавление внизу вопроса. Но можете ли вы указать мне на такое требование в документе? Я пытаюсь определить, следует ли мне подавать отчет об ошибке.   -  person Basil Bourque    schedule 23.03.2014
comment
Во-первых, хорошо, что у вас сработало. Во-вторых, я не видел, чтобы это было написано специально, это больше похоже на мою интерпретацию доли секунды. Доли секунды должна предшествовать десятичная дробь. Не имеет смысла без десятичной дроби. Для сравнения со временем ЧЧ: мм: сс двоеточие обычно используется в качестве разделителя, но оно не является частью поля. Следовательно, двоеточие не требуется, потому что оно не является частью поля. Но доли секунды - это часть поля секунд. Поэтому его следует разделять десятичной дробью. Вот как я это понимаю. Но не стесняйтесь регистрировать отчет об ошибке, если вы все еще думаете, что ошибка есть.   -  person anonymous    schedule 23.03.2014
comment
Почему доли секунды заслуживают пунктуации (точки), а минуты часа - двоеточия? Я не шучу.   -  person Basil Bourque    schedule 23.03.2014
comment
Как я уже сказал, потому что доли секунды - это ЧАСТЬ поля секунд. Это не просто разделитель, как двоеточие, разделяющее часы, минуты и секунды. Однако вы также можете возразить, поскольку это дробь, предполагается десятичная точка. С другой стороны, дизайнеры могут также подумать, что десятичная дробь необходима при указании дроби. Что вы могли бы попробовать, если это все еще работает. 20111203123456 .978 и примените ггггММддЧЧммсс SSS?   -  person anonymous    schedule 23.03.2014
comment
@anonymous (а) Я понимаю вашу интуитивную логику о том, что доли секунды принадлежат секундам минуты. Но документ явно выделяет долю секунды как отдельный объект, как и все другие части. Думаю, я продолжу работу над отчетом об ошибке. (b) Я попробовал ваше последнее предложение, опустив десятичную точку либо во входных данных, либо в программе форматирования. Оба вызывают сбой.   -  person Basil Bourque    schedule 23.03.2014
comment
Я думаю, что тег java-time может быть не так понятен. Под этим тегом мы также находим записи, которые не имеют ничего общего с новым API даты и времени в Java 8. Здесь, на SO, тег jsr310 гораздо более распространен до сих пор, хотя я признаю, что это имя скорее обращается к экспертам.   -  person Meno Hochschild    schedule 24.03.2014
comment
@MenoHochschild спасибо за подсказку. Я добавлю тег jsr310 и рассмотрю возможность создания нового тега.   -  person Basil Bourque    schedule 24.03.2014
comment
Я добавил новый тег java.time с ожидающим отрывком из вики.   -  person Basil Bourque    schedule 24.03.2014
comment
@BasilBourque, пожалуйста, добавьте эту вики в тег существующий вместо того, чтобы создавать новый.   -  person Charles    schedule 25.03.2014
comment
@Charles Учитывая то, что Мено сказал об историческом использовании java-time для других целей, предшествующих JSR 310 API, кажется целесообразным создать новый тег, специально предназначенный для нового API и названный так же, как новый API. Старый использовался всего 7 раз. Почему бы не удалить / игнорировать старый и не создать новый с именем, соответствующим имени пакета API? Я прочитал 3 записи на Meta StackExchange и не нашел никаких указаний на обратное. В целом я понимаю вашу заботу о том, чтобы не загрязнять пул тегов, но эта ситуация с java.time JSR 310 кажется уникальной.   -  person Basil Bourque    schedule 25.03.2014
comment
@BasilBourque, одна из больших проблем заключается в том, что система тегов SE не учитывает. так же учитываются другие знаки препинания. Люди, ищущие java-time, не увидят java.time, и в разных местах существует ужасное сочетание тегов, которые используют один вместо другого. Если вы думаете, что тег с точкой будет более полезен, давайте полностью удалим другой тег, чтобы устранить двусмысленность.   -  person Charles    schedule 25.03.2014


Ответы (3)


Ошибка - исправлена ​​в Java 9.

Об этой проблеме уже сообщалось в JDK-bug-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 см. эту документацию < / а>:

Дробь: выводит поле наносекунды как доли секунды. Значение наносекунды состоит из девяти цифр, таким образом, количество букв шаблона составляет от 1 до 9. Если оно меньше 9, то значение наносекунды усекается, и выводятся только самые старшие цифры. При синтаксическом анализе в строгом режиме количество анализируемых цифр должно соответствовать количеству букв шаблона. При синтаксическом анализе в мягком режиме количество анализируемых цифр должно быть не меньше количества букв шаблона, до 9 цифр.

Итак, мы видим, что S означает любую долю секунды (включая наносекунду), а не только миллисекунды. Более того, дробная часть, к сожалению, в настоящий момент не подходит для синтаксического анализа смежных значений.

РЕДАКТИРОВАТЬ:

В качестве фона сделаем несколько замечаний о синтаксическом анализе смежных значений. Пока поля разделены литералами, такими как десятичная точка или разделители частей времени (двоеточие), интерпретация полей в тексте, который нужно проанализировать, несложна, потому что синтаксический анализатор легко знает, когда остановиться, то есть когда часть поля завершена и когда начинается следующее поле. Следовательно, синтаксический анализатор JSR-310 может обработать текстовую последовательность, если вы укажете десятичную точку.

Но если у вас есть последовательность соседних цифр, охватывающая несколько полей, возникают некоторые трудности с реализацией. Чтобы синтаксический анализатор знал, когда поле останавливается в тексте, необходимо заранее проинструктировать синтаксический анализатор, что данное поле представлено цифровыми символами фиксированной ширины. Это работает со всеми appendValue(...)-методами, которые предполагают числовые представления.

К сожалению, JSR-310 не удалось сделать это также с дробной частью (appendFraction(...)). Если вы поищете ключевое слово, смежное с javadoc класса DateTimeFormatterBuilder, то обнаружите, что эта функция реализована ТОЛЬКО с помощью appendValue(...)-методов. Обратите внимание, что спецификация для шаблона буквы S немного отличается, но внутренне делегирует appendFraction()-method. Я предполагаю, что нам придется по крайней мере ждать до Java 9 (как сообщается в JDK-bug-log или позже ???), пока дробные части не смогут управлять синтаксическим анализом смежных значений.


Обновление от 25 ноября 2015 г .:

Следующий код, использующий только две цифры дробной части, не работает и неверно интерпретирует миллисекундную часть:

    DateTimeFormatter dtf =
        new DateTimeFormatterBuilder()
            .appendPattern("yyyyMMddHHmmss")
            .appendValue(ChronoField.MILLI_OF_SECOND, 2)
            .toFormatter();
    String input = "2011120312345655";
    LocalDateTime ldt = LocalDateTime.parse(input, dtf);
    System.out.println(ldt); // 2011-12-03T12:34:56.055

Обходной путь

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

Обновление от 29 апреля 2016 г .:

Как видно из упомянутой выше проблемы JDK, теперь она помечена как решенная - для Java 9.

person Meno Hochschild    schedule 24.03.2014
comment
В своем обновлении от 25 ноября 2015 г. вы говорите, что это средство форматирования выдает исключение DateTimeParseException: new DateTimeFormatterBuilder().appendPattern("yyyyMMddHHmmssSS").appendValue(ChronoField.MILLI_OF_SECOND, 2). Но он не вызывает исключения, если вы удаляете SS в конце шаблона (которого там не должно быть, поскольку вы используете appendValue() для миллисекунд). Проблема только в том, что он неправильно интерпретирует дробь, возвращая 55 вместо 550, как SimpleDateFormat. - person Etienne Neveu; 06.11.2020
comment
@EtienneNeveu Большое спасибо. Я исправил свое заявление от 2015 года согласно вашему комментарию. - person Meno Hochschild; 07.11.2020

DateTimeFormatterBuilder#appendFraction(ChronoField.MILLI_OF_SECOND, 0, 3, true)

Что-то вроде этого мне помогло

person Andrei Amarfii    schedule 16.05.2019

Вот алгоритм, который регулирует порядок конечных нулей, которые обычно возвращаются из форматированной даты String.

/**
 * Takes a Date and provides the format whilst compensating for the mistaken representation of sub-second values.
 * i.e. 2017-04-03-22:46:19.000991 -> 2017-04-03-22:46:19.991000
 * @param pDate Defines the Date object to format.
 * @param pPrecision Defines number of valid subsecond characters contained in the system's response.
 * */
private static final String subFormat(final Date pDate, final SimpleDateFormat pSimpleDateFormat, final int pPrecision) throws ParseException {
    // Format as usual.
    final String lString        = pSimpleDateFormat.format(pDate);
    // Count the number of characters.
    final String lPattern       = pSimpleDateFormat.toLocalizedPattern();
    // Find where the SubSeconds are.
    final int    lStart         = lPattern.indexOf('S');
    final int    lEnd           = lPattern.lastIndexOf('S');
    // Ensure they're in the expected format.
    for(int i = lStart; i <= lEnd; i++) { if(lPattern.charAt(i) != 'S') {
        // Throw an Exception; the date has been provided in the wrong format.
       throw new ParseException("Unable to process subseconds in the provided form. (" + lPattern + ").", i);
    } }
    // Calculate the number of Subseconds. (Account for zero indexing.)
    final int lNumSubSeconds = (lEnd - lStart) + 1;
    // Fetch the original quantity.
    String lReplaceString = lString.substring(lStart + (lNumSubSeconds - pPrecision), lStart + lNumSubSeconds);
    // Append trailing zeros.
    for(int i = 0; i < lNumSubSeconds - pPrecision; i++) { lReplaceString += "0"; }
    // Return the String.
    return lString.substring(0, lStart) + lReplaceString;
}
person Mapsy    schedule 04.04.2017
comment
Ответы на Stack Overflow лучше, если они включают обсуждение или объяснение. - person Basil Bourque; 04.04.2017
comment
@BasilBourque Вы абсолютно правы. Я надеялся, что комментарии скажут сами за себя, но вы правы, что они, по крайней мере, заслуживают некоторого контекста. - person Mapsy; 04.04.2017