SimpleDateFormat使用&Quot;S";格式显示不正确的毫秒,但使用&Quot;SSS&Quot;格式不显示错误的毫秒
我遇到显示日期的毫秒分量乘以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.Date
和java.time
来说明意外行为,我知道java.time
更好,但我仍然希望了解SimpleDateFormat
行为。
我运行的是Java 14.0.2.12,但可以在11.0.10.9中重现。
解决方案
旧的日期-时间接口(java.util
日期-时间类型及其格式类型,SimpleDateFormat
)过时且容易出错。建议完全停止使用,切换到java.time
、modern date-time API*。
让我们了解SimpleDateFormat
产生的结果是如何令人困惑的(因此容易出错)。
1620946852050毫秒=1620946852000毫秒+50毫秒
对于1620946852000,System.out.println(localDateTime)
将生成结果2021-05-14T00:00:52
。
50毫秒=(50/1000)秒=0.05秒。DateTimeFormatter
也是这样呈现的。documentation清楚地将S
描述为:。换句话说,它将其表示为小数点后9位(纳秒)的0.05的数学浮点数。
您可以这样理解:在String.valueOf(0.05)
的右侧填零,以将精度调到小数点后9位。因此,它变成"0.050000000"
。现在,根据S
的编号,得到"0.050000000"
的子字符串。请注意,您最多只能在9个位置执行此操作,即SSSSSSSSSS
将引发异常。
S
:.0SS
:0.05SSS
:.050SSSS
:.0500以此类推SSSSSSSSS
:.050000000
这是我们小时候学数学分数时学到的表示法。
另一方面,SimpleDateFormat
没有将其表示为秒的分数;相反,它将.
之后的数字表示为毫秒数。documentation将S
描述为:毫秒。因此,它将其表示为一个数学十进制整数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,它将大部分java.time功能移植到Java 6&;7。如果您正在为Android项目工作,而您的Android API级别仍然不符合Java-8,请勾选Java 8+ APIs available through desugaring和How to use ThreeTenABP in Android Project。从Trail: Date Time了解有关现代日期-时间API的更多信息。
相关文章