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

时间:2016-03-17 08:58:13

标签: java simpledateformat

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

7 个答案:

答案 0 :(得分:5)

SimpleDateFormat中的

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

答案 1 :(得分:4)

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

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

答案 2 :(得分:3)

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类的分辨率为nanosecond ,最多为 9 位小数的秒数。例如:

  

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 );

UTC

通过应用预期的时区,使LocalDateTime成为时间轴上的实际时刻。如果用于UTC,请制作Instant

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

分区

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

有点偏离主题,但SimpleDateFormat类不是线程安全的 - 不仅在解析上有些可理解,而且在格式化方面。网上有很多关于此的信息,这里有一个例子:http://javarevisited.blogspot.co.il/2012/03/simpledateformat-in-java-is-not-thread.html。这个问题不会得到解决。在Java 8中,有一个全新的包java.time具有很棒的新功能,可以处理完整或部分日期和时间。还有一个新类DateTimeFormatter,它提供了大大改进的格式和解析功能。但是,如果您使用Java旧版本然后使用java 8,那么建议使用Joda time library或Apache FastDateFormat

答案 6 :(得分:0)

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

您的代码正常运行只是由于java.text.SimpleDateFormat宽松(请参阅here)选项的一个非常可疑的功能。默认情况下,SimpleDateFormatsetLenient设置为true,这意味着解析器将尝试解释与100%模式不匹配的字符串,并通过某些方式将它们转换为日期对象启发式。例如,它会接受日期31.04.2016并将其转换为01.05.2016。在某些情况下,此功能可能会很好,但在大多数情况下会产生可疑的结果。

如果在代码中将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无法表示任何低于毫秒的精度,我认为解析日期的最佳选择是简单地删除输入日期的最后一位数字,如果后面的部分是dot有四位数以上。您的代码可能如下所示:

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位的所有信息......