Я столкнулся с ошибкой, из-за которой java.sql
Timestamp.valueOf(LocalDateTime.MIN())
конвертирует неправильно.
Учитывая java.time
LocalDateTime.MIN() = -999999999-01-01T00:00:00
Но когда я пытаюсь преобразовать его в дружественную к SQL метку времени, почему я сталкиваюсь с другим анализируемым временем, которое неправильно искажено?
Timestamp.valueOf(LocalDateTime.MIN()) = +169087565-03-15 04:51:43.000000
Я ожидал самую старую дату, но это выдает МАКС.
ПС:
Я работал над этим, используя
Timestamp.valueOf(//
LocalDateTime.of(LocalDate.ofYearDay(1800,1), LocalTime.MIDNIGHT)
Использование: в Timestamp.valueOf(LocalDateTime.MIN())
синтаксический анализ не выполняется. Это конверсия. (Парсинг — это анализ строки символов.)
Нам это знать не обязательно, поскольку, как я уже сказал, нам не следует использовать Timestamp
. Из любопытства только new Timestamp(Long.MIN_VALUE).toLocalDateTime()
дает +292278994-08-17T08:12:55.192
. Минимальное значение Timestamp
кажется около 0001-01-01 00:00:00.0. Что, вероятно, соответствует диапазону, поддерживаемому многими вариантами SQL.
Никогда не используйте Timestamp
.
Instant
, OffsetDateTime
, ZonedDateTime
java.time.OffsetDateTime
TIMESTAMP WITH TIME ZONE
Представляйте просто дату с указанием времени суток (а не момента).
LocalDateTime
java.time.LocalDateTime
TIMESTAMP WITHOUT TIME ZONE
java.sql.Timestamp
Вы сказали:
Дружественная к SQL отметка времени
Класс java.sql.Timestamp
— один из ужасно ошибочных устаревших классов даты и времени, которые много лет назад были вытеснены современными классами java.time, определенными в JSR 310.
OffsetDateTime
, а не Timestamp
JDBC 4.2 и более поздних версий требует, чтобы каждый драйвер JDBC поддерживал java.time. В частности, используйте OffsetDateTime вместо Timestamp
.
Java предлагает три класса для представления момента: Instant
, OffsetDateTime
и ZonedDateTime
. Но только OffsetDateTime
сопоставляется с типом SQL в JDBC из-за ограничений стандарта SQL.
ЛокальныйДатаВремя
Класс LocalDateTime
не может представлять момент, поскольку ему не хватает контекста часового пояса или смещения от UTC. Класс LocalDateTime
представляет собой просто дату с указанием времени суток, не более того.
И устаревший тип Timestamp
, и современный тип OffsetDateTime
представляют собой момент, точку на временной шкале. Так что не стоит смешивать LocalDateTime
с ними.
.MIN
Избегайте использования крайних значений минимума/максимума. Системы баз данных различаются по своим ограничениям. Все, что мне известно, имеет гораздо меньший диапазон, чем java.time.
Если вам нужно какое-то значение сигнала для обозначения «старого», используйте более современное значение. Например, рассмотрим использование java.time.Instant.EPOCH, первого момента 1970 года в формате UTC, 1970-01-01T00:00Z.
OffsetDateTime epoch = Instant.EPOCH.atOffset( ZoneOffset.UTC ) ;
Я ожидал самую старую дату, но это выдает МАКС.
Вероятно, вы видите эффект переноса из-за целочисленного переполнения .
Внутренняя реализация Timestamp
использует 32-битное целое число миллисекунд с момента ссылки на эпоху 1970-01-01T00:00Z, отрицательное до, положительное после. Классы java.time используют гораздо больший диапазон посредством двух счетчиков: 64-битное целое число целых секунд из той же ссылки на эпоху плюс дробная секунда, сохраняемая как счетчик наносекунд.
Но на самом деле нет смысла изучать этот вопрос. Нет смысла когда-либо использовать java.sql.Timestamp
.
Если вам вручен Timestamp
, немедленно преобразуйте, используя новые методы преобразования, добавленные к старым классам. В частности, java.time.Instant, затем назначьте смещение, чтобы получить SQL-дружественный OffsetDateTime
объект.
Instant instant = myTimestamp.toInstant() ; // A moment as seen in UTC, always UTC.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
Если вам нужен объект Timestamp
для взаимодействия со старым кодом, еще не обновленным до java.time, конвертируйте.
java.sql.Timestamp ts = Timestamp.from( odt.toInstant() ) ;
Прямым эквивалентом JDBC java.sql.Timestamp
является java.time.LocalDateTime
(что иллюстрирует, почему тот факт, что он был основан на java.util.Date
, был плохой идеей), как и для столбцов SQL TIMESTAMP [WITHOUT TIME ZONE]
, которые не содержат информации о часовом поясе и всегда интерпретируются в часовом поясе JVM по умолчанию для преобразование в java.sql.Timestamp
, если только для их получения не используется один из методов с Calendar
.
Кроме того, вы утверждаете, что «только OffsetDateTime сопоставляется с типом SQL в JDBC», неверно. LocalDate
сопоставляется с java.sql.Types.DATE
(стандарт SQL DATE
), LocalTime
сопоставляется с TIME
(стандарт SQL TIME [WITHOUT TIME ZONE]
), LocalDateTime
сопоставляется с TIMESTAMP
(стандарт SQL TIMESTAMP [WITHOUT TIME ZONE]
, OffsetTime
сопоставляется с TIME_WITH_TIMEZONE
(стандарт SQL TIME WITH TIME ZONE
) и OffsetDateTime
сопоставляется с TIMESTAMP_WITH_TIMEZONE
(SQL стандарт TIMESTAMP WITH TIME ZONE
).
Если базовым является TIMESTAMP
, то переключение на использование OffsetDateTime
не гарантированно будет работать, поскольку JDBC не определяет такое сопоставление. JDBC определяет только сопоставление с LocalDateTime
, поэтому совместимый драйвер не может (и, что более важно, не должен, поскольку интерпретация/преобразование будет неоднозначным) поддерживать сопоставление с OffsetDateTime
.
Небольшое дополнение к моему второму комментарию: LocalDateTime
также определено для DATE
и TIME
, по крайней мере, для setObject
(см. таблицу B-5 в JDBC 4.3)
Как уже было сказано, вам следует придерживаться своего
LocalDateTime
, это все, что вам нужно. Избегайтеjava.sql.Timestamp
, поскольку это был настоящий хак поверх и без того проблемного классаDate
, который устарел с версии JDBC 4.2. В любом случае, вашTimestamp
, вероятно, был недостаточным, вы пытались присвоить ему значение, выходящее за пределы диапазона, который он может удерживать.