Java中如何解析微秒级时间

6

我在使用Java解析时间字符串时遇到了问题,它们的格式为2013-01-09 09:15:03.000000。在我的数据中,最后三位数字始终为0(表示输入字符串仅有毫秒精度),因此我将此格式传递给SimpleDateFormat:

formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS'000'");

但是 formatter.parse(“2013-01-09 09:15:02.500000”) 抛出了异常:

Unparseable date: "2013-01-09 09:15:02.500000"
    at java.text.DateFormat.parse(DateFormat.java:357)

有人知道如何正确处理吗?我可以通过使用格式yyyy-MM-dd HH:mm:ss.SSS和使用substring来除去最后三位数字,但那太粗糙了。

编辑:有人能解释一下为什么格式字符串yyyy-MM-dd HH:mm:ss.SSS'000'不能用于解析时间"2013-01-09 09:15:02.500000"吗?


1
我能看到的最大问题是解析器不关心数字出现的位数(就这么说),所以你可以输入.S,它会解析成5500500000,但这会引入时间值的滚动来进行补偿... - MadProgrammer
1
@MadProgrammer,这是否意味着如果我传入一个字符串“2013-01-09 09:15:02.5”,它会将其解析为5毫秒而不是500毫秒?这真的很愚蠢。 - Kan Li
也许是这样,但它的灵活性使得你也可以传递2013-1-9 9:15:2.500并且它也能正常工作。解析器的工作方式和格式化程序的工作方式略有不同... - MadProgrammer
3个回答

3

java.time

我很乐意提供现代化的答案。使用 java.time,这是Java的现代日期和时间API。其中一种选项是使用格式化程序:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSS");
    LocalDateTime dateTime = LocalDateTime.parse(timeString, formatter);
    System.out.println(dateTime);

当使用您提供的字符串"2013-01-09 09:15:02.500000"时,打印输出如下:
2013-01-09T09:15:02.500

如果您想在秒数上打印具有六位小数的值,即使最后三位小数为0,也可以使用相同的格式化程序将时间格式化回字符串:
    System.out.println(dateTime.format(formatter));

另一个选项是,您可以利用字符串类似于ISO 8601格式的事实,现代类默认解析该格式,即没有任何显式格式化程序。只有ISO 8601有一个 T 来表示时间部分的开始,但我们可以轻松解决这个问题:

    LocalDateTime dateTime = LocalDateTime.parse(timeString.replace(' ', 'T'));

它提供了相同的结果,2013-01-09T09:15:02.500。它更短,但也更棘手。
为什么要费心? DateTimestamp类已经过时,尤其是SimpleDateFormat已被证明很麻烦。它在你的情况下表现出的意外行为只是许多故事中的一个小插曲。现代API通常更易于使用。
为什么你的格式化程序不起作用?
虽然SimpleDateFormatDateTimeFormatter使用的格式模式字符串相似,但存在差异。其中之一是SimpleDateFormat理解大写字母S表示毫秒,无论有一个还是九个,而对于DateTimeFormatter,它们表示秒的分数。此外,您的SimpleDateFormat获取了小数点后面的所有六个数字,忽略了您只键入了三个S的事实,因此没有零可以匹配'000'(顺便说一句,撇号不必要,只有字母需要)。
链接 Oracle教程

3

尝试使用java.sql.Timestamp

     Timestamp ts = Timestamp.valueOf("2013-01-09 09:15:03.500000"); 
     Date date = new Date(ts.getTime())

与 SimpleDateFormat 相比,它也是线程安全且快速的。


我忘了提到,时间字符串实际上不是在UTC时区,而是在某个本地时区。我想能够设置时区并转换为UTC。SimpleDateFormat可以设置时区,但Timestamp不能。 - Kan Li
时间戳会解析本地时区的输入,尝试使用String s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(ts.getTime());,你将得到相同的时间。 - Evgeniy Dorofeev

2
我已经自己解决了。顺便提一下,Apache commons的FastDateFormat似乎接受SSS000格式并正确解析时间。

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