【问题标题】:Java SimpleDateFormat.format() returns incorrect date when using milisecondsJava SimpleDateFormat.format() 在使用毫秒时返回不正确的日期
【发布时间】:2016-09-29 07:34:08
【问题描述】:

我想将日期字符串从 "2016-09-23T09:14:52.555000000" 格式解析为 "23-SEP-2016" 格式。

这是我的代码:

public class Tester {

    public static void main(String[] args) {
        System.out.println(displayDate("2016-09-23T09:14:52.555000000"));
        System.out.println(displayDate("2016-09-28T11:56:24.552000000"));
        System.out.println(displayDate("2016-09-23T09:29:12.507000000"));
        System.out.println(displayDate("2016-09-26T14:55:02.702000000"));
        System.out.println(displayDate("2016-09-26T09:50:24.880000000"));
        System.out.println(displayDate("2016-09-26T15:20:49.397000000"));
        System.out.println(displayDate("2016-09-26T15:21:21.559000000"));
    }

    public static String displayDate(String dateString) {
        String formattedDateString = "NA";

        if(dateString == null) {
            return formattedDateString;
        }

        SimpleDateFormat oracleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'kk:mm:ss.S");
        SimpleDateFormat dateFormatToDisplay = new SimpleDateFormat("dd-MMM-yyyy");

        try {
            Date date = oracleDateFormat.parse(dateString);
            formattedDateString = dateFormatToDisplay.format(date).toUpperCase();
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return formattedDateString;
    }

}

问题是如果我使用这条线

SimpleDateFormat oracleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'kk:mm:ss.S");

输出是(日期值不正确):

2016 年 9 月 29 日 2016 年 10 月 4 日 2016 年 9 月 29 日 2016 年 10 月 4 日 2016 年 10 月 6 日 2016 年 10 月 1 日 2016 年 10 月 3 日

而如果使用这一行

SimpleDateFormat oracleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'kk:mm:ss");

输出是(正确的日期值

2016 年 9 月 23 日 2016 年 9 月 28 日 2016 年 9 月 23 日 2016 年 9 月 26 日 2016 年 9 月 26 日 2016 年 9 月 26 日 2016 年 9 月 26 日

我想知道为什么在格式字符串中添加 "S (Millisecond)" 会导致值不正确。

编辑1:

即使是 "yyyy-MM-dd'T'kk:mm:ss.SSS" 也会返回不正确的值。

编辑2:

我在 Android 应用程序中使用此代码。它在模拟器(API 23)上完美运行。对于某些设备,它显示的日期不正确。可以和Java版本有关吗?

【问题讨论】:

    标签: java android date datetime


    【解决方案1】:

    谜团在于对 S 说明符的正确解释。

    SimpleDateFormat日期和时间模式 部分,S 的定义指出:

    S毫秒

    在某些方面你觉得它很奇怪吗?不?!这也让我自己感到意外,所以让我用负面的方式来形容吧:

    不是秒的小数部分,而是毫秒

    还是不明白?我的意思是字面意思毫秒数。

    喜欢:

      SimpleDateFormat oracleDateFormat = 
        new SimpleDateFormat("yyyy-MM-dd'T'kk:mm:ss.S");
      SimpleDateFormat dateFormatToDisplay = 
        new SimpleDateFormat("dd-MMM-yyyy--HH:mm:ss.SSS");
    
      Date d=oracleDateFormat.parse("2016-09-23T09:14:52.1");
      System.out.println(dateFormatToDisplay.format(d));
    

    将导致...等待它...繁荣-tshhh...

    “2016-09-23--09:14:52.001

    没错,伙计们! “一毫秒”而不是“十分之一秒”。

    那么,如果我们增加点后的位数,是否会发生同样的情况?当然可以。

      d=oracleDateFormat.parse("2016-09-23T09:14:52.1000");
      System.out.println(dateFormatToDisplay.format(d));
    

    “2016-09-23--09:14:53.000

    因此,如果您输入T09:14:53.555000000,您只需将555 millions of milliseconds 添加到您的基本时间或555000 整秒。这意味着比您的基本时间多出 6 天 10 小时 11 分钟。

    【讨论】:

      【解决方案2】:

      TL;DR:

      在 Java 中,它不是几分之一秒,而是几毫秒。不要让它大于 999。

      在 Oracle 中更改您的日期格式,以将 FF3 包含在几分之一秒中以产生毫秒。

      说明

      在 Java 中,S,或者更确切地说是SSS,在您的日期格式中代表毫秒,即数千秒。一秒只有一千毫秒。

      Oracle 中,日期格式不指定毫秒,而是指定秒的小数部分。

      FF [1..9]
      小数秒;不打印基数字符。使用 X 格式元素添加基数字符。在 FF 之后使用数字 1 到 9 来指定返回的日期时间值的小数秒部分中的位数。如果不指定数字,则 Oracle 数据库使用为 datetime 数据类型指定的精度或数据类型的默认精度。在时间戳和间隔格式中有效,但在 DATE 格式中无效。

      在 Java 中,如果您需要 1/3 秒,那么您无法得到比 333 毫秒更精确的时间。然而,在 Oracle 中,您可以将其表示为 333333 微秒,甚至可能是 333333333 纳秒。

      Oracle 允许您指定所需的小数位数,但如果您不这样做,您将获得类型所允许的精度。在您的情况下,这似乎是 9。
      然后您在 Java 中的日期格式将其解释为毫秒数。数以百万计的他们。这些是添加到你约会的其余部分。由于一天只有 86,400,000 毫秒,因此任何超过此时间的时间都会为您的日期添加另一天。

      示例

      让我们看一下您的第一个测试用例,2016-09-23T09:14:52.555000000
      555000000 毫秒 = 555000 秒 & 约;约 154 小时6天10小时。
      在剩下的日期(即 2016 年 9 月 23 日 09:14:52)上加上 6 天 10 小时,应该会让你到 2016 年 9 月 29 日 19:00 左右。更改您的输出格式 (dateFormatToDisplay) 以包含小时数,您就会看到发生了什么。

      修复

      您的 Java 日期格式要求毫秒数不超过 3 位。在 Oracle 中指定小数位数。 FF 使用该类型可用的最大精度,FF3 仅输出 3 个小数位 — 毫秒。
      如果您无法更改 Oracle 中使用的日期格式,请在 Java 中将其缩减为三位十进制数字。请注意,任何小于的数字都应该用零填充到三位数的长度; 34.12 被解释为 34 秒和 12 毫秒,而您可能正在寻找 120 毫秒。

      【讨论】:

      • 你的意思是我可以修剪毫秒,只保留前 3 位数字?
      • 实际上我是从服务器数据库中获取的,在我的例子中是 Oracle,它以这种格式存储日期。
      • 值得添加到您 FIX - 如果您得到的秒数的小数部分可以用小数点后 3 位来表示,请确保使用 @987654330 将它们填充到右侧@ 直到你得到 3 位小数。例如解析00:00:00.1 不会导致“午夜后十分之一秒”,而只会导致“午夜后一毫秒(即以十进制表示的秒数00:00:00.001
      • @SQB:非常感谢。你消除了我的疑惑。解释得很好。
      【解决方案3】:

      似乎当您添加“S”时,解析器会添加所有毫秒,例如“555000000”到日期。当您进行计算时(555000000 ms ~ 6,4d),它与结果匹配(日期不正确)。

      您应该使用“yyyy-MM-dd HH:mm:ss.SSS”,但还要将“2016-09-23T09:14:52.555000000”修剪为“2016-09-23T09:14:52.555”,例如:

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
      sdf.parse("2016-09-23T09:14:52.555000000".substring(0,23));
      

      【讨论】:

      • 我已经实现了我想要的格式。我只是想知道这种行为背后的原因。
      【解决方案4】:

      毫秒只能用 3 位数字表示。将代码更改为:

          System.out.println(displayDate("2016-09-23T09:14:52.555"));
          System.out.println(displayDate("2016-09-28T11:56:24.552"));
          System.out.println(displayDate("2016-09-23T09:29:12.507"));
          System.out.println(displayDate("2016-09-26T14:55:02.702"));
          System.out.println(displayDate("2016-09-26T09:50:24.880"));
          System.out.println(displayDate("2016-09-26T15:20:49.397"));
          System.out.println(displayDate("2016-09-26T15:21:21.559"));
      

      【讨论】:

        【解决方案5】:
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-10-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多