it-swarm.com.ru

Как конвертировать ZonedDateTime в Date?

Я пытаюсь установить независимую от сервера дату в моей базе данных, и я считаю, что для этого лучше всего установить UTC DateTime. Мой db-сервер - Cassandra, а драйвер db для Java понимает только тип Date.

Итак, если предположить, что в моем коде я сейчас использую новый Java 8 ZonedDateTime для получения UTC (ZonedDateTime.now(ZoneOffset.UTC)), как я могу преобразовать этот экземпляр ZonedDateTime в «устаревший» класс Date?

61
Milen Kovachev

Вы можете конвертировать ZonedDateTime в мгновение, которое вы можете использовать непосредственно с датой.

Date.from(Java.time.ZonedDateTime.now().toInstant());
106
Slim Soltani Dridi

тЛ; др

Java.util.Date.from(  // Transfer the moment in UTC, truncating any microseconds or nanoseconds to milliseconds.
    Instant.now() ;   // Capture current moment in UTC, with resolution as fine as nanoseconds.
)

Хотя в этом коде не было никакого смысла. И Java.util.Date, и Instant представляют момент в UTC, всегда в UTC. Код выше имеет тот же эффект, что и:

new Java.util.Date()  // Capture current moment in UTC.

Здесь нет никакой пользы от использования ZonedDateTime. Если у вас уже есть ZonedDateTime, настройте UTC, распаковав Instant.

Java.util.Date.from(             // Truncates any micros/nanos.
    myZonedDateTime.toInstant()  // Adjust to UTC. Same moment, same point on the timeline, different wall-clock time.
)

Другой ответ правильный

Ответ by ssoltanid правильно решает ваш конкретный вопрос, как преобразовать объект новой школы Java.time (ZonedDateTime) в объект старой школы Java.util.Date . Извлеките Instant из ZonedDateTime и перейдите к Java.util.Date.from()

Потери данных

Обратите внимание, что вы понесете потерю данных, так как Instant отслеживает наносекунды , поскольку Epoch , в то время как Java.util.Date отслеживает миллисекунды с момента Epoch.

diagram comparing resolutions of millisecond, microsecond, and nanosecond

Ваш вопрос и комментарии поднимают другие вопросы.

Хранить серверы в UTC

Ваши серверы должны иметь свою ОС хоста, установленную на UTC, как правило. JVM выбирает этот параметр Host OS в качестве часового пояса по умолчанию в тех реализациях Java, о которых я знаю.

Укажите часовой пояс

Но вы никогда не должны полагаться на текущий часовой пояс JVM по умолчанию. Вместо того, чтобы выбрать настройку Host, флаг, запущенный при запуске JVM, может установить другой часовой пояс. Еще хуже: любой код в любом потоке любого приложения в любой момент может вызвать Java.util.TimeZone::setDefault , чтобы изменить это значение по умолчанию во время выполнения!

Кассандра Timestamp Тип

Любой достойная база данных и драйвер должны автоматически обрабатывать настройку переданного времени и даты в UTC для хранения. Я не использую Cassandra, но, похоже, она имеет некоторую элементарную поддержку для даты и времени. В документации говорится, что тип Timestamp представляет собой количество миллисекунд той же эпохи (первый момент 1970 года в UTC). 

ISO 8601

Кроме того, Cassandra принимает строковые входные данные в стандартных форматах ISO 8601 . К счастью, Java.time использует форматы ISO 8601 в качестве значений по умолчанию для анализа/генерации строк. Реализация Instant class ’ toString будет работать хорошо. 

Точность: Миллисекунда против Нанокорда

Но сначала нам нужно уменьшить наносекундную точность ZonedDateTime до миллисекунд. Одним из способов является создание нового Мгновенного действия с использованием миллисекунд. К счастью, в Java.time есть несколько удобных методов для конвертации в миллисекунды и из них.

Пример кода

Вот пример кода в Java 8 Update 60.

ZonedDateTime zdt = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );
…
Instant instant = zdt.toInstant();
Instant instantTruncatedToMilliseconds = Instant.ofEpochMilli( instant.toEpochMilli() );
String fodderForCassandra = instantTruncatedToMilliseconds.toString();  // Example: 2015-08-18T06:36:40.321Z

Или, в соответствии с этим документация по драйверу Java Cassandra , вы можете передать экземпляр Java.util.Date (не путать с Java.sqlDate ). Таким образом, вы можете сделать j.u.Date из этого instantTruncatedToMilliseconds в коде выше.

Java.util.Date dateForCassandra = Java.util.Date.from( instantTruncatedToMilliseconds );

Если делать это часто, вы можете сделать одну строчку.

Java.util.Date dateForCassandra = Java.util.Date.from( zdt.toInstant() );

Но было бы аккуратнее создать небольшой вспомогательный метод.

static public Java.util.Date toJavaUtilDateFromZonedDateTime ( ZonedDateTime zdt ) {
    Instant instant = zdt.toInstant();
    // Data-loss, going from nanosecond resolution to milliseconds.
    Java.util.Date utilDate = Java.util.Date.from( instant ) ;
    return utilDate;
}

