java.time解析秒的分数失败了吗?
随着Mac OS X(Mavericks)上Java 8(b132)的首次发布,以下代码使用了新的works: 渲染:java.time解析秒的分数失败了吗?,java,java-time,Java,Java Time,随着Mac OS X(Mavericks)上Java 8(b132)的首次发布,以下代码使用了新的works: 渲染: 2011-12-03T12:34:56 但是,当我添加“SS”作为秒的分数(并将“55”作为输入),如中所述,将引发异常: java.time.format.DateTimeParseException:无法在索引0处分析文本“2011120312345655” 文档称默认情况下使用严格模式,并且需要与输入数字相同数量的格式字符。所以我不明白为什么这个代码会失败: Stri
2011-12-03T12:34:56
但是,当我添加“SS”作为秒的分数(并将“55”作为输入),如中所述,将引发异常:
java.time.format.DateTimeParseException:无法在索引0处分析文本“2011120312345655”
文档称默认情况下使用严格模式,并且需要与输入数字相同数量的格式字符。所以我不明白为什么这个代码会失败:
String input = "2011120312345655";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
使用文档中的示例(“978”)的另一个示例(失败):
这个例子很有效,添加了一个小数点(但我在文档中没有发现这样的要求): 呈现:
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 );
失败:
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 );
Bug–在Java9中修复
这一问题已在中报告。Stephen Colebourne提到以下解决方案:
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter();
注意:此解决方案不包括仅两个模式符号的用例。调整可能只使用其他字段,如微秒(6倍SSSS)或纳米秒(9倍SSSS)。有关两个小数位数,请参见下面的“我的更新”
@关于图案符号“S”的含义,请参见:
分数:以秒的分数输出秒场的纳米。
第二个值的nano有九个数字,因此模式的计数
字母从1到9。如果小于9,则纳米级为秒
值被截断,只有最有效的数字被截断
输出。在严格模式下解析时,必须指定解析的位数
匹配图案字母的计数。在宽松模式下解析时
解析的位数必须至少为模式字母的计数,
最多9位
我们看到S代表秒的任何分数(包括纳秒),而不仅仅是毫秒。此外,不幸的是,小数部分目前在邻接值解析中并不适用
编辑:
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
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
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
作为背景,这里有一些关于邻接值解析的注释。只要字段由文字(如小数点或时间部分分隔符(冒号))分隔,就不难解释要解析的文本中的字段,因为解析器很容易知道何时停止,即字段部分何时结束以及下一个字段何时开始。因此,如果指定小数点,JSR-310解析器可以处理文本序列
但是,如果您有一个跨越多个字段的相邻数字序列,则会出现一些实现困难。为了让解析器知道字段在文本中何时停止,有必要提前指示解析器给定字段由固定宽度的数字字符表示。这适用于所有采用数字表示的appendValue(…)
-方法
不幸的是,JSR-310并没有很好地处理小数部分(appendFraction(…)
)。如果在类DateTimeFormatterBuilder
的javadoc中查找关键字“nextant”,则会发现此功能仅通过appendValue(…)
-方法实现。请注意,模式字母S的规范略有不同,但内部委托给appendFraction()
-method。我假设我们至少要等到Java9(如JDKBug日志或更高版本中所报告的那样?),直到分数部分也可以管理相邻值解析
从2015年11月25日起更新:
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
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
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
以下仅使用两个小数位数的代码不起作用,并误解了毫秒部分:
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
不起作用,因为SimpleDataFormat
也以错误的方式解释分数,类似于现代示例(请参见输出,55毫秒而不是550毫秒)
剩下的解决方案要么是等待不确定的很长时间,直到Java9(或更高版本?),要么是编写自己的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
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
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
解决方案使用:
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
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
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
使用我的库的解决方案:
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
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
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
String input=“2011120312345655”;
计时器f=
TimeStampPattern的计时格式(“yyyymmddhhmmsss”,PatternType.CLDR,Locale.ROOT);
System.out.println(f.parse(input));//2011-12-03T12:34:56.550
从2016年4月29日起更新:
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
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
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
正如人们通过上面提到的JDK问题所看到的,它现在被标记为已解决-对于Java9这里有一个算法,它调整从格式化日期
字符串
返回的尾随零的顺序
/**
* 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;
}
/**
*获取日期并提供格式,同时补偿亚秒值的错误表示。
*即2017-04-03-22:46:19.000991->2017-04-03-22:46:19.991000
*@param pDate定义要格式化的日期对象。
*@param pPrecision定义系统响应中包含的有效亚秒字符数。
* */
私有静态最终字符串子格式(最终日期更新、最终SimpleDateFormat pSimpleDateFormat、最终整数精度)引发ParseException异常{
//格式和往常一样。
最终字符串lString=pSimpleDateFormat.format(pDate);
//计算字符数。
最后一个字符串lPattern=pSimpleDateFormat.toLocalizedPattern();
//找到亚秒的位置。
final int lStart=lPattern.indexOf('S');
final int lEnd=lPattern.lastIndexOf('S');
//确保它们符合预期格式。
对于(int i=l开始;i
类似这样的东西帮助我分数是纳秒。你能试着用9位数字吗?尽管从阅读文档来看,只要模式与数字匹配,它应该用少于9位数字就行了