Я пытаюсь установить независимую от сервера дату и время в своей базе данных, и я считаю, что лучший способ сделать это - установить дату и время в формате UTC. Мой сервер db - это Cassandra, а драйвер db для Java понимает только тип Date.
Итак, предполагая, что в моем коде я использую новую Java 8 ZonedDateTime для получения UTC сейчас ( ZonedDateTime.now(ZoneOffset.UTC)
), как я могу преобразовать этот экземпляр ZonedDateTime в «устаревший» класс Date?
java
java-8
java.util.date
datetime-conversion
zoneddatetime
Милен Ковачева
источник
источник
java.util.Date
иjava.sql.Date
.Ответы:
Вы можете преобразовать ZonedDateTime в момент времени, который можно использовать непосредственно с Date.
источник
Date
количество миллисекунд с начала эпохи - значит, это связано с UTC. Если вы распечатаете его, будет использоваться часовой пояс по умолчанию, но класс Date не знает часовой пояс пользователя ... См., Например, раздел «сопоставление» в docs.oracle.com/javase/tutorial/datetime/iso/legacy .html И LocalDateTime явно без ссылки на часовой пояс, что может сбивать с толку ...ZonedDateTime
.java.time.Instant
Класс является непосредственно заменойjava.util.Date
, как представляющий момент в UTC , хотяInstant
использует более высокое разрешение наносекунд вместо миллисекунд.Date.from( Instant.now() )
должно было быть твоим решением. Или, если на то пошло, этоnew Date()
имеет тот же эффект, фиксируя текущий момент в UTC.tl; dr
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. )
Другой ответ правильный
Ответ на ssoltanid правильно обращается ваш конкретный вопрос, как преобразовать новую школу java.time объект (
ZonedDateTime
) для старой школыjava.util.Date
объекта. ИзвлекитеInstant
из ZonedDateTime и перейдите кjava.util.Date.from()
.Потеря данных
Обратите внимание, что вы столкнетесь с потерей данных , поскольку
Instant
отслеживает наносекунды с эпохи, аjava.util.Date
отслеживает миллисекунды с эпохи.Ваш вопрос и комментарии поднимают другие вопросы.
Хранить серверы в UTC
На ваших серверах обычно должна быть установлена ОС хоста в формате UTC. JVM выбирает этот параметр ОС хоста в качестве часового пояса по умолчанию в реализациях Java, о которых я знаю.
Укажите часовой пояс
Но никогда не следует полагаться на текущий часовой пояс JVM по умолчанию. Вместо того, чтобы выбирать настройку хоста, флаг, переданный при запуске JVM, может установить другой часовой пояс. Еще хуже: любой код в любом потоке любого приложения в любой момент может позвонить,
java.util.TimeZone::setDefault
чтобы изменить это значение по умолчанию во время выполнения!Timestamp
Тип КассандрыЛюбая достойная база данных и драйвер должны автоматически обрабатывать настройку прошедшей даты и времени на UTC для хранения. Я не использую Cassandra, но, похоже, у него есть элементарная поддержка даты и времени. В документации указано, что его
Timestamp
тип - это количество миллисекунд той же эпохи (первый момент 1970 года по UTC).ISO 8601
Кроме того, Cassandra принимает строковые входные данные в стандартных форматах ISO 8601 . К счастью, java.time использует форматы ISO 8601 по умолчанию для синтаксического анализа / генерации строк. Реализация
Instant
классаtoString
подойдет.Точность: миллисекунда против наношнура
Но сначала нам нужно уменьшить наносекундную точность ZonedDateTime до миллисекунд. Один из способов - создать новый Instant за миллисекунды. К счастью, в java.time есть несколько удобных методов для преобразования в миллисекунды и обратно.
Пример кода
Вот пример кода в Java 8 (обновление 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
Или, согласно этому документу драйвера Cassandra Java , вы можете передать
java.util.Date
экземпляр (не путать сjava.sqlDate
). Таким образом, вы можете сделать juDate из этогоinstantTruncatedToMilliseconds
в приведенном выше коде.Если делать это часто, можно сделать однострочник.
Но было бы аккуратнее создать небольшой служебный метод.
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. Но в этом нет необходимости. Концептуально:
Мы просто извлекаем часть Instant, которая уже находится в UTC (в основном в UTC, подробности читайте в документации класса).
О java.time
Java.time каркас встроен в Java 8 и более поздних версий. Эти классы вытеснять неприятные старые устаревшие классы даты и времени , такие как
java.util.Date
,Calendar
, иSimpleDateFormat
.Проект Joda-Time , находящийся сейчас в режиме обслуживания , рекомендует перейти на классы java.time .
Чтобы узнать больше, см. Oracle Tutorial . И поищите в Stack Overflow множество примеров и объяснений. Спецификация - JSR 310 .
Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, совместимый с JDBC 4.2 или новее. Нет необходимости в строках, нет необходимости в
java.sql.*
занятиях.Где взять классы java.time?
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является испытательной площадкой для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь , такие как
Interval
,YearWeek
,YearQuarter
, и более .источник
Если вы используете Backport ThreeTen для Android и не можете использовать более новую версию
Date.from(Instant instant)
(для которой требуется минимум API 26), вы можете использовать:ZonedDateTime zdt = ZonedDateTime.now(); Date date = new Date(zdt.toInstant().toEpochMilli());
или:
Также прочитайте совет в ответе Базиля Бурка
источник
DateTimeUtils
класс с методами преобразования, поэтому я бы использовал Date date = DateTimeUtils.toDate (zdt.toInstant ()); `. Это не так уж и низко.Вот пример преобразования текущего системного времени в UTC. Он включает форматирование ZonedDateTime как String, а затем объект 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(); }
источник
Вы можете сделать это с помощью классов 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);
источник
Instant
s отслеживать секунды и наносекунды.Date
отслеживать миллисекунды. Это преобразование из одного в другое правильно.Я использую это.
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 в любом регионе, это вызовет проблемы. Потому что компьютер не является вашим часовым поясом в облаке. Вы должны правильно установить часовой пояс в Code. Например, Азия / Тайбэй. Потом исправят в 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); } }
источник
ans=Mon Jun 18 01:07:56 CEST 2018
, что неверно, а затемwrongAns=Sun Jun 17 17:07:56 CEST 2018
, что правильно.Принятый ответ у меня не сработал. Возвращаемая дата всегда является местной датой, а не датой исходного часового пояса. Я живу в UTC + 2.
//This did not work for me Date.from(java.time.ZonedDateTime.now().toInstant());
Я придумал два альтернативных способа получить правильную дату из ZonedDateTime.
Допустим, у вас есть ZonedDateTime для Гавайев
LocalDateTime ldt = LocalDateTime.now(); ZonedDateTime zdt = ldt.atZone(ZoneId.of("US/Hawaii"); // UTC-10
или для UTC, как было изначально задано
Instant zulu = Instant.now(); // GMT, UTC+0 ZonedDateTime zdt = zulu.atZone(ZoneId.of("UTC"));
Альтернатива 1
Мы можем использовать java.sql.Timestamp. Это просто, но, вероятно, также повредит вашу целостность программирования.
Альтернатива 2
Мы создаем дату из миллисекунд (ответ здесь ранее). Обратите внимание, что локальный ZoneOffset является обязательным.
ZoneOffset localOffset = ZoneOffset.systemDefault().getRules().getOffset(LocalDateTime.now()); long zonedMillis = 1000L * zdt.toLocalDateTime().toEpochSecond(localOffset) + zdt.toLocalDateTime().getNano() / 1000000L; Date date2 = new Date(zonedMillis);
источник
Для приложения-докера, такого как beehuang, вы должны установить свой часовой пояс.
В качестве альтернативы вы можете использовать withZoneSameLocal . Например:
2014-07-01T00: 00 + 02: 00 [GMT + 02: 00] преобразуется в
по Вт 01 июл 00:00:00 CEST 2014 и до
по Пн 30 июн 22:00:00 UTC 2014
источник
Если вас интересует только сейчас, просто используйте:
Date d = new Date();
источник