Java8 java.util.Date перетворення в java.time.ZonedDateTime


77

Я отримую наступний виняток під час спроби конвертувати java.util.Dateв java.time.LocalDate.

java.time.DateTimeException: Unable to obtain ZonedDateTime from TemporalAccessor: 2014-08-19T05:28:16.768Z of type java.time.Instant

Код такий:

public static Date getNearestQuarterStartDate(Date calculateFromDate){

    int[] quaterStartMonths={1,4,7,10};     
    Date startDate=null;

    ZonedDateTime d=ZonedDateTime.from(calculateFromDate.toInstant());
    int frmDateMonth=d.getMonth().getValue();

Чи є щось не так у використанні ZonedDateTimeкласу?

Відповідно до документації, це повинно перетворити java.util.Dateоб'єкт на ZonedDateTime. Формат дати вище стандартний Дата?

Чи повинен я відступати за часом Джоди?

Якби хтось міг дати якусь пропозицію, це було б чудово.

Відповіді:


107

Щоб перетворити a Instantна a ZonedDateTime, ZonedDateTimeпропонує метод ZonedDateTime.ofInstant(Instant, ZoneId). Так

Отже, якщо припустити, що вам потрібен ZonedDateTimeчасовий пояс за замовчуванням, ваш код повинен бути

ZonedDateTime d = ZonedDateTime.ofInstant(calculateFromDate.toInstant(),
                                          ZoneId.systemDefault());

На жаль Дякую. Виправлено зараз.
JB Nizet

Що робити, якщо я хочу перетворити його на той самий часовий пояс? Я завжди вважаю дату UTC, тому я можу просто зробити ZoneId.UTC?
Дідьє А.

6
Дата взагалі не має часового поясу. Це лише кількість мілісекодів, що минули з точного моменту (1970-01-01T00: 00 UTC). Не існує "того самого" часового поясу. Якщо ви хочете, щоб ZonedDateTime знаходився в часовому поясі UTC, ви повинні передати ZoneOffset.UTC.
JB Nizet

2
@DidierA. A Dateсхожий на an Instant, які одночасно є позначками часу UNIX. Крім того, загальноприйнятою практикою є твердження, що мітки часу UNIX - це часовий пояс UTC.
kevinarpe

38

Щоб отримати ZonedDateTime від дати, ви можете використовувати:

calculateFromDate.toInstant().atZone(ZoneId.systemDefault())

Потім ви можете викликати toLocalDateметод, якщо вам потрібен LocalDate. Див. Також: Перетворення java.util.Date на java.time.LocalDate


Вищевказане рішення також працює для мене, і перетворення на LocalDate було дуже корисним. Одне питання, з точки зору дизайну, ZonedDateTime.from () має вести себе належним чином, доки воно забезпечене java.time.Instant. У цьому випадку це не так, як очікувалося. Це якась непослідовність?
Ironluca

3
ZonedDateTime.from () вимагає ZoneId, але миттєвий його не має. Таким чином, ZonedDateTime.from (миттєво) видає виняток.
JodaStephen

7

Відповіді на цей питання assylias і відповіді на цей питання JB Nizet є правильними:

  1. Викличте новий метод перетворення, доданий до застарілого класу, java.util.Date::toInstant .
  2. Телефонуйте Instant::atZone, передаючи a ZoneId, в результаті чого a ZonedDateTime.

введіть тут опис зображення

Але ваш приклад коду спрямований на квартали. Про це читайте далі.

Чверті

Не потрібно самостійно обробляти квартали. Використовуйте вже написаний та перевірений клас.

org.threeten.extra.YearQuarter

У java.time класи продовжені ThreeTen-Extra проекту. Серед безлічі зручних класів, що пропонуються в цій бібліотеці, ви знайдете QuarterіYearQuarter .

Спочатку дістаньте свій ZonedDateTime.

ZonedId z = ZoneID.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = myJavaUtilDate.toInstant().atZone( z ) ;

Визначте рік-квартал для цієї конкретної дати.

YearQuarter yq = YearQuarter.from( zdt ) ;

Далі нам потрібна дата початку цього кварталу.

LocalDate quarterStart = yq.atDay( 1 ) ;

Хоча я не обов'язково рекомендую це робити, ви можете використовувати один рядок коду, а не реалізовувати метод.

LocalDate quarterStart =                    // Represent a date-only, without time-of-day and without time zone.
    YearQuarter                             // Represent a specific quarter using the ThreeTen-Extra class `org.threeten.extra.YearQuarter`. 
    .from(                                  // Given a moment, determine its year-quarter.
        myJavaUtilDate                      // Terrible legacy class `java.util.Date` represents a moment in UTC as a count of milliseconds since the epoch of 1970-01-01T00:00:00Z. Avoid using this class if at all possible.
        .toInstant()                        // New method on old class to convert from legacy to modern. `Instant` represents a moment in UTC as a count of nanoseconds since the epoch of 1970-01-01T00:00:00Z. 
        .atZone(                            // Adjust from UTC to the wall-clock time used by the people of a particular region (a time zone). Same moment, same point on the timeline, different wall-clock time.
            ZoneID.of( "Africa/Tunis" )     // Specify a time zone using proper `Continent/Region` format. Never use 2-4 letter pseudo-zone such as `PST` or `EST` or `IST`. 
        )                                   // Returns a `ZonedDateTime` object.
    )                                       // Returns a `YearQuarter` object.
    .atDay( 1 )                             // Returns a `LocalDate` object, the first day of the quarter. 
;

До речі, якщо ви можете повністю відмовитися від використання java.util.Date, зробіть це. Це жахливий клас, разом із такими братами та сестрами, як Calendar. Використовуйте Dateлише там, де це потрібно, коли ви взаємодієте зі старим кодом, який ще не оновлений до java.time .


Про java.time

Java.time каркас вбудований в Java 8 і пізніших версій. Ці класи витісняти неприємні старі застарілі класи дати і часу , такі як java.util.Date, Calendar, і SimpleDateFormat.

Щоб дізнатись більше, див. Підручник Oracle . І шукайте в Stack Overflow багато прикладів та пояснень. Специфікація - JSR 310 .

Проект Joda-Time , який зараз перебуває в режимі обслуговування , радить перейти на java.time класи .

Ви можете обмінювати об'єкти java.time безпосередньо з базою даних. Використовуйте драйвер JDBC, сумісний з JDBC 4.2 або новішою. Не потрібні рядки, не потрібні java.sql.*класи.

Де отримати класи java.time?

Проект ThreeTen-Extra розширює java.time додатковими класами. Цей проект є полігоном для можливих майбутніх доповнень до java.time. Ви можете знайти деякі корисні класи тут , такі як Interval, YearWeek, YearQuarter, і більш .


0

Відповідь не спрацювала для мене на Java 10, що зберігає util.Date в UTC.

Схоже, Date.toInstant () перетворює EpochMillis у локальний часовий пояс сервера.

ZDT.ofInstant (instant, zoneId) і instant.atZone (zoneId), здається, просто позначають TZ в даний момент, але це вже переплутано.

Я не міг знайти спосіб перешкодити Date.toInstant () зіпсувати час UTC з системним часовим поясом.

Єдиний спосіб, який я знайшов, щоб обійти це, - пройти клас sql.Timestamp:

new java.sql.Timestamp(date.getTime()).toLocalDateTime()
                                      .atZone(ZoneId.of("UTC"))
                                      .withZoneSameInstant(desiredTZ)

Ні, перетворення з Date::toInstant. І a, java.util.Dateі a java.time.Instantзнаходяться в UTC, за визначенням, завжди в UTC. Dateпредставляє відлік мілісекунд з епохи 1970-01-01T00: 00: 00Z, в той час Instantяк також відлік з тієї ж епохи, але з більш точним дозволом наносекунд.
Василь Бурк,

LocalDateTimeтут використовується саме той неправильний клас. У цьому класі навмисно не вистачає будь-якого поняття часового поясу чи відступу від UTC. Як такий, він не здатний представити момент, як зазначено у своєму класі JavaDoc. Перетворення на a LocalDateTimeвідкидає цінну інформацію, а нічого не отримано. Ця відповідь вводить в оману та неправильно. Дивіться правильну відповідь assylias. Все , що вам потрібно , щоб отримати ZonedDateTimeвід А Dateце: myJavaUtilDate.toInstant().atZone( desiredZoneIdGoesHere ).
Василь Бурк,

Василю, я на 100% погоджуюся з твоїми сподіваннями, але, на жаль, ми не були свідками того, як Java 10 поводилася з датою UTC у JST у PST. Ми використовуємо LocalDateTime тут лише як проміжний змінний, щоб не заплутати час UTC, перш ніж ми позначимо на ньому TZ.
TheArchitect

Немає жодної причини, щоб коли-небудь використовувати java.sql.Timestampзнову, тепер замінено на java.sql.OffsetDateTime- станом на JDBC 4.2 ми можемо безпосередньо обмінюватися деякими типами java.time з базою даних.
Василь Бурк,

Я пропоную замість того, щоб розміщувати цю відповідь, ви публікуєте власне запитання, де окреслюєте проблему, з якою ви зіткнулись, разом із прикладом коду. Ми мали б змогу це розібрати для вас.
Василь Бурк,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.