Wymuś strefę czasową Java jako GMT / UTC

100

Muszę wymusić wszystkie operacje związane z czasem na GMT / UTC, niezależnie od strefy czasowej ustawionej na komputerze. Jakikolwiek wygodny sposób na to w kodzie?

Aby wyjaśnić, używam czasu serwera DB do wszystkich operacji, ale wychodzi on sformatowany zgodnie z lokalną strefą czasową.

Dzięki!

SyBer
źródło
Właściwie jego problem jest podzbiorem moich, ale znalazłem rozwiązanie.
SyBer
Spójrz na zduplikowane pytanie i wyszukaj „Joda” i „DateTimeZone”.
Basil Bourque,

Odpowiedzi:

127

OP odpowiedział na to pytanie, aby zmienić domyślną strefę czasową dla pojedynczej instancji działającej maszyny JVM, ustawić user.timezonewłaściwość systemu:

java -Duser.timezone=GMT ... <main-class>

Jeśli potrzebujesz ustawić określone strefy czasowe podczas pobierania obiektów Data / Czas / Znacznik czasu z bazy danych ResultSet, użyj drugiej formy getXXXmetod, które przyjmują Calendarobiekt:

Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
ResultSet rs = ...;
while (rs.next()) {
    Date dateValue = rs.getDate("DateColumn", tzCal);
    // Other fields and calculations
}

Lub ustawiając datę w PreparedStatement:

Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
PreparedStatement ps = conn.createPreparedStatement("update ...");
ps.setDate("DateColumn", dateValue, tzCal);
// Other assignments
ps.executeUpdate();

Zapewni to spójność wartości przechowywanej w bazie danych, gdy kolumna bazy danych nie przechowuje informacji o strefie czasowej.

java.util.DateI java.sql.Datezajęcia zapisać aktualny czas (w milisekundach) UTC. Aby sformatować je na wyjściu w innej strefie czasowej, użyj SimpleDateFormat. Możesz również powiązać strefę czasową z wartością za pomocą obiektu Calendar:

TimeZone tz = TimeZone.getTimeZone("<local-time-zone>");
//...
Date dateValue = rs.getDate("DateColumn");
Calendar calValue = Calendar.getInstance(tz);
calValue.setTime(dateValue);

Przydatne odniesienie

https://docs.oracle.com/javase/9/troubleshoot/time-zone-settings-jre.htm#JSTGD377

https://confluence.atlassian.com/kb/setting-the-timezone-for-the-java-environment-841187402.html

Kevin Brock
źródło
Jedną rzeczą, na którą należy zwrócić uwagę przy ustawianiu strefy czasowej dla całej maszyny JVM, jest to, że wpływa ona na wszystko, w tym na takie rzeczy, jak rejestrowanie. Po prostu coś, o czym należy pamiętać, jeśli jest to pożądany efekt.
Hermann Hans
Tak, w porządku. Jeszcze tylko jedna kwestia, aby wspomnieć, że tak naprawdę ustawiłem user.timezone bezpośrednio w kodzie, a nie za pomocą parametru -D.
SyBer
6
@SyBer: To działa, ale musisz upewnić się, że ustawiłeś to przed wywołaniem metody TimeZone.getDefault () przez jakąkolwiek metodę. Jeśli nie, będziesz miał trochę zamieszania, ponieważ wartość domyślna jest buforowana po pierwszym wykonaniu. Oznacza to również, że może to być problem, jeśli zrobisz to w API / bibliotece (ukrytej przed zwykłym widokiem) - pamiętaj, aby dokładnie udokumentować to, co robisz.
Kevin Brock,
23

Również jeśli możesz ustawić strefę czasową JVM w ten sposób

System.setProperty("user.timezone", "EST");

lub -Duser.timezone=GMTw argumentach JVM.

MG Developer
źródło
System.setProperty("user.timezone" ...)nie działa.
wilmol
11

Możesz zmienić strefę czasową za pomocą TimeZone.setDefault () :

