Java SimpleDateFormat.format()在使用毫秒时返回不正确的日期

3

我想将日期字符串从"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");

输出的结果是(错误的日期值) :
29-SEP-2016
04-OCT-2016
29-SEP-2016
04-OCT-2016
06-OCT-2016
01-OCT-2016
03-OCT-2016
而如果使用这行代码:

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

则输出结果为(正确的日期值) :
 
23-SEP-2016
28-SEP-2016
23-SEP-2016
26-SEP-2016
26-SEP-2016
26-SEP-2016
26-SEP-2016
我想知道为什么将"S(毫秒)"添加到格式字符串中会导致出现不正确的值。

编辑1:

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

编辑2:

我正在Android应用程序中使用此代码。它在模拟器(API 23)上完美地工作。对于某些设备,它显示不正确的日期。这是否与Java版本有关?

5个回答

4

简介:

在Java中,时间不是以秒为单位,而是毫秒。毫秒不能超过999。

在Oracle中更改日期格式以包含 FF3来获取毫秒级的分数。

解释

在Java中,你的日期格式中的 S 或者更准确地说是 SSS 代表毫秒,即一秒的千分之一。一秒钟只有1000毫秒。

Oracle中,日期格式没有指定毫秒,但是指定了秒的分数。

FF [1..9]
分数秒; 小数点不会被打印出来。使用X格式元素添加小数点。使用数字1到9表示FF后小数部分的数字位数。如果您没有指定数字,则Oracle数据库将使用datetime数据类型或数据类型默认精度指定的精度。在时间戳和间隔格式中有效,但在DATE格式中无效。

在Java中,如果你需要三分之一秒的精度,最多只能得到333毫秒的精度。但是,在Oracle中,可以将其表示为333333微秒,甚至可能是333333333纳秒。

Oracle允许您指定所需的小数位数,但如果您没有指定,将使用类型的精度。在您的情况下,这似乎是9。
然后,Java中的日期格式将其解释为毫秒的数量。成百万或十亿毫秒。这些添加到您的日期的余下部分。由于一天中只有86400000毫秒,因此超过该数字将导致另一天添加到您的日期。

示例

让我们看看您的第一个测试用例:2016-09-23T09:14:52.555000000
555000000毫秒= 555000秒≈ 154小时≈ 6天10小时。
将6天10小时添加到您的日期的其余部分,即2016-09-23 09:14:52,应该会得到大约2016-09-29 19:00左右。更改输出格式(dateFormatToDisplay),以包括小时,您就会看到正在发生的事情。

修复

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

你的意思是我可以截取毫秒数,只保留前三位数字吗? - Monish Kamble
实际上,我是从服务器数据库中获取这个数据的,在我的情况下它是Oracle,它以这种格式存储日期。 - Monish Kamble
值得添加到你的 FIX - 如果你得到了可以用不到3位小数表示的秒的小数部分,请确保在右侧用 0 填充,直到你得到3个小数位。例如解析 00:00:00.1 不会导致 "午夜后十分之一秒",而只是 "午夜后一毫秒(即以秒为单位的十进制表示 00:00:00.001)"。 - Adrian Colomitchi
@SQB:非常感谢。您解决了我的疑惑。讲解得很清楚。 - Monish Kamble

4
谜团在于正确解释 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));

将会导致...等待它...嘭-嘶...

"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 百万毫秒 或者 555000 秒。这意味着相对于基准时间额外增加了6天10小时11分钟。

1
似乎当您添加“S”时,解析器会将所有毫秒(例如“555000000”)加入日期。 当您进行计算(555000000毫秒约为6.4天)时,它与结果相匹配(日期不正确)。您应该使用“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));

我已经达到了我想要的格式,我只是想知道这种行为背后的原因。 - Monish Kamble

0

毫秒应该只用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"));

-1
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接