Muszę przechowywać datę i godzinę UTC w DB.
Przekonwertowałem dateTime podaną w określonej strefie czasowej na UTC. w tym celu postępowałem zgodnie z poniższym kodem.
Moja data wejściowaGodzina to „20121225 10:00:00 Z” strefa czasowa to „Azja / Kalkuta”
Mój serwer / baza danych (Oracle) działa w tej samej strefie czasowej (IST) „Azja / Kalkuta”
Pobierz obiekt Date w tej określonej strefie czasowej
String date = "20121225 10:00:00 Z";
String timeZoneId = "Asia/Calcutta";
TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
DateFormat dateFormatLocal = new SimpleDateFormat("yyyyMMdd HH:mm:ss z");
//This date object is given time and given timezone
java.util.Date parsedDate = dateFormatLocal.parse(date + " "
+ timeZone.getDisplayName(false, TimeZone.SHORT));
if (timeZone.inDaylightTime(parsedDate)) {
// We need to re-parse because we don't know if the date
// is DST until it is parsed...
parsedDate = dateFormatLocal.parse(date + " "
+ timeZone.getDisplayName(true, TimeZone.SHORT));
}
//assigning to the java.sql.TimeStamp instace variable
obj.setTsSchedStartTime(new java.sql.Timestamp(parsedDate.getTime()));
Przechowuj w DB
if (tsSchedStartTime != null) {
stmt.setTimestamp(11, tsSchedStartTime);
} else {
stmt.setNull(11, java.sql.Types.DATE);
}
WYNIK
DB (oracle) przechowuje to samo, podane dateTime: "20121225 10:00:00
nie w UTC.
Potwierdziłem z poniższego sql.
select to_char(sched_start_time, 'yyyy/mm/dd hh24:mi:ss') from myTable
Mój serwer DB działa również w tej samej strefie czasowej „Asia / Calcutta”
Daje mi poniższe wyglądy
Date.getTime()
nie jest w UTC- Lub Znacznik czasu ma wpływ na strefę czasową podczas zapisywania w DB. Co ja tu robię źle?
Jeszcze jedno pytanie:
Czy będzie timeStamp.toString()
drukować w lokalnej strefie czasowej, tak jak java.util.date
robi? Nie UTC?
java.sql.Timestamp
jest w czasie UTC, podczas przechowywania w polu TIMESTAMP bez informacji o strefie czasowej używa równoważnej daty / godziny w bieżącej strefie czasowej maszyny wirtualnejOdpowiedzi:
Chociaż nie jest to wyraźnie określone,
setTimestamp(int parameterIndex, Timestamp x)
kierowcy muszą przestrzegać zasad ustalonych przezsetTimestamp(int parameterIndex, Timestamp x, Calendar cal)
javadoc :Podczas rozmowy ze
setTimestamp(int parameterIndex, Timestamp x)
sterownikiem JDBC używa strefy czasowej maszyny wirtualnej do obliczenia daty i godziny znacznika czasu w tej strefie czasowej. Ta data i godzina jest tym, co jest przechowywane w bazie danych, a jeśli kolumna bazy danych nie przechowuje informacji o strefie czasowej, wszelkie informacje o strefie są tracone (co oznacza, że jest to zależne od aplikacji używającej bazy danych do korzystania z tę samą strefę czasową konsekwentnie lub wymyśl inny sposób rozróżniania strefy czasowej (tj. zapisz ją w osobnej kolumnie).Na przykład: Twoja lokalna strefa czasowa to GMT + 2. Przechowujesz „2012-12-25 10:00:00 UTC”. Rzeczywista wartość przechowywana w bazie danych to „2012-12-25 12:00:00”. Pobierasz go ponownie: odzyskujesz go jako „2012-12-25 10:00:00 UTC” (ale tylko jeśli pobierasz go przy użyciu
getTimestamp(..)
), ale gdy inna aplikacja uzyskuje dostęp do bazy danych w strefie czasowej GMT + 0, pobierz znacznik czasu jako „2012-12-25 12:00:00 UTC”.Jeśli chcesz przechowywać go w innej strefie czasowej, musisz użyć
setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
instancji Kalendarza w wymaganej strefie czasowej. Po prostu upewnij się, że podczas pobierania wartości używasz również równoważnego gettera z tą samą strefą czasową (jeśli używaszTIMESTAMP
w bazie danych bez informacji o strefie czasowej).Więc zakładając, że chcesz zapisać aktualną strefę czasową GMT, musisz użyć:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); stmt.setTimestamp(11, tsSchedStartTime, cal);
Z JDBC 4.2 zgodny sterownik powinien obsługiwać
java.time.LocalDateTime
(ijava.time.LocalTime
) forTIMESTAMP
(iTIME
) throughget/set/updateObject
. Tejava.time.Local*
zajęcia są bez stref czasowych, więc konwersja nie musi być stosowane (choć może otworzyć nowy zestaw problemów, jeśli kod nie przybierały określoną strefę czasową).źródło
toString()
Najava.lang.Date
(ijava.lang.Timestamp
) będą zawsze przedstawić go w lokalnej strefy czasowej.Myślę, że poprawną odpowiedzią powinno być java.sql.Timestamp NIE jest specyficzne dla strefy czasowej. Sygnatura czasowa jest złożeniem wartości java.util.Date i oddzielnej wartości w nanosekundach. W tych zajęciach nie ma informacji o strefie czasowej. Tak więc, podobnie jak Date, ta klasa po prostu przechowuje liczbę milisekund od 1 stycznia 1970, 00:00:00 GMT + nanos.
W PreparedStatement.setTimestamp (int parameterIndex, Timestamp x, Calendar cal) Kalendarz jest używany przez sterownik do zmiany domyślnej strefy czasowej. Ale Timestamp nadal zawiera milisekundy w GMT.
API nie jest jasne, jak dokładnie sterownik JDBC ma używać Kalendarza. Wydaje się, że dostawcy czują się swobodnie, jeśli chodzi o jego interpretację, np. Kiedy ostatnio pracowałem z kalendarzem MySQL 5.5, sterownik po prostu zignorował Kalendarz w obu PreparedStatement.setTimestamp i ResultSet.getTimestamp.
źródło
W przypadku MySQL mamy ograniczenie. W sterowniku Mysql doc mamy:
useTimezone=true useLegacyDatetimeCode=false serverTimezone=UTC
Tak więc, gdy nie używamy tych parametrów i dzwonimy
setTimestamp or getTimestamp
z kalendarzem lub bez kalendarza, mamy znacznik czasu w strefie czasowej jvm.Przykład:
Strefa czasowa jvm to GMT + 2. W bazie danych mamy sygnaturę czasową: 1461100256 = 19/04/16 21: 10: 56,000000000 GMT
Properties props = new Properties(); props.setProperty("user", "root"); props.setProperty("password", ""); props.setProperty("useTimezone", "true"); props.setProperty("useLegacyDatetimeCode", "false"); props.setProperty("serverTimezone", "UTC"); Connection con = DriverManager.getConnection(conString, props); ...... Calendar nowGMT = Calendar.getInstance(TimeZone.getTimeZone("GMT")); Calendar nowGMTPlus4 = Calendar.getInstance(TimeZone.getTimeZone("GMT+4")); ...... rs.getTimestamp("timestampColumn");//Oracle driver convert date to jvm timezone and Mysql convert date to GMT (specified in the parameter) rs.getTimestamp("timestampColumn", nowGMT);//convert date to GMT rs.getTimestamp("timestampColumn", nowGMTPlus4);//convert date to GMT+4 timezone
Pierwsza metoda zwraca: 1461100256000 = 19/04/2016 - 21:10:56 GMT
Druga metoda zwraca: 1461100256000 = 19/04/2016 - 21:10:56 GMT
Trzecia metoda zwraca: 1461085856000 = 19/04/2016 - 17:10:56 GMT
Zamiast Oracle, gdy używamy tych samych wywołań, mamy:
Pierwsza metoda zwraca: 1461093056000 = 19/04/2016 - 19:10:56 GMT
Druga metoda zwraca: 1461100256000 = 19/04/2016 - 21:10:56 GMT
Trzecia metoda zwraca: 1461085856000 = 19/04/2016 - 17:10:56 GMT
Uwaga: nie ma potrzeby określania parametrów Oracle.
źródło
ALTER SESSION SET TIME_ZONE = 'UTC'
”.Jest to specyficzne dla twojego kierowcy. Musisz podać parametr w programie Java, aby określić strefę czasową, której chcesz użyć.
java -Duser.timezone="America/New_York" GetCurrentDateTimeZone
Dalej to:
to_char(new_time(sched_start_time, 'CURRENT_TIMEZONE', 'NEW_TIMEZONE'), 'MM/DD/YY HH:MI AM')
Może również mieć znaczenie przy prawidłowym przeprowadzaniu konwersji. Zaczerpnięte stąd
źródło
Odpowiedź brzmi:
java.sql.Timestamp
to bałagan i należy go unikać. Użyjjava.time.LocalDateTime
zamiast tego.Więc dlaczego to bałagan? Z
java.sql.Timestamp
JavaDoc, ajava.sql.Timestamp
jest „cienkim opakowaniem,java.util.Date
które umożliwia interfejsowi API JDBC zidentyfikowanie tego jako wartości TIMESTAMP języka SQL”. Wjava.util.Date
JavaDoc „Date
klasa ma odzwierciedlać uniwersalny czas koordynowany (UTC)”. Ze specyfikacji ISO SQL TIMESTAMP BEZ STREFY CZASOWEJ „jest typem danych, który jest datą i godziną bez strefy czasowej”. TIMESTAMP to krótka nazwa TIMESTAMP BEZ STREFY CZASOWEJ. Zatemjava.sql.Timestamp
„odzwierciedla” UTC, podczas gdy SQL TIMESTAMP jest „bez strefy czasowej”.Ponieważ
java.sql.Timestamp
odzwierciedla UTC, jego metody stosują konwersje. To powoduje niekończące się zamieszanie. Z punktu widzenia języka SQL nie ma sensu przekształcanie wartości TIMESTAMP języka SQL na inną strefę czasową, ponieważ TIMESTAMP nie ma strefy czasowej, z której można by dokonać konwersji. Co to znaczy przeliczyć 42 stopnie Fahrenheita? To nic nie znaczy, ponieważ 42 nie ma jednostek temperatury. To tylko pusta liczba. Podobnie nie możesz przekonwertować TIMESTAMP z 2020-07-22T10: 38: 00 na Ameryki / Los Angeles, ponieważ 2020-07-22T10: 30: 00 nie znajduje się w żadnej strefie czasowej. To nie jest UTC, GMT ani nic innego. To nagi czas na randkę.java.time.LocalDateTime
jest również samą datą. Nie ma strefy czasowej, dokładnie tak jak SQL TIMESTAMP. Żadna z jego metod nie stosuje żadnego rodzaju konwersji stref czasowych, co znacznie ułatwia przewidywanie i zrozumienie jego zachowania. Więc nie używajjava.sql.Timestamp
. Posługiwać sięjava.time.LocalDateTime
.LocalDateTime ldt = rs.getObject(col, LocalDateTime.class); ps.setObject(param, ldt, JDBCType.TIMESTAMP);
źródło
Możesz użyć poniższej metody, aby zapisać sygnaturę czasową w bazie danych specyficzną dla żądanej strefy / identyfikatora strefy.
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Calcutta")) ; Timestamp timestamp = Timestamp.valueOf(zdt.toLocalDateTime());
Częstym błędem jest używanie
LocaleDateTime
uzyskanie sygnatury czasowej tej chwili, która odrzuca wszelkie informacje dotyczące Twojej strefy, nawet jeśli spróbujesz je później przekonwertować. Nie rozumie Strefy.Należy pamiętać, że
Timestamp
jest z klasyjava.sql.Timestamp
.źródło