TimeZone.setDefault(TimeZone.getTimeZone("GMT"))
snorbi
źródło
Bez obrazy, ale tymczasowa modyfikacja globalnego stanu statycznego brzmi jak bardzo zły pomysł ...
G. Demecki
Możesz, ale nie powinieneś. To wyjątkowo zła rada. Strefa czasowa całej maszyny JVM uległa zmianie.
scot
1
Czasami jest to dokładnie to, co chcesz osiągnąć. Na przykład. Widziałem mikrousługi oparte na Javie, działające zawsze z czasem UTC, aby uniknąć problemów ze strefą czasową. W tych systemach zaplecze bazy danych również działa z czasem UTC, więc naturalne jest „wymuszenie” również na JVM czasu UTC. Co ważne: musisz wiedzieć, co robisz i jakie są tego konsekwencje ...
snorbi
absolutnie nie. Jeśli chcesz, aby cały system był w UTC (bardzo dobry pomysł), ustaw strefę czasową systemu na UTC i zostaw strefę czasową Java w spokoju.
scot
3
Z wyjątkiem sytuacji, gdy masz wiele maszyn JVM z różnymi wymaganiami. Możemy kłócić się w nieskończoność, ale każdy przypadek jest wyjątkowy: czasami trzeba ustawić strefę czasową całego systemu, a czasem tylko jedną maszynę JVM. Cieszmy się, że dzisiejsze systemy są tak elastyczne, że możemy zrobić jedno i drugie 😉
snorbi,
8

dla mnie tylko szybki SimpleDateFormat,

  private static final SimpleDateFormat GMT = new SimpleDateFormat("yyyy-MM-dd");
  private static final SimpleDateFormat SYD = new SimpleDateFormat("yyyy-MM-dd");
  static {
      GMT.setTimeZone(TimeZone.getTimeZone("GMT"));
    SYD.setTimeZone(TimeZone.getTimeZone("Australia/Sydney"));
  }

następnie sformatuj datę, używając innej strefy czasowej.

lwpro2
źródło
7

tl; dr

String sql = "SELECT CURRENT_TIMESTAMP ;
…
OffsetDateTime odt = myResultSet.getObject( 1 , OffsetDateTime.class ) ;

Unikaj zależności od systemu operacyjnego hosta lub maszyny JVM dla domyślnej strefy czasowej

Zalecam napisanie całego kodu w celu jawnego określenia żądanej / oczekiwanej strefy czasowej. Nie musisz polegać na bieżącej domyślnej strefie czasowej maszyny JVM. Należy również pamiętać, że bieżąca domyślna strefa czasowa maszyny JVM może ulec zmianie w dowolnym momencie podczas działania, z dowolnym kodem w dowolnym wątku wywołania dowolnej aplikacji TimeZone.setDefault. Takie wywołanie ma natychmiastowy wpływ na wszystkie aplikacje w tej maszynie JVM.

java.time

Niektóre z pozostałych odpowiedzi były poprawne, ale obecnie są przestarzałe. Okropne klasy data-czas dołączone do najwcześniejszych wersji Javy były błędne, napisane przez ludzi, którzy nie rozumieli złożoności i subtelności obsługi daty i godziny.

Starsze klasy daty i godziny zostały zastąpione przez klasy java.time zdefiniowane w JSR 310.

Aby przedstawić strefę czasową, użyj ZoneId. Aby przedstawić przesunięcie od czasu UTC, użyj ZoneOffset. Przesunięcie to zaledwie kilka godzin-minut-sekund przed lub za głównym południkiem. Strefa czasowa to znacznie więcej. Strefa czasowa to historia przeszłych, obecnych i przyszłych zmian offsetu używanego przez mieszkańców określonego regionu.

Muszę wymusić wszystkie operacje związane z czasem na GMT / UTC

Aby uzyskać przesunięcie równe zero godzin-minut-sekund, użyj stałej ZoneOffset.UTC.

Instant

Aby uchwycić aktualny moment w UTC, użyj pliku Instant. Ta klasa reprezentuje moment w UTC, z definicji zawsze w UTC.

Instant instant = Instant.now() ;  // Capture current moment in UTC.

ZonedDateTime

Aby zobaczyć ten sam moment przez zegar ścienny używany przez mieszkańców danego regionu, dostosuj się do strefy czasowej. Ten sam moment, ten sam punkt na osi czasu, inny zegar ścienny.

ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

Baza danych

Wspominasz o bazie danych.

Aby pobrać moment z bazy danych, twoja kolumna powinna być typu danych zbliżonego do standardu SQL TIMESTAMP WITH TIME ZONE. Pobierz obiekt, a nie ciąg. W JDBC 4.2 i nowszych możemy wymieniać obiekty java.time z bazą danych. OffsetDateTimeJest wymagane przez JDBC, podczas Instanti ZonedDateTimesą opcjonalne.

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

