Baca Pertanyaan Praktik terbaik waktu musim panas dan zona waktu. Milik Anda pada dasarnya adalah duplikat.
Server dalam UTC
Ya, umumnya server harus mengatur OS mereka ke UTC sebagai zona waktu, atau jika tidak disediakan gunakan GMT
atau zona waktu Islandia Reykjavík. Implementasi Java Anda mungkin mengambil pengaturan ini sebagai zona waktu default saat ini.
Tentukan zona waktu
Tetapi jangan bergantung pada zona waktu yang disetel ke UTC. Seorang sysadmin dapat mengubahnya. Dan kode Java apa pun di utas aplikasi apa pun dalam JVM Anda dapat mengubah zona waktu default JVM saat ini saat runtime dengan memanggil TimeZone.setDefault
. Jadi, sebagai gantinya, biasakan untuk selalu menentukan zona waktu yang diinginkan/diharapkan dengan meneruskan argumen opsional dalam kode Java Anda.
Saya menganggapnya sebagai cacat desain bahwa kerangka tanggal-waktu apa pun akan membuat zona waktu opsional. Menjadi opsional menciptakan kebingungan yang tak ada habisnya karena programmer, seperti orang lain, secara tidak sadar berpikir dalam zona waktu pribadi mereka sendiri kecuali diminta. Jadi terlalu sering dalam pekerjaan tanggal-waktu tidak ada perhatian yang diberikan pada masalah ini. Tambahkan masalah bahwa default JVM bervariasi. Omong-omong, begitu juga untuk Locale
, masalah yang sama, harus selalu ditentukan secara eksplisit.
UTC
Logika bisnis Anda, penyimpanan data, dan pertukaran data harus hampir selalu dilakukan di UTC. Hampir setiap basis data memiliki fitur untuk menyesuaikan input apa pun ke dalam UTC dan menyimpannya dalam UTC.
Saat menyajikan tanggal-waktu kepada pengguna, sesuaikan dengan zona waktu yang diharapkan. Saat membuat serial nilai tanggal-waktu, gunakan format string ISO 8601. Lihat Jawaban oleh VickyArora untuk Oracle secara khusus (saya adalah orang Postgres). Pastikan untuk membaca dokumen dengan cermat, dan berlatihlah dengan bereksperimen untuk memahami sepenuhnya perilaku basis data Anda. Spesifikasi SQL tidak menjelaskan banyak hal dalam hal ini, dan perilakunya sangat bervariasi.
java.sql
Ingat bahwa saat menggunakan Java dan JDBC, Anda akan menggunakan java.sql.Timestamp
dan tipe data terkait. Mereka selalu dalam UTC, secara otomatis. Di masa mendatang, harapkan untuk melihat driver JDBC diperbarui untuk langsung menggunakan tipe data baru yang ditentukan dalam kerangka kerja java.time yang dibangun ke dalam Java 8 dan yang lebih baru.
java.time
Kelas lama sudah ketinggalan zaman oleh Java.time. Belajar menggunakan java.time sambil menghindari java.util.Date/.Calendar lama dan membuat kehidupan pemrograman Anda jauh lebih menyenangkan.
Sampai driver JDBC Anda diperbarui, Anda dapat menggunakan metode kemudahan konversi yang ada di dalam java.time. Lihat contoh berikutnya, di mana Instant
adalah momen dalam UTC dan ZonedDateTime
adalah Instan yang disesuaikan dengan zona waktu.
Instant instant = myJavaSqlTimestamp.toInstant();
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Untuk pergi ke arah lain.
java.sql.Timestamp myJavaSqlTimestamp = java.sql.Timestamp.from( zdt.toInstant() );
Jika Anda membutuhkan zona waktu asli, simpanlah
Jika persyaratan bisnis Anda menganggap zona waktu data input asli penting, untuk diingat, maka simpan secara eksplisit sebagai kolom terpisah di tabel database Anda. Anda dapat menggunakan offset-dari-UTC, tetapi itu tidak memberikan informasi lengkap. Zona waktu adalah offset plus seperangkat aturan untuk penanganan anomali di masa lalu, sekarang, dan masa depan seperti Waktu Musim Panas. Jadi nama zona waktu yang tepat adalah yang paling tepat seperti America/Montreal
.
Hanya tanggal tidak jelas
Anda mengatakan bahwa Anda mengumpulkan banyak nilai hanya tanggal, tanpa waktu dan tanpa zona waktu. Kelas untuk itu di java.time adalah LocalDate
. Seperti halnya LocalTime
dan LocalDateTime
, bagian "Lokal..." berarti tidak ada lokalitas tertentu, jadi tidak ada zona waktu, dan bukan titik pada garis waktu -- tidak memiliki arti sebenarnya.
Ingatlah bahwa nilai hanya-tanggal menurut definisinya ambigu. Pada saat tertentu, tanggal bervariasi di seluruh dunia. Misalnya, tepat setelah tengah malam di Paris Prancis adalah hari baru tetapi di Montréal Québec tanggalnya masih "kemarin".
Biasanya dalam bisnis beberapa zona waktu tersirat, bahkan secara tidak sadar terintuisi. Intuisi bawah sadar tentang titik data cenderung tidak bekerja dengan baik dalam jangka panjang, terutama dalam perangkat lunak. Lebih baik untuk membuat eksplisit zona waktu apa yang dimaksudkan. Anda bisa menyimpan zona yang dimaksud di samping tanggal seperti kolom lain di tabel database, atau Anda bisa membuat komentar di kode pemrograman Anda. Saya percaya akan jauh lebih baik dan lebih aman untuk menyimpan nilai tanggal-waktu. Jadi bagaimana kita mengubah tanggal-saja menjadi waktu-tanggal?
Seringkali hari baru adalah saat setelah tengah malam, momen pertama hari itu. Anda mungkin berpikir itu berarti waktu 00:00:00.0
tapi tidak selalu. Waktu Musim Panas (DST) dan kemungkinan anomali lainnya dapat mendorong momen pertama ke waktu jam dinding yang berbeda. Biarkan java.time menentukan waktu hari yang tepat untuk momen pertama melalui LocalDate
kelas dan atStartOfDay
metode.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( zoneId );
ZonedDateTime todayStart = today.atStartOfDay( zoneId );
Dalam beberapa konteks bisnis, hari baru dapat didefinisikan (atau diasumsikan) sebagai jam kerja. Misalnya, katakanlah penerbit di New York berarti jam 9 pagi di waktu lokal mereka ketika mereka mengatakan "draf buku akan jatuh tempo sebelum 2 Januari". Mari kita dapatkan waktu hari itu untuk tanggal itu di zona waktu itu.
ZoneId zoneId = ZoneId.of( "America/New_York" );
ZonedDateTime zdt = ZonedDateTime.of( 2016 , 1 , 2 , 9 , 0 , 0 , 0 , zoneId );
Apa artinya bagi penulis yang bekerja di Selandia Baru? Sesuaikan dengan zona waktu khusus untuk presentasi kepadanya dengan menelepon withZoneSameInstant
.
ZoneId zoneId_Pacific_Auckland = ZoneId.of( "Pacific/Auckland" );
ZonedDateTime zdt_Pacific_Auckland = zdt.withZoneSameInstant( zoneId_Pacific_Auckland );
Basis Data
Untuk penyimpanan database kami ubah menjadi Instant
(sesaat di timeline dalam UTC) dan lulus sebagai java.sql.Timestamp
seperti yang terlihat sebelumnya di atas.
java.sql.Timestamp ts = java.sql.Timestamp.from( zdt.toInstant() );
Saat diambil dari database, ubah kembali ke waktu tanggal New York. Konversi dari java.sql.Timestamp
ke Instant
, lalu terapkan zona waktu ZoneId
untuk mendapatkan ZonedDateTime
.
Instant instant = ts.toInstant();
ZoneId zoneId = ZoneId.of( "America/New_York" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Jika driver database Anda sesuai dengan JDBC 4.2 atau yang lebih baru, Anda mungkin dapat meneruskan/mengambil tipe java.time secara langsung daripada mengonversi ke/dari tipe java.sql. Coba PreparedStatement::setObject
dan ResultSet::getObject
metode.