【问题标题】:SimpleDateFormat is not parsing the milliseconds correctlySimpleDateFormat 未正确解析毫秒
【发布时间】:2011-11-01 20:37:52
【问题描述】:

背景:

在我的数据库表中,我有两个时间戳

timeStamp1 = 2011-08-23 14:57:26.662
timeStamp2 = 2011-08-23 14:57:26.9

当我执行“ORDER BY TIMESTAMP ASC”时,timeStamp2 被认为是更大的时间戳(这是正确的)。

要求:我需要得到这些时间戳的差异(timeStamp2 - timeStamp1)

我的实现:

public static String timeDifference(String now, String prev) {
    try {
        final Date currentParsed = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(now);
        final Date previousParsed = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(prev);
        long difference = currentParsed.getTime() - previousParsed.getTime();
        return "" + difference;
    } catch (ParseException e) {
        return "Unknown";
    }
}

答案应该是 238 毫秒,但返回的值是 -653 毫秒。 我不确定我做错了什么。有什么建议吗?

【问题讨论】:

  • The answer should have been 232ms,真的 - 我得到 900-662 = 238。
  • 如果它们作为时间戳存储在数据库中,为什么要将它们作为字符串转换为日期?只需将它们作为 Timestamp 实例,您将拥有两个可比较的日期,而无需解析任何字符串。见download.oracle.com/javase/6/docs/api/java/sql/…
  • Thomas- 是的,抱歉,这是一个错字。 238ms

标签: java date simpledateformat


【解决方案1】:

我建议使用Joda-Time。它可以正确处理这些情况。在以下示例中,毫秒被正确解析为 200 毫秒。

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class ParseMillis {

  public static void main(String[] args) {
    String s = "00:00:01.2";
    DateTimeFormatter format = DateTimeFormat.forPattern("HH:mm:ss.S");
    DateTime dateTime = format.parseDateTime(s);
    System.out.println(dateTime.getMillisOfSecond());
  }
}

【讨论】:

    【解决方案2】:

    我的日志文件中的 6 位数毫秒的时间太准确了,我也遇到了同样的问题。解析时间最多相差16分钟!什么鬼?

    16-JAN-12 04.00.00.999999 PM GMT --> 16 Jan 2012 04:16:39 GMT
    

    更改位数减少了错误的差异,并且由于这个线程我可以识别问题:

    16-JAN-12 04.00.00.99999 PM GMT --> 16 Jan 2012 04:01:39 GMT
    16-JAN-12 04.00.00.9999 PM GMT --> 16 Jan 2012 04:00:09 GMT
    16-JAN-12 04.00.00.999 PM GMT --> 16 Jan 2012 04:00:00 GMT
    

    由于SimpleDateFormat 在内部仅处理 3 位数字,我用一个小的正则表达式删除了不必要的(忽略舍入错误,最多为 n 位数字):

    str = str.replaceAll("(\\.[0-9]{3})[0-9]*( [AP]M)", "$1$2");
    

    感谢@Peter Lawrey 的回答,防止我发疯 :-)

    【讨论】:

      【解决方案3】:

      您正在解析的格式与使用的格式不匹配。您期望一个三位数的字段并且只提供一位数字。它需要9 并假设您的意思是009,而您想要的是900。日期格式很复杂,当您以不同格式证明日期时,它可能会以不同的方式解析它们。

      文档说S 表示毫秒数,该字段中的数字是 9,因此它的行为正确。


      编辑:这个例子可能会有所帮助

      final SimpleDateFormat ss_SSS = new SimpleDateFormat("ss.SSS");
      ss_SSS.setTimeZone(TimeZone.getTimeZone("GMT"));
      for (String text : "0.9, 0.456, 0.123456".split(", ")) {
        System.out.println(text + " parsed as \"ss.SSS\" is "
            + ss_SSS.parse(text).getTime() + " millis");
      }
      

      打印

      0.9 parsed as "ss.SSS" is 9 millis
      0.456 parsed as "ss.SSS" is 456 millis
      0.123456 parsed as "ss.SSS" is 123456 millis
      

      【讨论】:

      • 那么,我应该如何处理这种情况?截至目前,我正在手动添加尾随零。这真的是正确的方法吗?
      • 您必须以预期格式提供日期字符串。如果您更改了日、月或年的顺序,您不会期望它只是“知道”您的意思。添加零可能是最简单的解决方案。
      • 我认为当我将数据输入数据库时​​会出现问题。进入数据库的值实际上是 2011-08-23 14:57:26.900。我正在使用 H2 db,它正在删除尾随零。所以,当我从数据库中提取数据时,我得到 2011-08-23 14:57:26.9
      • 您能否通过 JDBC 而不是使用字符串从数据库中提取日期作为时间戳?这将避免转换/解析日期/时间的需要。
      • @dkulkarni 你放Date 并获取String 吗?如果没有,您可能让数据库列具有相应的数据类型,即 timestamp 代表 Date(然后检索 Date)或 (var)char 并始终使用 String
      【解决方案4】:

      我不完全确定,但 JavaDoc 指出:

      在解析时,模式字母的数量会被忽略,除非需要分隔两个相邻的字段。

      这表示来自2011-08-23 14:57:26.9 的毫秒数将被解析为9 而不是900。添加尾随零可能有效:2011-08-23 14:57:26.900

      【讨论】:

      • 我正在添加零,它工作正常。但是,这是正确的处理方式吗?
      • 在内部调用NumberFormat.parse("9"),因此除了添加尾随零之外别无他法。
      • @dkulkarni - 正确的方法是按照已经评论的方式:不要将时间戳作为字符串处理。将它们作为 java.sql.Timestamp 读取和处理,最终转换为 java.util.Date!
      • 我想我现在会添加零。谢谢托马斯
      • @RobertCasto 该值确实很重要,但“26.9”被解析为 26.009 已经不正确,因为它不是预期的结果。
      猜你喜欢
      • 1970-01-01
      • 2018-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多