如何将十进制时间戳转换为 Java 中带有尾随小数的日期
How to convert decimal timestamp to date in Java with trailing decimals
我一直在试图弄清楚如何将时间戳转换为日期但末尾有尾随小数,例如:
时间戳 - C50204EC EC42EE92 相当于 2004 年 9 月 27 日 03:18:04.922896299 UTC.
时间戳格式包括作为跨越 136 年的字段的前 32 位无符号秒和解析 232 picoseconds 的 32 位小数字段。在时间戳格式中,原始纪元或纪元 0 的基准日期为 0 时 1900 年 1 月 1 日 UTC,此时所有位均为零。
到目前为止,这是我为代码编写的内容:
BigDecimal bi = new BigDecimal("1096255084000");
double decimal_timestamp = bi.doubleValue();
DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss.SSS");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(decimal_timestamp);
String date = formatter.format(calendar.getTime());
System.out.println(decimal_timestamp + " = " + date);
我的想法是日历可能无法实现,所以我必须从头开始,但我不知道该怎么做。
java.time
使用解释中的示例:
Timestamp - C50204EC EC42EE92 is equivalent to Sep 27, 2004
03:18:04.922896299 UTC.
Instant epoch = OffsetDateTime.of(1900, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC).toInstant();
BigInteger timeStamp = new BigInteger("C50204ECEC42EE92", 16);
// To get the whole part and the fraction right, divide by 2^32
double secondsSince1900 = timeStamp.doubleValue() / 0x1_0000_0000L;
// Convert seconds to nanos by multiplying by 1 000 000 000
Instant converted = epoch.plusNanos(Math.round(secondsSince1900 * 1_000_000_000L));
System.out.println(converted);
输出为:
2004-09-27T03:18:04.922896384Z
它关闭了 85 纳秒。可能更好的浮点运算可以做得更好。编辑:由于原始时间戳的分辨率为 2^-32 秒,精度是 Instant
的纳秒(10^-9 秒)分辨率的 4 倍以上,因此不可避免地会损失一点精度。
您尝试使用的 Calendar
class 设计总是很糟糕,现在已经过时了。相反,我按照评论中建议的 Amongalen 做,我使用 java.time,现代 Java 日期和时间 API。编辑:为了比较,Calendar
具有毫秒分辨率,因此充其量只会给您带来精度的实质性损失。
编辑:更精确的数学
我不能让 85 纳秒成为现实。这是一个尽可能保持精度并给出预期结果的版本:
BigDecimal timeStamp = new BigDecimal(new BigInteger("C50204ECEC42EE92", 16));
// To get the whole part and the fraction right, divide by 2^32
BigDecimal bit32 = new BigDecimal(0x1_0000_0000L);
BigDecimal secondsSince1900 = timeStamp.divide(bit32);
// Convert seconds to nanos by multiplying by 1 000 000 000; round to long
long nanosSince1900 = secondsSince1900.multiply(new BigDecimal(TimeUnit.SECONDS.toNanos(1)))
.setScale(0, RoundingMode.HALF_UP)
.longValueExact();
Instant converted = epoch.plusNanos(nanosSince1900);
2004-09-27T03:18:04.922896300Z
1纳米太多了?这是因为我在 setScale
的调用中使用了半向上舍入。相反,如果我截断(使用 RoundingMode.FLOOR
),我会从解释中得到准确的结果。所以我的版本并没有比他们的更精确。
Link
Oracle tutorial: Date Time 解释如何使用 java.time。
我一直在试图弄清楚如何将时间戳转换为日期但末尾有尾随小数,例如: 时间戳 - C50204EC EC42EE92 相当于 2004 年 9 月 27 日 03:18:04.922896299 UTC.
时间戳格式包括作为跨越 136 年的字段的前 32 位无符号秒和解析 232 picoseconds 的 32 位小数字段。在时间戳格式中,原始纪元或纪元 0 的基准日期为 0 时 1900 年 1 月 1 日 UTC,此时所有位均为零。
到目前为止,这是我为代码编写的内容:
BigDecimal bi = new BigDecimal("1096255084000");
double decimal_timestamp = bi.doubleValue();
DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss.SSS");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(decimal_timestamp);
String date = formatter.format(calendar.getTime());
System.out.println(decimal_timestamp + " = " + date);
我的想法是日历可能无法实现,所以我必须从头开始,但我不知道该怎么做。
java.time
使用解释中的示例:
Timestamp - C50204EC EC42EE92 is equivalent to Sep 27, 2004 03:18:04.922896299 UTC.
Instant epoch = OffsetDateTime.of(1900, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC).toInstant();
BigInteger timeStamp = new BigInteger("C50204ECEC42EE92", 16);
// To get the whole part and the fraction right, divide by 2^32
double secondsSince1900 = timeStamp.doubleValue() / 0x1_0000_0000L;
// Convert seconds to nanos by multiplying by 1 000 000 000
Instant converted = epoch.plusNanos(Math.round(secondsSince1900 * 1_000_000_000L));
System.out.println(converted);
输出为:
2004-09-27T03:18:04.922896384Z
它关闭了 85 纳秒。可能更好的浮点运算可以做得更好。编辑:由于原始时间戳的分辨率为 2^-32 秒,精度是 Instant
的纳秒(10^-9 秒)分辨率的 4 倍以上,因此不可避免地会损失一点精度。
您尝试使用的 Calendar
class 设计总是很糟糕,现在已经过时了。相反,我按照评论中建议的 Amongalen 做,我使用 java.time,现代 Java 日期和时间 API。编辑:为了比较,Calendar
具有毫秒分辨率,因此充其量只会给您带来精度的实质性损失。
编辑:更精确的数学
我不能让 85 纳秒成为现实。这是一个尽可能保持精度并给出预期结果的版本:
BigDecimal timeStamp = new BigDecimal(new BigInteger("C50204ECEC42EE92", 16));
// To get the whole part and the fraction right, divide by 2^32
BigDecimal bit32 = new BigDecimal(0x1_0000_0000L);
BigDecimal secondsSince1900 = timeStamp.divide(bit32);
// Convert seconds to nanos by multiplying by 1 000 000 000; round to long
long nanosSince1900 = secondsSince1900.multiply(new BigDecimal(TimeUnit.SECONDS.toNanos(1)))
.setScale(0, RoundingMode.HALF_UP)
.longValueExact();
Instant converted = epoch.plusNanos(nanosSince1900);
2004-09-27T03:18:04.922896300Z
1纳米太多了?这是因为我在 setScale
的调用中使用了半向上舍入。相反,如果我截断(使用 RoundingMode.FLOOR
),我会从解释中得到准确的结果。所以我的版本并没有比他们的更精确。
Link
Oracle tutorial: Date Time 解释如何使用 java.time。