SimpleDateFormat 使用 "S" 格式显示不正确的毫秒数,但不使用 "SSS"

SimpleDateFormat showing incorrect milliseconds with "S" format, but not with "SSS"

我遇到了显示日期的毫秒分量乘以 10 的问题。

具体来说,时间 52.050 在使用 .S 格式时显示为 52.50,但在使用 .SSS 格式时显示为 52.050 .

以下面的代码为例:

// Some arbitrary point with 50 milliseconds
final Date date = new Date(1620946852050 l);
final LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

final String format = "%-40s%-20s%-20s%n";
System.out.format(format, "Date Formatter", "Date Format", "Formatted Output");

Stream.of("HH:mm:ss", "HH:mm:ss.S", "HH:mm:ss.SS", "HH:mm:ss.SSS").forEach(dateFormat - > {
    System.out.println();
    System.out.format(format, SimpleDateFormat.class.getName(), dateFormat,
        new SimpleDateFormat(dateFormat).format(date));
    System.out.format(format, DateTimeFormatter.class.getName(), dateFormat,
        DateTimeFormatter.ofPattern(dateFormat).format(localDateTime));
});

这会产生以下输出:

Date Formatter                          Date Format         Formatted Output    

java.text.SimpleDateFormat              HH:mm:ss            00:00:52            
java.time.format.DateTimeFormatter      HH:mm:ss            00:00:52            

java.text.SimpleDateFormat              HH:mm:ss.S          00:00:52.50         
java.time.format.DateTimeFormatter      HH:mm:ss.S          00:00:52.0          

java.text.SimpleDateFormat              HH:mm:ss.SS         00:00:52.50         
java.time.format.DateTimeFormatter      HH:mm:ss.SS         00:00:52.05         

java.text.SimpleDateFormat              HH:mm:ss.SSS        00:00:52.050        
java.time.format.DateTimeFormatter      HH:mm:ss.SSS        00:00:52.050        

我已经使用 java.util.Datejava.time 来说明意外行为,我知道 java.time 更好,但我仍然想了解SimpleDateFormat 行为。

我是 运行 Java 14.0.2.12,但可以在 11.0.10.9 中重现。

遗留日期时间 API(java.util 日期时间类型及其格式类型,SimpleDateFormat)已过时且容易出错。建议完全停止使用,改用java.timemodern date-time API*.

让我们了解一下 SimpleDateFormat 产生的结果是如何令人困惑(因此容易出错)。

1620946852050 毫秒 = 1620946852000 毫秒 + 50 毫秒

对于 1620946852000,System.out.println(localDateTime) 将产生结果,2021-05-14T00:00:52

50 毫秒 = (50 / 1000) 秒 = 0.05 秒。 DateTimeFormatter也是这样介绍的。 documentationS 清楚地描述为:秒的分数。换句话说,它将它呈现为 0.05 到小数点后 9 位(纳秒)的数学浮点数。

可以这样理解:在String.valueOf(0.05)右边补零,将精度提高到小数点后9位。因此,它变成了"0.050000000"。现在,根据S的个数,得到"0.050000000"的子串。请注意,您最多只能在 9 个位置执行此操作,即 SSSSSSSSSS 将引发异常。

  • S: .0
  • SS: .05
  • SSS: .050
  • SSSS: .0500 等等直到
  • SSSSSSSSS:.050000000

这是我们小时候学数学分数时学的表示法

另一方面,SimpleDateFormat 不会将其显示为秒数;相反,它将 . 之后的数字显示为毫秒数。 documentationS描述为:毫秒。因此,它将它表示为 50 的数学十进制整数。这让人感到困惑,因为我们一看到 .,就会想到分数,而 SimpleDateFormat 认为它只是秒和毫秒的分隔符。

以下示例说明了这些不同的表示方式:

public class Main {
    public static void main(String[] args) {
        int sdf = 50;
        String dtf = String.format("%.9f", 0.05);

        System.out.format("sdf: %01d, dtf: %s%n", sdf, dtf.substring(0, 3));// Including two places for "0."
        System.out.format("sdf: %02d, dtf: %s%n", sdf, dtf.substring(0, 4));// Including two places for "0."
        System.out.format("sdf: %03d, dtf: %s%n", sdf, dtf.substring(0, 5));// Including two places for "0."
        System.out.format("sdf: %04d, dtf: %s%n", sdf, dtf.substring(0, 6));// Including two places for "0."
    }
}

输出:

sdf: 50, dtf: 0.0
sdf: 50, dtf: 0.05
sdf: 050, dtf: 0.050
sdf: 0050, dtf: 0.0500

* 无论出于何种原因,如果您必须坚持Java 6 或Java 7,您可以使用ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and . Learn more about the modern date-time API from Trail: Date Time