Как бы вы представили дату рождения в вашей модели Java? - PullRequest
7 голосов
/ 27 октября 2010

И подождите, не спешите отвечать на «java.util.Date», рассмотрите следующий сценарий.

Объект Person, имеющий 2 поля: "birthday" и "nextMeeting", оба java.util.Date. Теперь день рождения хранится в базе данных как столбец типа даты (без времени), например. 01-10-1979, и nextMeeting как тип даты и времени, например, 01-10-2010 20: 00: 00.

Вы извлекаете его из базы данных, JDBC автоматически устанавливает полночь на день рождения. Теперь вам нужно отправить этот объект в другую JVM, используя, скажем, RMI или любую другую технологию.

На другом конце JVM имеет часовой пояс -1h от исходящей JVM. Здесь начинается проблема. nextMeeting становится 01-10-2010 19:00:00, что абсолютно ТОЧНО и ПРАВИЛЬНО с точки зрения пользователя и т. д. *

НО день рождения станет 30-09-1979 23:00:00, который будет представлен пользователю как 30 сентября, что на самом деле не то, что мы хотим, потому что день рождения явно статичен и НЕ зависит от часовых поясов.

Таким образом, тип столбца в дБ выбран правильно (дата). Этот тип столбца обычно представлен как java.util.Date. Но в нашем случае это неправильный тип java для использования.

Так как бы вы представили день рождения? Учтите, что вам нужно манипулировать этим объектом в пользовательском интерфейсе, как в компоненте DatePicker и т. Д.

Ответы [ 6 ]

6 голосов
/ 31 марта 2016

В других ответах используются устаревшие классы.

java.time

И Joda-Time, и старые классы java.util.Date/.Calendar были вытеснены фреймворком java.time , встроенным в Java 8 и более поздние версии. Определяется JSR 310 . Продлен проектом ThreeTen-Extra . Обратный перенос в Java 6 & 7 с помощью проекта ThreeTen-BackPort , который обернут для Android с помощью проекта ThreeTenABP .

LocalDate

Значение только для даты без времени суток и без часового пояса может быть представлено классом LocalDate. Такой класс отсутствовал в старых классах даты-времени, связанных с ранними версиями Java. Старый класс java.sql.Date претендует на то, чтобы иметь только дату, но фактически имеет время суток, унаследованное от java.util.Date (значение даты-времени).

LocalDate dateOfBirth = LocalDate.new( 1979 , 1 , 10 );

Без времени суток и часового пояса дата рождения по своей сути является неточной для определения возраста. Но почти во всех случаях использования нам все равно; часть дня «давай или возьми» достаточно близка.

ZonedDateTime

Для встречи мы не можем быть такими глупыми. Нам нужна дата, время и часовой пояс. В java.time это означает ZonedDateTime класс. Часовой пояс является ключевым элементом , отсутствующим в сценарии в Вопросе. Добавьте часовой пояс и все хорошо.

ZoneId zoneIdMontreal = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtMontreal = ZonedDateTime.of( 2010 , 1 , 10 , 20 , 0 , 0 , zoneIdMontreal );

Теперь передайте объекты на другую машину. Оба остаются неизменными: одно и то же значение только для даты рождения (1979-01-10) и одно и то же время для собрания в Монреале.

Настройка часового пояса

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

ZoneId zoneIdParis = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtParis = zdtMontreal.withZone( zoneIdParis );

У нас есть один и тот же момент на временной шкале, представленной двумя модами, в двух объектах, zdtMontreal & zdtParis.

LocalDateTime

Если ваш nextMeeting не имеет ни часового пояса, ни информации о смещении от UTC, тогда представляйте как LocalDateTime объект. В базе данных он будет храниться в виде типа TIMESTAMP WITHOUT TIME ZONE.

Такие значения не представляют момент на временной шкале. Они представляют только диапазон возможных моментов. Чтобы определить фактический момент, вы должны предоставить контекст определенного часового пояса.

