Paksa zona waktu Java sebagai GMT / UTC

97

Saya perlu memaksakan operasi terkait waktu apa pun ke GMT / UTC, terlepas dari zona waktu yang disetel pada mesin. Adakah cara mudah untuk melakukannya dalam kode?

Untuk memperjelas, saya menggunakan waktu server DB untuk semua operasi, tetapi hasilnya diformat sesuai dengan zona waktu lokal.

Terima kasih!

SyBer
sumber
Sebenarnya, masalahnya adalah bagian dari masalah saya, tetapi saya menemukan solusinya.
SyBer
Lihat pertanyaan duplikat, dan cari "Joda" dan "DateTimeZone".
Basil Bourque

Jawaban:

126

OP menjawab pertanyaan ini untuk mengubah zona waktu default untuk satu instance JVM yang sedang berjalan, setel user.timezoneproperti sistem:

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

Jika Anda perlu menyetel zona waktu tertentu saat mengambil objek Tanggal / Waktu / Timestamp dari database ResultSet, gunakan bentuk kedua dari getXXXmetode yang mengambil Calendarobjek:

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

Atau, mengatur tanggal di PreparedStatement:

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

Ini akan memastikan bahwa nilai yang disimpan dalam database konsisten ketika kolom database tidak menyimpan informasi zona waktu.

The java.util.Datedan java.sql.Datekelas menyimpan waktu yang sebenarnya (milidetik) di UTC. Untuk memformatnya pada keluaran ke zona waktu lain, gunakan SimpleDateFormat. Anda juga dapat mengaitkan zona waktu dengan nilai menggunakan objek Kalender:

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

Referensi Berguna

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
sumber
Satu hal yang perlu diperhatikan tentang menyetel zona waktu untuk seluruh JVM adalah hal itu memengaruhi segalanya, termasuk hal-hal seperti logging. Hanya sesuatu yang perlu diingat jika itu adalah efek yang diinginkan.
Hermann Hans
Ya, itu bagus. Hanya satu hal lagi, bahwa saya sebenarnya mengatur user.timezone langsung dalam kode, daripada melalui parameter -D.
SyBer
6
@SyBer: Itu berhasil tetapi Anda harus memastikan bahwa Anda menyetel ini sebelum metode apa pun memanggil metode TimeZone.getDefault (). Jika tidak, Anda akan bingung karena default di-cache setelah eksekusi pertama. Ini juga berarti bisa menjadi masalah jika Anda melakukan ini di dalam API / perpustakaan (tersembunyi dari tampilan biasa) - pastikan untuk mendokumentasikan apa yang Anda lakukan.
Kevin Brock
23

Juga jika Anda dapat mengatur zona waktu JVM dengan cara ini

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

atau -Duser.timezone=GMTdi args JVM.

Pengembang MG
sumber
System.setProperty("user.timezone" ...)tidak bekerja.
wilmol
10

Anda dapat mengubah zona waktu menggunakan TimeZone.setDefault () :

TimeZone.setDefault(TimeZone.getTimeZone("GMT"))
snorbi
sumber
Jangan tersinggung, tetapi mengubah sementara keadaan statis global terdengar seperti ide yang sangat buruk ...
G. Demecki
Bisa, tapi tidak boleh. Ini adalah nasihat yang sangat buruk. Zona waktunya seluruh JVM telah berubah.
scot
1
Terkadang itulah yang ingin Anda capai. Misalnya. Saya telah melihat layanan mikro berbasis Java berjalan selalu dengan UTC untuk menghindari masalah zona waktu. Dalam sistem ini, backend database juga berjalan dengan UTC, jadi wajar untuk "memaksa" JVM ke UTC juga. Yang penting: Anda harus tahu apa yang Anda lakukan dan apa konsekuensinya ...
snorbi
benar-benar tidak. Jika Anda ingin seluruh sistem Anda dalam UTC (ide yang sangat bagus), setel zona waktu sistem ke UTC dan biarkan zona waktu Java saja.
scot
3
Kecuali jika Anda memiliki beberapa JVM dengan persyaratan berbeda. Kami bisa berdebat selamanya, tetapi setiap kasus unik: terkadang Anda perlu menyetel seluruh zona waktu sistem, terkadang Anda hanya menyetel satu JVM. Mari kita berbahagia bahwa sistem saat ini begitu fleksibel sehingga kita dapat melakukan keduanya 😉
snorbi
8

bagi saya, SimpleDateFormat cepat,

  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"));
  }

lalu format tanggal dengan zona waktu yang berbeda.

lwpro2
sumber
6

Saya akan mengambil waktu dari DB dalam bentuk mentah (stempel waktu lama atau Tanggal java), dan kemudian menggunakan SimpleDateFormat untuk memformatnya, atau Kalender untuk memanipulasinya. Dalam kedua kasus, Anda harus menyetel zona waktu objek sebelum menggunakannya.

Lihat SimpleDateFormat.setTimeZone(..)dan Calendar.setTimeZone(..)untuk detailnya

Eyal Schneider
sumber
6

tl; dr

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

Hindari bergantung pada OS host atau JVM untuk zona waktu default

