Как преобразовать десятичную метку времени в дату в Java с конечными десятичными числами - PullRequest
0 голосов
/ 06 марта 2019

Я пытался выяснить, как преобразовать временную метку в дату, но с последними десятичными знаками в конце, например: Timestamp - C50204EC EC42EE92 эквивалентен 27 сентября 2004 года 03: 18: 04.922896299 UTC.

Формат метки времени включает в себя первые 32-битные секунды без знака в виде поля, охватывающего 136 лет, и 32-битное поле дроби, разрешающее 232 пикосекунд .В форматах меток времени основная эпоха, или базовая дата эпохи 0, равна 0 ч 1 января 1900 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); 

Я думаю, что это, вероятно, невозможно с календарем, поэтому мне придется делать это с нуля, но я не знаю, как это сделать.

1 Ответ

2 голосов
/ 06 марта 2019

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.

...