java SimpleDateFormat 无法解析超过 4 位的毫秒数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36055717/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 00:55:38  来源:igfitidea点击:

SimpleDateFormat cannot parse milliseconds with more than 4 digits

javasimpledateformat

提问by Alter Hu

I want to parse a timestamp, like this - "2016-03-16 01:14:21.6739". But when I use the SimpleDateFormatto parse it, I find that it outputs an incorrect parsed value. It will covert 6739 milliseconds to 6 seconds with 739 millseconds left. It converted the date to this format - Wed Mar 16 01:14:27 PDT 2016. Why the seconds part has changed from 21 seconds to 27 seconds(an addition of 6 seconds?). The following is my code snippet:

我想解析一个时间戳,就像这样 - "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();
}

采纳答案by Bruno Piqueras

It seems that is not possible to use SimpleDateFormat to express times with a finer grain than the millisecond. What is happening is that as you put 6739, Java understands it as 6739 milliseconds i.e. 6 seconds and 739 milliseconds hence the 6 seconds difference observed.

似乎不可能使用 SimpleDateFormat 来表达比毫秒更精细的时间。发生的情况是,当您输入 6739 时,Java 将其理解为 6739 毫秒,即 6 秒和 739 毫秒,因此观察到 6 秒的差异。

Check these ones, it is explained quite well: String-Date conversion with nanoseconds Java date parsing with microsecond or nanosecond accuracy

检查这些,它解释得很好: String-Date conversion with nanoseconds Java date parsing with microsecond or nanosecond accuracy

回答by khelwood

SSin SimpleDateFormatis milliseconds. You have 6739 milliseconds, which means you are adding an extra 6.7 seconds onto your time. Perhaps you can truncate the 6739to 673(or if you prefer, round it to 674) so it can be parsed correctly as milliseconds.

SSinSimpleDateFormat是毫秒。您有 6739 毫秒,这意味着您的时间增加了 6.7 秒。也许您可以将6739to截断673(或者,如果您愿意,674可以将其四舍五入为),以便将其正确解析为毫秒。

回答by Basil Bourque

tl;dr

tl;博士

LocalDateTime.parse(
    "2016-03-16 01:14:21.6739".replace( " " , "T" )  // Comply with ISO 8601 standard format.
)

Milliseconds versus Microseconds

毫秒与微秒

As others noted, java.util.Datehas millisecond resolution. That means up to 3 digits of a decimal fraction of second.

正如其他人指出的那样,java.util.Date具有毫秒分辨率。这意味着最多 3 位秒的小数部分。

You have 4 digits in your input string, one too many. Your input value demands finer resolution such as microseconds or nanoseconds.

您的输入字符串中有 4 位数字,一位太多了。您的输入值需要更精细的分辨率,例如微秒或纳秒。

java.time

时间

Instead of using the flawed, confusing, and troublesome java.util.Date/.Calendar classes, move on to their replacement: the java.timeframework built into Java 8 and later.

与其使用有缺陷、令人困惑和麻烦的 java.util.Date/.Calendar 类,不如继续使用它们的替代品:Java 8 及更高版本中内置的java.time框架。

The java.time classes have a resolution of nanosecond, up to ninedigits of decimal fraction of a second. For example:

该java.time类有一个分辨率纳秒,高达9秒的小数位数。例如:

2016-03-17T05:19:24.123456789Z

2016-03-17T05:19:24.123456789Z

ISO 8601

ISO 8601

Your string input is almost in standard ISO 8601format used by default in java.time when parsing/generating textual representations of date-time values. Replace that space in the middle with a Tto comply with ISO 8601.

在解析/生成日期时间值的文本表示时,您的字符串输入几乎是java.time 中默认使用的标准ISO 8601格式。用 a 替换中间的空格T以符合 ISO 8601。

String input = "2016-03-16 01:14:21.6739".replace( " " , "T" );

Unzoned

未分区

A LocalDateTimeis an approximation of a date-time, without any time zone context. Nota moment on the timeline.

ALocalDateTime是日期时间的近似值,没有任何时区上下文。时间轴上没有片刻。

LocalDateTime ldt = LocalDateTime.parse( input );