Saya sarankan Anda menulis semua kode Anda untuk secara eksplisit menyatakan zona waktu yang diinginkan / diharapkan. Anda tidak perlu bergantung pada zona waktu default JVM saat ini. Dan ketahuilah bahwa zona waktu default JVM saat ini dapat berubah kapan saja selama waktu proses , dengan kode apa pun di utas apa pun dari panggilan aplikasi apa pun TimeZone.setDefault. Panggilan semacam itu segera memengaruhi semua aplikasi dalam JVM tersebut.

java.time

Beberapa Jawaban lain benar, tetapi sekarang sudah ketinggalan zaman. Kelas tanggal-waktu yang mengerikan yang digabungkan dengan versi Java yang paling awal memiliki cacat, ditulis oleh orang-orang yang tidak memahami kerumitan dan seluk-beluk penanganan waktu-tanggal.

Kelas tanggal-waktu warisan telah digantikan oleh kelas java.time yang ditentukan di JSR 310.

Untuk mewakili zona waktu, gunakan ZoneId. Untuk merepresentasikan offset-from-UTC, gunakan ZoneOffset. Offset hanyalah beberapa jam-menit-detik di depan atau di belakang meridian utama. Zona waktu lebih dari itu. Zona waktu adalah sejarah perubahan masa lalu, sekarang, dan masa depan untuk offset yang digunakan oleh orang-orang di wilayah tertentu.

Saya perlu memaksakan operasi terkait waktu ke GMT / UTC

Untuk offset nol jam-menit-detik, gunakan konstanta ZoneOffset.UTC.

Instant

Untuk menangkap momen saat ini di UTC, gunakan Instant. Kelas ini mewakili momen dalam UTC, selalu dalam UTC menurut definisi.

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

ZonedDateTime

Untuk melihat momen yang sama melalui jam dinding yang digunakan oleh orang-orang di wilayah tertentu, sesuaikan dengan zona waktu. Momen yang sama, titik yang sama di garis waktu, waktu jam dinding yang berbeda.

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

Database

Anda menyebutkan database.

Untuk mengambil momen dari database, kolom Anda harus dari tipe data yang mirip dengan standar SQL TIMESTAMP WITH TIME ZONE. Mengambil objek, bukan string. Di JDBC 4.2 dan yang lebih baru, kita dapat bertukar objek java.time dengan database. The OffsetDateTimediperlukan oleh JDBC, sementara Instant& ZonedDateTimeadalah opsional.

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

Di sebagian besar database dan driver, saya rasa Anda akan mendapatkan momen seperti yang terlihat di UTC. Namun jika tidak, Anda dapat menyesuaikan dengan salah satu dari dua cara berikut:

  • Ekstrak a Instant: odt.toInstant()
  • Sesuaikan dari offset yang diberikan ke offset nol: OffsetDateTime odtUtc = odt.withOffsetSameInstant (ZoneOffset.UTC); `.

Tabel tipe tanggal-waktu di Java (legacy dan modern) dan dalam SQL standar


Tentang java.time

The java.time kerangka dibangun ke Jawa 8 dan kemudian. Kelas-kelas ini menggantikan tua merepotkan warisan kelas tanggal-waktu seperti java.util.Date, Calendar, & SimpleDateFormat.

Untuk mempelajari lebih lanjut, lihat Tutorial Oracle . Dan cari Stack Overflow untuk banyak contoh dan penjelasan. Spesifikasinya adalah JSR 310 .

Proyek Joda-Time , sekarang dalam mode pemeliharaan , menyarankan migrasi ke kelas java.time .

Anda dapat bertukar objek java.time langsung dengan database Anda. Gunakan driver JDBC yang sesuai dengan JDBC 4.2 atau yang lebih baru. Tidak perlu string, tidak perlu java.sql.*kelas.

Dimana mendapatkan kelas java.time?

Basil Bourque
sumber
1

buat sepasang klien / server, sehingga setelah eksekusi, server klien mengirimkan waktu dan tanggal yang benar. Kemudian, klien meminta server pm GMT dan server mengirimkan kembali jawabannya dengan benar.

bertan
sumber
1

Gunakan properti sistem:

-Duser.timezone=UTC
Dawid Stępień
sumber
5
Mengapa seseorang harus menggunakan itu? Harap tambahkan beberapa penjelasan pada jawaban Anda sehingga orang lain dapat belajar darinya
Nico Haase
1

Jalankan baris ini di MySQL untuk mengatur ulang zona waktu Anda:

SET @@global.time_zone = '+00:00';
Saeed
sumber
0

Wow. Saya tahu ini adalah utas kuno tetapi yang bisa saya katakan hanyalah jangan memanggil TimeZone.setDefault () dalam kode tingkat pengguna mana pun. Ini selalu menetapkan Zona Waktu untuk seluruh JVM dan hampir selalu merupakan ide yang sangat buruk. Pelajari cara menggunakan perpustakaan joda.time atau kelas DateTime baru di Java 8 yang sangat mirip dengan perpustakaan joda.time.

scot
sumber
-6

Jika Anda ingin mendapatkan waktu GMT hanya dengan 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
sumber
5
yang terlihat seperti JavaScript (! = Java) bagi saya.
Phil