SimpleDateFormat无法解析超过4位数的毫秒数
我想解析一个时间戳,像这样 – "2016-03-16 01:14:21.6739"
。 但是当我使用SimpleDateFormat
来解析它时,我发现它输出了一个不正确的解析值。 它将隐藏6739毫秒到6秒,剩下739毫秒。 它将日期转换为此格式 – Wed Mar 16 01:14:27 PDT 2016
。 为什么秒部分从21秒变为27秒(增加6秒?)。 以下是我的代码片段:
final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS"); String parsedate="2016-03-16 01:14:21.6739"; try { Date outputdate = sf.parse(parsedate); String newdate = outputdate.toString(); //==output date is: Wed Mar 16 01:14:27 PDT 2016 System.out.println(newdate); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); }
似乎不可能使用SimpleDateFormat来表示比毫秒更精细的粒度。 发生的事情是,当你输入6739时,Java将其理解为6739毫秒,即6秒和739毫秒,因此观察到6秒的差异。
检查这些,它得到了很好的解释: String-Date转换为纳秒 Java日期解析,具有微秒或纳秒精度
SimpleDateFormat
SS
是毫秒。 你有6739毫秒,这意味着你的时间增加了6.7秒。 也许您可以截断6739
到673
(或者如果您愿意,将其四舍五入到674
),这样就可以正确解析为毫秒。
TL;博士
LocalDateTime.parse( "2016-03-16 01:14:21.6739".replace( " " , "T" ) // Comply with ISO 8601 standard format. )
毫秒与微秒
正如其他人所说, java.util.Date
具有毫秒级的分辨率。 这意味着最多3位小数秒。
输入字符串中有4位数,一位数太多。 您的输入值需要更精细的分辨率,例如微秒或纳秒。
java.time
不要使用有缺陷,令人困惑和麻烦的java.util.Date/.Calendar类,而是继续使用它们:Java 8及更高版本中内置的java.time框架。
java.time类的分辨率为纳秒级 ,最高可达一位小数的九位数。 例如:
2016-03-17T05:19:24.123456789Z
ISO 8601
在解析/生成日期时间值的文本表示时,您的字符串输入几乎是标准的ISO 8601格式,在java.time中默认使用。 用T
替换中间的空间以符合ISO 8601。
String input = "2016-03-16 01:14:21.6739".replace( " " , "T" );
不分区
LocalDateTime
是日期时间的近似值,没有任何时区上下文。 时间轴上没有片刻。
LocalDateTime ldt = LocalDateTime.parse( input );
世界标准时间
通过应用预期的时区,使LocalDateTime
成为时间轴上的实际时刻。 如果用于UTC,请Instant
。
Instant instant = ldt.toInstant( ZoneOffset.UTC );
划
如果用于特定时区,请指定ZoneId
以获取ZoneDateTime
。
ZoneId zoneId = ZoneId.of( "America/Montreal" ); ZonedDateTime zdt = ldt.atZone( zoneId );
你可以使用String newdate = sf.format(outputdate); String newdate = outputdate.toString();
有点偏离主题但是SimpleDateFormat
类不是线程安全的 – 不仅在解析上有些可理解,而且在格式化方面。 网上有很多关于此的信息,这里有一个例子: http : //javarevisited.blogspot.co.il/2012/03/simpledateformat-in-java-is-not-thread.html 。 这个问题不会得到解决。 在Java 8中,有一个全新的java.time
包, java.time
具有很棒的新function,可以处理完整或部分日期和时间。 还有一个新类DateTimeFormatter ,它提供了大大改进的格式和解析function。 但是,如果你使用旧版本的Java然后使用Java 8,那么建议使用Joda时间库或Apache FastDateFormat
如果必须将字符串作为最终输出,为什么不使用format
而不是parse
final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"); sf.setTimeZone(TimeZone.getTimeZone("UTC")); Date curDate = new Date(); String outputdate = sf.format(curDate); // 2016-03-17 09:45:28.658+0000 System.out.println(outputdate); Date strToDate = new Date(); try { strToDate = sf.parse(outputdate); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } //Thu Mar 17 17:11:30 MYT 2016 System.out.println(strToDate);
而不是"yyyy-MM-dd HH:mm:ss.SSSS"
使用"yyyy-MM-dd HH:mm:ss.SSSZ"
在这里查看https://docs.oracle.com/javase/7/docs /api/java/text/SimpleDateFormat.html
"yyyy-MM-dd'T'HH:mm:ss.SSSZ" 2001-07-04T12:08:56.235-0700
正如上面的评论中提到的,你的问题已经包含了答案:毫秒不得超过3位,否则它至少代表一整秒。
你的代码工作正是由于java.text.SimpleDateFormat
(一个宽松的 (见这里 )选项)的一个非常值得怀疑的特性。 默认情况下, SimpleDateFormat
将setLenient
设置为true
这意味着解析器将尝试解释与100%模式不匹配的字符串,并通过一些启发式方法将它们转换为日期对象。 例如,它将接受日期31.04.2016
并将其转换为01.05.2016
。 在某些情况下,此function可能很好,但在大多数情况下会产生可疑的结果。
如果在代码中将lenient设置为false
,则不再解析日期字符串。 使模式中的错误更明显:
final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS"); sf.setLenient(false); String parsedate="2016-03-16 01:14:21.6739"; ...
由于java.util.Date
不能表示低于毫秒的任何精度,我认为解析日期的最佳选择是简单地删除输入日期的最后数字,如果点后面的部分有更多超过四位数。 您的代码可能如下所示:
final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); sf.setLenient(false); String parsedate="2016-03-16 01:14:21.6739"; try { // 23 is the length of the date pattern if (parsedate.length() > 23) { parsedate = parsedate.substring(0, 23); } Date outputdate = sf.parse(parsedate); String newdate = sf.format(outputdate); //==output date is: 2016-03-16 01:14:21.673 System.out.println(newdate); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); }
您可以尝试添加一些舍入逻辑,以便不会丢失第4位的所有信息……