Strategi apa pun untuk menyimpan data tanggal dan waktu di PostgreSQL harus, IMO, bergantung pada dua poin ini:
- Solusi Anda seharusnya tidak pernah bergantung pada pengaturan zona waktu server atau klien.
- Saat ini, PostgreSQL (seperti kebanyakan database) tidak memiliki tipe data untuk menyimpan penuh tanggal-dan-waktu dengan zona waktu. Jadi, Anda perlu memutuskan antara
Instant
atauLocalDateTime
tipe data.
Resep saya berikut.
Jika Anda ingin merekam fisik instan pada saat peristiwa tertentu terjadi, ("stempel waktu . yang sebenarnya " , biasanya beberapa peristiwa pembuatan/modifikasi/penghapusan), lalu gunakan:
- Java:
Instant
(Java 8 , atau Jodatime). - JDBC:
java.sql.Timestamp
- PostgreSQL:
TIMESTAMP WITH TIMEZONE
(TIMESTAMPTZ
)
(Jangan biarkan PostgreSQL tipe data aneh WITH TIMEZONE
/WITHOUT TIMEZONE
membingungkan Anda:tidak ada yang benar-benar menyimpan zona waktu)
Beberapa kode boilerplate:berikut ini mengasumsikan bahwa ps
adalah PreparedStatement
, rs
sebuah ResultSet
dan tzUTC
adalah Calendar
statis objek yang sesuai dengan UTC
zona waktu.
public static final Calendar tzUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Tulis Instant
ke database TIMESTAMPTZ
:
Instant instant = ...;
Timestamp ts = instant != null ? Timestamp.from(instant) : null;
ps.setTimestamp(col, ts, tzUTC); // column is TIMESTAMPTZ!
Baca Instant
dari basis data TIMESTAMPTZ
:
Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ
Instant inst = ts !=null ? ts.toInstant() : null;
Ini bekerja dengan aman jika tipe PG Anda adalah TIMESTAMPTZ
(Dalam hal ini, calendarUTC
tidak berpengaruh dalam kode itu; tetapi selalu disarankan untuk tidak bergantung pada zona waktu default).."Aman" berarti bahwa hasilnya tidak bergantung pada zona waktu server atau basis data , atau informasi zona waktu:operasi ini sepenuhnya dapat dibalik, dan apa pun yang terjadi pada pengaturan zona waktu, Anda akan selalu mendapatkan "waktu instan" yang sama dengan yang Anda miliki di sisi Java.
Jika, alih-alih stempel waktu (seketika pada garis waktu fisik), Anda berurusan dengan waktu-tanggal lokal "sipil" (yaitu, kumpulan bidang {year-month-day hour:min:sec(:msecs)}
), Anda akan menggunakan:
- Java:
LocalDateTime
(Java 8 , atau Jodatime). - JDBC:
java.sql.Timestamp
- PostgreSQL:
TIMESTAMP WITHOUT TIMEZONE
(TIMESTAMP
)
Baca LocalDateTime
dari basis data TIMESTAMP
:
Timestamp ts = rs.getTimestamp(col, tzUTC); //
LocalDateTime localDt = null;
if( ts != null )
localDt = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC);
Tulis LocalDateTime
ke database TIMESTAMP
:
Timestamp ts = null;
if( localDt != null)
ts = new Timestamp(localDt.toInstant(ZoneOffset.UTC).toEpochMilli()), tzUTC);
ps.setTimestamp(colNum,ts, tzUTC);
Sekali lagi, strategi ini aman dan Anda dapat tidur nyenyak:jika Anda menyimpan 2011-10-30 23:59:30
, Anda akan selalu mengambil bidang yang tepat tersebut (jam=23, menit=59... dll) selalu, apa pun yang terjadi - bahkan jika besok zona waktu server Postgresql (atau klien) Anda berubah, atau JVM atau zona waktu OS Anda, atau jika negara Anda mengubah aturan DST-nya, dll.
Ditambahkan:Jika Anda ingin (tampaknya merupakan persyaratan alami) untuk menyimpan spesifikasi datetime lengkap (ZonedDatetime
:stempel waktu bersama dengan zona waktu, yang secara implisit juga mencakup info tanggal waktu sipil lengkap - ditambah zona waktu)... . Anda harus merancang penyimpanan Anda sendiri, mungkin dalam sepasang bidang:bisa dua jenis di atas (sangat berlebihan, meskipun efisien untuk pengambilan dan perhitungan), atau salah satunya ditambah offset waktu (Anda kehilangan info zona waktu, beberapa perhitungan menjadi sulit, dan beberapa tidak mungkin), atau salah satunya ditambah zona waktu (sebagai string; beberapa perhitungan bisa sangat mahal).