UTC

世界标准时间

Make that LocalDateTimean actual moment on the timeline by applying the intended time zone. If meant for UTC, make an Instant.

作出这样的LocalDateTime应用预期的时区的时间轴上的实际的时刻。如果适用于 UTC,请制作一个Instant.

Instant instant = ldt.toInstant( ZoneOffset.UTC );

Zoned

分区

If meant for a particular time zone, specify a ZoneIdto get a ZoneDateTime.

如果适用于特定时区,请指定 aZoneId以获取ZoneDateTime.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( zoneId );

回答by Raunak Kathuria

If you have to get string as final output why not use formatinstead of parse

如果您必须将字符串作为最终输出,为什么不使用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);

and instead of "yyyy-MM-dd HH:mm:ss.SSSS"use "yyyy-MM-dd HH:mm:ss.SSSZ"check it here https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

而不是在这里"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

回答by Michael Gantman

A little off-topic but SimpleDateFormatclass is not thread safe - not only on parsing which is somewhat understandable but also on formatting. there are a lot of info on this on the net, here is one example: http://javarevisited.blogspot.co.il/2012/03/simpledateformat-in-java-is-not-thread.html. This problem will not be fixed. In Java 8 there is a whole new package java.timewith wonderful new features allowing to work with full or partial dates and times. Also there is a new class DateTimeFormatterthat provides vastly improved formatting and parsing features. However, if you use java older then java 8 then the recommendation would be to use Joda time libraryor Apache FastDateFormat

有点离题,但SimpleDateFormat类不是线程安全的 - 不仅在解析上是可以理解的,而且在格式上也是如此。网上有很多关于这方面的信息,这里是一个例子:http: //javarevisited.blogspot.co.il/2012/03/simpledateformat-in-java-is-not-thread.html。这个问题不会被修复。在 Java 8 中,有一个全新的包,java.time具有出色的新功能,允许使用完整或部分日期和时间。还有一个新类DateTimeFormatter,它提供了极大改进的格式设置和解析功能。但是,如果您使用较旧的 java 8 然后建议使用Joda 时间库或 Apache FastDateFormat

回答by dpr

As mentioned in the comments above your question already contains the answer: milliseconds must not have more than 3 digits otherwise it represents at least one full second already.

正如上面的评论中提到的,您的问题已经包含答案:毫秒不能超过 3 位数字,否则它至少代表一整秒。

That your code is working is simply due to a very questionable feature of java.text.SimpleDateFormat, the lenient(see here) option. By default a SimpleDateFormathas setLenientset to truewich means that the parser will try to interpret strings that do not match the pattern 100% and will convert them to date objects by means of some heuristics. For example it will accept a date 31.04.2016and convert it to 01.05.2016. This feature may be nice in some situations but produces questionable results in most of the cases.

您的代码工作的根本是由于一个非常可疑的功能java.text.SimpleDateFormat,在宽松(见这里)选项。默认情况下,一个SimpleDateFormatsetLenient设置为true至极意味着分析器将试图解释不匹配模式的100%,并通过一些启发式的手段,将其转换为Date对象的字符串。例如,它将接受一个日期31.04.2016并将其转换为01.05.2016. 此功能在某些情况下可能很好,但在大多数情况下会产生有问题的结果。

If you set lenient to falsein your code, the date string won't be parsed anymore. Making the error in the pattern more obvious:

如果您false在代码中设置 lenient ,则不会再解析日期字符串。使模式中的错误更加明显:

final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
sf.setLenient(false);
String parsedate="2016-03-16 01:14:21.6739";
...

As java.util.Dateis not able to represent any precision lower than milliseconds, I think the best option for you to parse your date would be to simply strip of the last digits of your input date, if the part after the dot has more than four digits. Your code might look something like this:

由于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();
}

You could try to add some rounding logic as well in order to not loose all the information of the 4th digit...

您也可以尝试添加一些舍入逻辑,以免丢失第 4 位数字的所有信息...

回答by visingh

You can use String newdate = sf.format(outputdate);in place of String newdate = outputdate.toString();

您可以使用String newdate = sf.format(outputdate); 代替String newdate = outputdate.toString();