java.time 是否无法解析几分之一秒?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22588051/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is java.time failing to parse fraction-of-second?
提问by Basil Bourque
With the first release of Java 8 (b132) on Mac OS X (Mavericks), this code using the new java.time packageworks:
在 Mac OS X (Mavericks) 上 Java 8 (b132) 的第一个版本中,使用新的java.time 包的这段代码有效:
String input = "20111203123456";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Rendering:
渲染:
2011-12-03T12:34:56
But when I add "SS" for fraction-of-second (and "55" as input), as specified in the DateTimeFormatter class doc, an exception is thrown:
但是,当我按照DateTimeFormatter 类 doc中的指定为秒的分数添加“SS”(并将“55”作为输入)时,会引发异常:
java.time.format.DateTimeParseException: Text '2011120312345655' could not be parsed at index 0
The doc says Strict mode is used by default and requires the same number of format characters as input digits. So I'm confused why this code fails:
该文档说默认情况下使用严格模式,并且需要与输入数字相同数量的格式字符。所以我很困惑为什么这段代码会失败:
String input = "2011120312345655";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Another example using example from documentation ("978") (fails):
另一个示例使用文档中的示例(“978”)(失败):
String input = "20111203123456978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
This example works, adding a decimal point (but I find no such requirement in the doc):
这个例子有效,添加了一个小数点(但我在文档中没有发现这样的要求):
String input = "20111203123456.978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Renders:
渲染:
localDateTime: 2011-12-03T12:34:56.978
Omitting the period character from either the input string orthe format cause a fail.
从输入字符串或格式中省略句点字符会导致失败。
Fails:
失败:
String input = "20111203123456.978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Fails:
失败:
String input = "20111203123456978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
采纳答案by Meno Hochschild
Bug – Fixed in Java 9
错误 – 在 Java 9 中修复
This issue was already reported in JDK-bug-log. Stephen Colebourne mentions as work-around following solution:
此问题已在JDK-bug-log 中报告。Stephen Colebourne 提到了以下解决方案的解决方法:
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter();
Note: This workaround does not cover your use-caseof only two pattern symbols SS. An adjustment might only be to use other fields like MICRO_OF_SECOND (6 times SSSSSS) or NANO_OF_SECOND (9 times SSSSSSSSS). For two fraction digits see my update below.
注意:此解决方法不涵盖您仅使用两个模式符号 SS 的用例。调整可能只是使用其他字段,如 MICRO_OF_SECOND(6 次 SSSSSS)或 NANO_OF_SECOND(9 次 SSSSSSSS)。对于两个小数位数,请参阅下面的更新。
@PeterLawrey About the meaning of pattern symbol "S" see this documentation:
@PeterLawrey 关于模式符号“S”的含义,请参阅此文档:
Fraction: Outputs the nano-of-second field as a fraction-of-second. The nano-of-second value has nine digits, thus the count of pattern letters is from 1 to 9. If it is less than 9, then the nano-of-second value is truncated, with only the most significant digits being output. When parsing in strict mode, the number of parsed digits must match the count of pattern letters. When parsing in lenient mode, the number of parsed digits must be at least the count of pattern letters, up to 9 digits.
分数:将纳秒字段输出为秒的分数。纳秒值有九位数字,因此模式字母的计数是从 1 到 9。如果它小于 9,那么纳秒值将被截断,只输出最重要的数字。在严格模式下解析时,解析数字的数量必须与模式字母的数量相匹配。在宽松模式下解析时,解析数字的个数必须至少是模式字母的个数,最多 9 位。
So we see that S stands for any fraction of second (including nanosecond), not just milliseconds. Furthermore, the fractional part does at the moment not take well in adjacent value parsing, unfortunately.
所以我们看到 S 代表任何几分之一秒(包括纳秒),而不仅仅是毫秒。此外,不幸的是,小数部分目前在相邻值解析中表现不佳。
EDIT:
编辑:
As background here some remarks about adjacent value parsing. As long as fields are separated by literals like a decimal point or time part separators (colon), the interpretation of fields in a text to be parsed is not difficult because the parser then knows easily when to stop i.e. when the field part is ended and when the next field starts. Therefore the JSR-310 parser can process the text sequence if you specify a decimal point.
作为背景,这里有一些关于相邻值解析的评论。只要字段由文字分隔,如小数点或时间部分分隔符(冒号),要解析的文本中的字段的解释并不困难,因为解析器很容易知道何时停止,即字段部分何时结束和当下一个字段开始时。因此,如果您指定小数点,JSR-310 解析器可以处理文本序列。
But if you have a sequence of adjacent digits spanning over multiple fields then some implementation difficulties arise. In order to let the parser know when a field stops in text it is necessary to instruct the parser in advance that a given field is represented by a fixed-width of digit chars. This works with all appendValue(...)
-methods which assume numerical representations.
但是如果你有一个跨越多个字段的相邻数字序列,那么就会出现一些实现困难。为了让解析器知道文本中的字段何时停止,有必要提前指示解析器给定字段由固定宽度的数字字符表示。这适用于所有appendValue(...)
假设数字表示的方法。
Unfortunately JSR-310 has not managed well to do this also with the fractional part (appendFraction(...)
). If you look for the keyword "adjacent" in the javadoc of class DateTimeFormatterBuilder
then you find that this feature is ONLY realized by appendValue(...)
-methods. Note that the spec for pattern letter S is slightly different but internally delegates to appendFraction()
-method. I assume we will at least have to waint until Java 9 (as reported in JDK-bug-log, or later???) until fraction parts can manage adjacent value parsing as well.
不幸的是,JSR-310 在小数部分 ( appendFraction(...)
) 中也没有很好地做到这一点。如果你在class的javadoc中DateTimeFormatterBuilder
查找关键字“adjacent”,就会发现这个特性只能通过appendValue(...)
-methods来实现。请注意,模式字母 S 的规范略有不同,但在内部委托给appendFraction()
-method。我认为我们至少要等到 Java 9(如 JDK-bug-log 中所报告的,或更高版本???),直到分数部分也可以管理相邻的值解析。
Update from 2015-11-25:
2015-11-25 更新:
The following code using two fraction digits only does not work and throws a DateTimeParseException
.
以下仅使用两个小数位的代码不起作用并抛出DateTimeParseException
.
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmssSS")
.appendValue(ChronoField.MILLI_OF_SECOND, 2)
.toFormatter();
String input = "2011120312345655";
LocalDateTime.parse(input, dtf); // abort
The workaround
解决方法
String input = "2011120312345655";
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSS");
Date d = sdf.parse(input);
System.out.println(d.toInstant()); // 2011-12-03T12:34:56.055Z
does not work because SimpleDateFormat
interpretes the fraction in a wrong way (see output, 55 ms instead of 550 ms).
不起作用,因为SimpleDateFormat
以错误的方式解释分数(参见输出,55 毫秒而不是 550 毫秒)。
What is left as solution is either waiting an undertermined long time until Java 9 (or later?) or writing your own hack or using 3rd-party libraries as solution.
剩下的解决方案是等待 Java 9(或更高版本?)或编写自己的 hack 或使用 3rd-party 库作为解决方案。
Solution based on a dirty hack:
基于脏 hack 的解决方案:
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
Solution using Joda-Time:
使用Joda-Time 的解决方案:
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
Solution using my library Time4J:
使用我的库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
Update from 2016-04-29:
2016-04-29 更新:
As people can see via the JDK-issue mentioned above, it is now marked as resolved - for Java 9.
正如人们通过上面提到的 JDK 问题所看到的,它现在被标记为已解决 - 对于Java 9。
回答by Mapsy
Here's an algorithm which adjusts the order of the trailing zeros that are conventionally returned from the formatted date String
.
这是一种算法,用于调整通常从格式化的 date 返回的尾随零的顺序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;
}
回答by Andrei Amarfii
DateTimeFormatterBuilder#appendFraction(ChronoField.MILLI_OF_SECOND, 0, 3, true)
Something like this helped me
像这样的东西帮助了我