java.time
Использование примера из пояснения:
Метка времени - C50204EC EC42EE92 эквивалентен 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 секунд, что более чем в 4 раза лучше, чем наносекундное (10 ^ -9 секунд) разрешение Instant
.
Класс Calendar
, который вы пытались использовать, всегда был плохо спроектирован и теперь давно устарел.Вместо этого я делаю то, что предложил Inalen в комментарии, я использую java.time, современный Java-интерфейс даты и времени.Редактировать: Для сравнения 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 nano тожемного?Это потому, что я использовал округление до половины при вызове на setScale
.Если вместо этого я усекаю (используя RoundingMode.FLOOR
), я получаю точный результат из объяснения.Поэтому моя версия не теряет большей точности, чем их.
Ссылка
Учебное пособие по Oracle: Дата и время , объясняющие, как использовать java.time.