Date
adalah agnostik zona waktu di Jawa. Itu selalu membutuhkan UTC (secara default dan selalu) tetapi ketika Date
/ Timestamp
dilewatkan melalui driver JDBC ke database, itu menafsirkan tanggal / waktu sesuai dengan zona waktu JVM yang default ke zona waktu sistem pada gilirannya (zona sistem operasi asli).
Oleh karena itu, kecuali jika driver MySQL JDBC secara eksplisit dipaksa untuk menggunakan zona UTC atau JVM sendiri disetel untuk menggunakan zona itu, driver tersebut tidak akan menyimpan Date
/ Timestamp
ke database target menggunakan UTC meskipun MySQL sendiri harus dikonfigurasi untuk menggunakan UTC menggunakan default_time_zone='+00:00'
di my.ini
atau my.cnf
di [mysqld]
bagian. Beberapa database seperti Oracle mungkin mendukung cap waktu dengan zona waktu dan mungkin pengecualian yang tidak saya kenal (belum diuji karena saya tidak memiliki lingkungan itu saat ini).
void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException
Ini selanjutnya dapat diklarifikasi dengan memeriksa permintaan setTimestampInternal()
metode implementasi driver MySQL JDBC.
Lihat dua
berikut ini panggilan ke setTimestampInternal()
metode dari dalam dua versi kelebihan beban setTimestamp()
metode.
Bila tidak ada Calendar
instance ditentukan dengan PreparedStatement#setTimestamp()
metode, zona waktu default akan digunakan (this.connection.getDefaultTimeZone()
).
Saat menggunakan kumpulan koneksi di server aplikasi / wadah Servlet yang didukung oleh koneksi / JNDI mengakses atau beroperasi pada sumber data seperti,
com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
(xa)com.mysql.jdbc.jdbc2.optional.MysqlDataSource
(non-xa)
driver MySQL JDBC perlu dipaksa untuk menggunakan zona waktu yang diinginkan (UTC), dua parameter berikut harus diberikan melalui string kueri dari URL koneksi.
Saya tidak akrab dengan sejarah driver MySQL JDBC tetapi dalam versi driver MySQL yang relatif lebih lama, parameter ini useLegacyDatetimeCode
mungkin tidak diperlukan. Jadi, seseorang mungkin perlu menyesuaikan diri dalam kasus itu.
Dalam hal server aplikasi, GlassFish, misalnya, mereka dapat diatur saat membuat ranah JDBC bersama dengan kumpulan koneksi JDBC di dalam server itu sendiri bersama dengan properti lain yang dapat dikonfigurasi baik menggunakan alat GUI web admin atau di domain.xml
secara langsung. domain.xml
terlihat seperti berikut (menggunakan sumber data XA).
<jdbc-connection-pool datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"
name="jdbc_pool"
res-type="javax.sql.XADataSource">
<property name="password" value="password"></property>
<property name="databaseName" value="database_name"></property>
<property name="serverName" value="localhost"></property>
<property name="user" value="root"></property>
<property name="portNumber" value="3306"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="characterEncoding" value="UTF-8"></property>
<property name="useUnicode" value="true"></property>
<property name="characterSetResults" value="UTF-8"></property>
<!-- The following two of our interest -->
<property name="serverTimezone" value="UTC"></property>
<property name="useLegacyDatetimeCode" value="false"></property>
</jdbc-connection-pool>
<jdbc-resource pool-name="jdbc_pool"
description="description"
jndi-name="jdbc/pool">
</jdbc-resource>
Dalam kasus WildFly, mereka dapat dikonfigurasi di standalone-xx.yy.xml
menggunakan perintah CLI atau menggunakan alat GUI web admin (menggunakan sumber data XA).
<xa-datasource jndi-name="java:jboss/datasources/datasource_name"
pool-name="pool_name"
enabled="true"
use-ccm="true">
<xa-datasource-property name="DatabaseName">database_name</xa-datasource-property>
<xa-datasource-property name="ServerName">localhost</xa-datasource-property>
<xa-datasource-property name="PortNumber">3306</xa-datasource-property>
<xa-datasource-property name="UseUnicode">true</xa-datasource-property>
<xa-datasource-property name="CharacterEncoding">UTF-8</xa-datasource-property>
<!-- The following two of our interest -->
<xa-datasource-property name="UseLegacyDatetimeCode">false</xa-datasource-property>
<xa-datasource-property name="ServerTimezone">UTC</xa-datasource-property>
<xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
<driver>mysql</driver>
<transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
<xa-pool>
<min-pool-size>5</min-pool-size>
<max-pool-size>15</max-pool-size>
</xa-pool>
<security>
<user-name>root</user-name>
<password>password</password>
</security>
<validation>
<valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"/>
<background-validation>true</background-validation>
<exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"/>
</validation>
<statement>
<share-prepared-statements>true</share-prepared-statements>
</statement>
</xa-datasource>
<drivers>
<driver name="mysql" module="com.mysql">
<driver-class>com.mysql.jdbc.Driver</driver-class>
</driver>
</drivers>
Hal yang sama berlaku untuk sumber data non-XA. Mereka dapat langsung ditambahkan ke URL koneksi itu sendiri dalam kasus itu.
Semua properti yang disebutkan ini akan disetel ke kelas yang disebutkan yang tersedia di driver JDBC yaitu com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
menggunakan metode penyetel masing-masing di kelas ini dalam kedua kasus.
Jika menggunakan inti JDBC API secara langsung, atau penggabungan koneksi di Tomcat, misalnya, mereka dapat disetel langsung ke URL koneksi (di context.xml
)
<Context antiJARLocking="true" path="/path">
<Resource name="jdbc/pool"
auth="Container"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="10000"
username="root"
password="password"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/database_name?useEncoding=true&characterEncoding=UTF-8&useLegacyDatetimeCode=false&serverTimezone=UTC"/>
</Context>
Tambahan :
Jika server database target berjalan di zona peka-DST dan Waktu Musim Panas (DST) tidak dimatikan, itu akan menyebabkan masalah. Lebih baik konfigurasikan server database juga untuk menggunakan zona waktu standar yang tidak terpengaruh oleh DST seperti UTC atau GMT. UTC biasanya lebih disukai daripada GMT tetapi keduanya serupa dalam hal ini. Mengutip langsung dari tautan ini .
Omong-omong, saya menjatuhkan konverter milik EclipseLink, sejak JPA 2.1 menyediakan konverter standarnya sendiri
yang dapat di-porting ke penyedia JPA yang berbeda jika diperlukan tanpa sedikit atau tanpa modifikasi sama sekali. Sekarang terlihat seperti berikut di mana java.util.Date
juga digantikan oleh java.sql.Timestamp
.
import java.sql.Timestamp;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@Converter(autoApply = true)
public final class JodaDateTimeConverter implements AttributeConverter<DateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(DateTime dateTime) {
return dateTime == null ? null : new Timestamp(dateTime.withZone(DateTimeZone.UTC).getMillis());
}
@Override
public DateTime convertToEntityAttribute(Timestamp timestamp) {
return timestamp == null ? null : new DateTime(timestamp, DateTimeZone.UTC);
}
}
Maka sepenuhnya tanggung jawab klien aplikasi terkait (Servlets / JSP / JSF / klien desktop jarak jauh dll) untuk mengonversi tanggal / waktu sesuai dengan zona waktu pengguna yang sesuai sambil menampilkan atau menyajikan tanggal / waktu kepada pengguna akhir yang tidak tercakup dalam jawaban ini untuk singkatnya dan di luar topik berdasarkan sifat pertanyaan saat ini.
Pemeriksaan nol tersebut di konverter juga tidak diperlukan karena ini juga merupakan tanggung jawab klien aplikasi terkait kecuali beberapa bidang bersifat opsional.
Semuanya berjalan baik-baik saja sekarang. Ada saran/rekomendasi lain dipersilahkan. Kritik kepada siapa pun yang tidak tahu apa-apa tentang saya sangat saya harapkan.