Sądzę, że w większości baz danych i sterowników uzyskasz moment taki jak w UTC. Ale jeśli nie, możesz to zmienić na dwa sposoby:

  • Wyodrębnij Instant: odt.toInstant()
  • Dostosuj od podanego przesunięcia do przesunięcia zerowego: OffsetDateTime odtUtc = odt.withOffsetSameInstant (ZoneOffset.UTC); `.

Tabela typów daty i godziny w Javie (zarówno starszej, jak i nowoczesnej) oraz w standardowym języku SQL


Informacje o java.time

Struktura java.time jest wbudowana w Javę 8 i nowsze. Klasy te kłopotliwe zastąpić stary starszych klas Date-Time, takich jak java.util.Date, Calendar, i SimpleDateFormat.

Aby dowiedzieć się więcej, zobacz samouczek Oracle . I przeszukaj Stack Overflow, aby znaleźć wiele przykładów i wyjaśnień. Specyfikacja to JSR 310 .

Projekt Joda-Time , obecnie w trybie konserwacji , zaleca migrację do klas java.time .

Możesz wymieniać obiekty java.time bezpośrednio ze swoją bazą danych. Użyj sterownika JDBC zgodnego z JDBC 4.2 lub nowszym. Nie ma potrzeby stosowania ciągów ani java.sql.*klas.

Skąd wziąć klasy java.time?

Basil Bourque
źródło
6

Pobrałbym czas z bazy danych w postaci surowej (długi znacznik czasu lub data java), a następnie użyłbym SimpleDateFormat do sformatowania go lub Kalendarza, aby nim manipulować. W obu przypadkach należy ustawić strefę czasową obiektów przed ich użyciem.

Zobacz SimpleDateFormat.setTimeZone(..)i Calendar.setTimeZone(..)po szczegóły

Eyal Schneider
źródło
1

stwórz parę klient / serwer, tak aby po wykonaniu klient serwer wysyłał poprawny czas i datę. Następnie klient pyta serwer o godz. GMT, a serwer odsyła prawidłową odpowiedź.

bertan
źródło
1

Użyj właściwości systemu:

-Duser.timezone=UTC
Dawid Stępień
źródło
5
Dlaczego należy tego używać? Dodaj wyjaśnienie do swojej odpowiedzi, aby inni mogli się z niej nauczyć
Nico Haase
1

Wykonaj tę linię w MySQL, aby zresetować swoją strefę czasową:

SET @@global.time_zone = '+00:00';
Saeed
źródło
0

Łał. Wiem, że to starożytny wątek, ale wszystko, co mogę powiedzieć, to nie wywoływać TimeZone.setDefault () w żadnym kodzie na poziomie użytkownika. To zawsze ustawia strefę czasową dla całej maszyny JVM i prawie zawsze jest to bardzo zły pomysł. Naucz się korzystać z biblioteki joda.time lub nowej klasy DateTime w Javie 8, która jest bardzo podobna do biblioteki joda.time.

Szkot
źródło
0

Mam nadzieję, że poniższy kod ci pomoże.

    DateFormat formatDate = new SimpleDateFormat(ISO_DATE_TIME_PATTERN);
    formatDate.setTimeZone(TimeZone.getTimeZone("UTC")); 
    formatDate.parse(dateString);
Vijay Kesanupalli
źródło
-6

Jeśli chcesz uzyskać czas GMT tylko za pomocą intiger: var currentTime = new Date (); var currentYear = '2010' var currentMonth = 10; var currentDay = '30 'var currentHours = '20' var currentMinutes = '20 'var currentSeconds = '00' var currentMilliseconds = '00 '

currentTime.setFullYear(currentYear);
currentTime.setMonth((currentMonth-1)); //0is January
currentTime.setDate(currentDay);  
currentTime.setHours(currentHours);
currentTime.setMinutes(currentMinutes);
currentTime.setSeconds(currentSeconds);
currentTime.setMilliseconds(currentMilliseconds);

var currentTimezone = currentTime.getTimezoneOffset();
currentTimezone = (currentTimezone/60) * -1;
var gmt ="";
if (currentTimezone !== 0) {
  gmt += currentTimezone > 0 ? ' +' : ' ';
  gmt += currentTimezone;
}
alert(gmt)
Incmos
źródło
5
wygląda mi to na JavaScript (! = Java).
Phil