Для хранения будущих значений даты и времени, таких как запланированное собрание, продолжительностью более нескольких недель, может быть целесообразно использовать это без часового пояса. Политики по всему миру склонны часто менять летнее время и пересматривать свои часовые пояса. И они часто делают это с небольшим предупреждением, всего за несколько недель.

Чтобы определить фактический момент, такой как отображение расписания в календаре, примените часовой пояс ZoneId, чтобы получить ZonedDateTime.

ISO 8601

Классы java.time по умолчанию используют стандартные форматы ISO 8601 при синтаксическом анализе / генерации текстовых представлений значений даты и времени. Класс ZonedDateTime идет еще дальше, расширяя стандарт ISO 8601, добавляя название часового пояса в квадратных скобках.

При сериализации значений через текст используйте понятные и однозначные форматы ISO 8601.

Ни один из вопросов, поднятых в Вопросе, не остается. Использование превосходной библиотеки даты и времени и ISO 8601, такого как java.time, решает проблему.

База данных

В вашей базе данных должен использоваться тип только дата для даты рождения и тип отметки времени с часовым поясом для собрания (см. Типы данных SQL в Википедии и в документации вашей базы данных). Ваш драйвер JDBC обеспечивает вас обоими типами.

Со временем драйверы JDBC будут обновлены для непосредственного использования типов java.time. Но до тех пор мы должны конвертировать в java.sql типы, такие как java.sql.Date и java.sql.Timestamp. Новые методы были добавлены в старые классы для поддержки этих преобразований.

java.sql.Date sqlDateOfBirth = java.sql.Date.valueOf( dateOfBirth );
java.sql.Timestamp sqlMeeting = java.sql.Timestamp.valueOf( zdtMontreal );

Затем позвоните setDate и setTimestamp на ваш PreparedStatement.

Идя в другом направлении, от базы данных до Java, наберите getDate и getTimestamp на вашем ResultSet. Затем немедленно переводите в типы java.time, избегая использования типов java.sql в вашей бизнес-логике.

Для значения даты и времени мы должны пройти через объект Instant. Instant - это момент времени на UTC . Мы применяем часовой пояс, чтобы получить время настенных часов для пользователя.

LocalDate dateOfBirth = mySqlDate.toLocalDate();
Instant instant = mySqlTimestamp.toInstant();
ZonedDateTime zdtMontreal = ZonedDateTime.ofInstant( instant , zoneIdMontreal );
5 голосов
/ 27 октября 2010

Используйте LocalDate из JodaTime и сохраняйте только дату дня рождения, а не время.

1 голос
/ 27 октября 2010

Каким-то образом две java-системы должны будут согласовать информацию Calendar / TimeZone, или объект Date должен быть преобразован в метку времени при передаче в удаленную систему.

Самый простой способ может состоять в том, чтобы просто потребовать от всех клиентов рассматривать день рождения как время по Гринвичу - когда они отображают / сравнивают / независимо от того, какие дни рождения, они создают Calendar с "GMT" TimeZone, и затем setTime() на нем с прилагаемым Date.

Если вы вообще работаете с моделью локально, у вас действительно должен быть объект Date, а не просто отметка времени.

0 голосов
/ 27 октября 2010

Это очень хороший вопрос ...

Например, Android хранит день рождения как String в формате 'yyyy-MM-dd'.Мне было интересно, почему они не используют java.util.Date, и я предполагаю, что причиной будет та же проблема, которую вы привели здесь.».Но после нескольких минут поиска в документации по Java и joda-time я понятия не имею, как это сделать.

РЕДАКТИРОВАТЬ: Кажется, @Jeroen прав - используйте LocalDate.

0 голосов
/ 27 октября 2010

если вы имеете дело с датами, лучше использовать joda time .Вы можете построить объект DateTime с информацией о дате / времени и часовом поясе, чтобы иметь всю информацию, необходимую для работы с различными часовыми поясами.

0 голосов
/ 27 октября 2010

Для манипуляции я советую java.util.Calendar

Для представления

День рождения как java.util.Date
NextMeetin as java.sql.Timestamp

...