随着Mac OS X(Mavericks)上的第一个Java 8(b132)发布,使用新的java.time包的代码可以工作:
String input = "20111203123456";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Run Code Online (Sandbox Code Playgroud)
渲染:
2011-12-03T12:34:56
Run Code Online (Sandbox Code Playgroud)
但是当我按照DateTimeFormatter类doc中的指定添加"SS"作为秒的小数(和"55"作为输入)时,会抛出异常:
java.time.format.DateTimeParseException: Text '2011120312345655' could not be parsed at index 0
Run Code Online (Sandbox Code Playgroud)
该文档说默认使用严格模式,并且需要与输入数字相同数量的格式字符.所以我很困惑为什么这段代码失败了:
String input = "2011120312345655";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Run Code Online (Sandbox Code Playgroud)
使用文档中的示例("978")的另一个示例(失败):
String input = "20111203123456978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Run Code Online (Sandbox Code Playgroud)
这个例子工作,添加一个小数点(但我发现在doc中没有这样的要求):
String input = "20111203123456.978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Run Code Online (Sandbox Code Playgroud)
呈现:
localDateTime: 2011-12-03T12:34:56.978
Run Code Online (Sandbox Code Playgroud)
从输入字符串或格式中省略句点字符会导致失败.
失败:
String input = "20111203123456.978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Run Code Online (Sandbox Code Playgroud)
失败:
String input = "20111203123456978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
Run Code Online (Sandbox Code Playgroud)
Men*_*ild 33
JDK-bug-log中已经报告了此问题.Stephen Colebourne提到了解决方案:
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter();
Run Code Online (Sandbox Code Playgroud)
注意:此解决方法不包括仅使用两个模式符号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查找关键字"adjacent",则会发现此功能仅由appendValue(...)-methods 实现.请注意,模式字母S的规范略有不同,但内部委托给appendFraction()-method.我假设我们至少要在Java 9之前(如JDK-bug-log中报告的那样,或者稍后???)直到分数部分也可以管理相邻的值解析.
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
Run Code Online (Sandbox Code Playgroud)
解决方法
String input = "2011120312345655";
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSS");
Date d = sdf.parse(input);
System.out.println(d.toInstant()); // 2011-12-03T12:34:56.055Z
Run Code Online (Sandbox Code Playgroud)
不起作用,因为SimpleDateFormat以错误的方式解释分数(见输出,55毫秒而不是550毫秒).
剩下的解决方案是要么长时间等待Java 9(或更高版本?),要么编写自己的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
Run Code Online (Sandbox Code Playgroud)
使用Joda-Time的解决方案:
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
Run Code Online (Sandbox Code Playgroud)
使用我的库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
Run Code Online (Sandbox Code Playgroud)
2016-04-29更新:
正如人们可以通过上面提到的JDK问题看到的那样,它现在被标记为已解决 - 对于Java 9.