Обратите внимание на разницу во всем этом коде, чем в вопросе. Код Вопроса пытался настроить часовой пояс экземпляра ZonedDateTime на UTC. Но это не обязательно. Концептуально:

ZonedDateTime = Instant + ZoneId

Мы просто извлекаем часть Instant, которая уже находится в UTC (в основном в UTC, для получения более подробной информации прочитайте документацию класса). 


О Java.time

Инфраструктура Java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые классы legacy date-time, такие как Java.util.Date , Calendar , & SimpleDateFormat .

Проект Joda-Time , теперь находящийся в режим обслуживания , рекомендует выполнить переход к классам Java.time .

Чтобы узнать больше, смотрите Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация: JSR 310 .

Вы можете обмениваться объектами Java.time непосредственно с вашей базой данных. Используйте драйвер JDBC , совместимый с JDBC 4.2 или новее. Нет необходимости в строках, нет необходимости в классах Java.sql.*.

Где взять классы Java.time? 

  • Java SE 8 , Java SE 9 , Java SE 10 и более поздних версий
    • Встроенный. 
    • Часть стандартного Java API со встроенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности Java.time перенесена в Java 6 & 7 в ThreeTen-Backport .
  • Android

Проект ThreeTen-Extra расширяет Java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к Java.time. Вы можете найти здесь несколько полезных классов, таких как Interval , YearWeek , YearQuarter и more .

41
Basil Bourque

Вот пример преобразования текущего системного времени в UTC. Он включает в себя форматирование ZonedDateTime как строки, а затем объект String будет проанализирован в объект даты с использованием Java.text DateFormat. 

    ZonedDateTime zdt = ZonedDateTime.now(ZoneOffset.UTC);
    final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss");
    final DateFormat FORMATTER_YYYYMMDD_HH_MM_SS = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
    String dateStr = zdt.format(DATETIME_FORMATTER);

    Date utcDate = null;
    try {
        utcDate = FORMATTER_YYYYMMDD_HH_MM_SS.parse(dateStr);
    }catch (ParseException ex){
        ex.printStackTrace();
    }
3
Asanka Siriwardena

Если вы используете ThreeTen backport для Android и не можете использовать более новую функцию Date.from(Instant instant) (для которой требуется минимум API 26), вы можете использовать:

ZonedDateTime zdt = ZonedDateTime.now();
Date date = new Date(zdt.toInstant().toEpochMilli());

или же:

Date date = DateTimeUtils.toDate(zdt.toInstant());

Пожалуйста, прочитайте также совет в ответ Василия Бурка

2
David Rawson

Если вас интересует только сейчас, просто используйте:

Date d = new Date();
1
Jacob Eckel

Вы можете сделать это, используя Java.time классы, встроенные в Java 8 и более поздние версии.

ZonedDateTime temporal = ...
long epochSecond = temporal.getLong(INSTANT_SECONDS);
int nanoOfSecond = temporal.get(NANO_OF_SECOND);
Date date = new Date(epochSecond * 1000 + nanoOfSecond / 1000000);
1
Peter Lawrey

Для докер-приложения, подобного комментарию beehuang, вы должны установить часовой пояс.

В качестве альтернативы вы можете использовать withZoneSameLocal . Например:

2014-07-01T00: 00 + 02: 00 [GMT + 02: 00] конвертируется

Date.from(zonedDateTime.withZoneSameLocal(ZoneId.systemDefault()).toInstant())

до вт июл 01 00:00:00 CEST 2014 и

Date.from(zonedDateTime.toInstant())

до Пн 30 июня 22:00:00 UTC 2014

0
dwe

Я использую это. 

public class TimeTools {

    public static Date getTaipeiNowDate() {
        Instant now = Instant.now();
        ZoneId zoneId = ZoneId.of("Asia/Taipei");
        ZonedDateTime dateAndTimeInTai = ZonedDateTime.ofInstant(now, zoneId);
        try {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateAndTimeInTai.toString().substring(0, 19).replace("T", " "));
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

Потому что Date.from(Java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant()); Это не работает !!! Если вы запускаете приложение на своем компьютере, это не проблема. Но если вы работаете в каком-либо регионе AWS, Docker или GCP, это вызовет проблемы. Потому что компьютер не ваш часовой пояс в облаке. Вы должны правильно установить часовой пояс в коде. Например, Азия/Тайбэй. Тогда это будет исправлено в AWS или Docker или GCP.

public class App {
    public static void main(String[] args) {
        Instant now = Instant.now();
        ZoneId zoneId = ZoneId.of("Australia/Sydney");
        ZonedDateTime dateAndTimeInLA = ZonedDateTime.ofInstant(now, zoneId);
        try {
            Date ans = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateAndTimeInLA.toString().substring(0, 19).replace("T", " "));
            System.out.println("ans="+ans);
        } catch (ParseException e) {
        }
        Date wrongAns = Date.from(Java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant());
        System.out.println("wrongAns="+wrongAns);
    }
}
0
beehuang