Как сохранить дату в ET со смещением, когда код Java выполняется в UTC - PullRequest
3 голосов
/ 14 мая 2019

Мы храним дату в таблице базы данных sqlserver как varchar.Когда это читается в коде Java как String, а затем анализируется в Date, он читается как UTC (код Java находится на серверах, которые находятся в UT).И при пересмотре даты в ET, это идет на 4 часа позже.Как мне справиться с сохранением даты в ET в этом столбце БД, чтобы она читалась как ET в коде Java.

Мы изучаем смещения, но не понимаем, что именно делать.

Varchar date в таблице 29.03.2009 23:23:03 // мы хотим, чтобы эта дата была в ET

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
Date beginDate = sdf.parse("03/29/2019 23:23:03");

// Проблема в том, что когда этот код выполняется, сервер находится в UTC.Итак, beginDate // читается как 29.03.2009 23:23:03 UTC вместо 29.03.2009 23:23:03 ET

Ожидается 29.03.2009 23:23:03 ET Actual29.03.2009 23:23:03 UTC

Ответы [ 3 ]

4 голосов
/ 14 мая 2019

Во-первых, вы должны знать, что у объекта Date нет часового пояса вообще . Это просто мгновение во времени. Таким образом, даже если вы правильно проанализировали значение, оно будет отображаться в нужный момент времени, но вам может потребоваться преобразовать его обратно в восточное время позже.

Во-вторых, вы должны знать, что при хранении таких значений возникает двусмысленность - если вы храните значение (скажем) 11/03/2019 01:30, это местное время происходит дважды - один раз перед переходом на летнее время и один раз после этого. Если вы всегда сохраняете время в прошлом, вы должны хотя бы рассмотреть вместо хранения UTC - хотя это не всегда правильный ответ, особенно если вы не храните будущие значения даты / времени .

Для синтаксического анализа вам просто нужно установить часовой пояс в Calendar, используемом SimpleDateFormat. Например:

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York");
Date beginDate = sdf.parse("03/29/2019 23:23:03");

Наконец, я бы настоятельно посоветовал вам начать миграцию кода для использования java.time, если это вообще возможно. Это намного приятнее API, чем java.util.Date и т. Д. API.

2 голосов
/ 14 мая 2019
  1. Используйте тип данных datetime вашей базы данных (СУБД), а не varchar для даты и / или времени.Во многих СУБД тип timestamp with timezone, определенный в стандарте SQL, является правильным типом для использования.Я не знаю SQL Server достаточно хорошо, чтобы точно сказать, что вы должны использовать там.Убедитесь, что вы храните дату и время в формате UTC.
  2. Если вы не можете обойти требование хранить как varchar, сохраните в формате ISO 8601 в формате UTC.Например, 2019-03-30T03:23:03Z.
  3. Если вы также не можете обойти требование хранить в восточном времени, убедитесь, что у вас есть четкое представление о североамериканском восточном времени или австралийском восточном времени.Храните дату и время со смещением UTC, например 2019-03-29T23:23:03-04:00.
  4. Если вы также не можете обойти требование хранить в формате 03/29/2019 23:23:03 (без смещения), имейте в виду, что осенью, когда летнее время(DST) заканчивается, и часы перемещаются назад, вы сохраняете неоднозначное время.

При любых обстоятельствах предпочитайте java.time, современный Java API даты и времени.Ответ Василия Бурка показывает, как, мне не нужно это повторять.

2 голосов
/ 14 мая 2019

java.time

Ответ Джона Скита правильный. Как он упомянул, вы должны использовать классы java.time , определенные в JSR 310. Эти современные классы много лет назад вытеснили ужасные классы даты и времени, такие как Date и SimpleDateFormat. Вот пример кода в этом направлении.

дата Varchar в таблице 29.03.2009 23:23:03 // мы хотим, чтобы эта дата была в ET

Разобрать строку как LocalDateTime, потому что в нашем вводе отсутствует индикатор смещения от UTC или часового пояса.

Определить шаблон форматирования, соответствующий вводу.

DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM/dd/uuuu HH:mm:ss" ) ;

Анализировать.

LocalDateTime ldt = LocalDateTime.parse( input , f ) ;

Если вы абсолютно уверены , эта дата и время были предназначены для определенного часового пояса, примените ZoneId, чтобы получить ZonedDateTime.

ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;

Настройте UTC, извлекая Instant.

Instant instant = zdt.toInstant() ;

Ваш драйвер JDBC может не принять Instant. Поэтому преобразуйте в OffsetDateTime, где смещение установлено равным нулю часов-минут-секунд (другими словами, сам UTC).

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;

Запись в столбец типа TIMESTAMP WITH TIME ZONE в вашей базе данных. Начиная с JDBC 4.2 и более поздних версий, мы можем напрямую обмениваться объектами java.time с базой данных.

myPreparedStatement.setObject( … , odt ) ;

И поиск.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

Настройте желаемый часовой пояс.

ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

Теперь у вас есть части, необходимые для некоторого рефакторинга базы данных , чтобы заменить этот столбец varchar на правильный столбец TIMESTAMP WITH TIME ZONE.

...