我需要将所有与时间相关的操作强制转换为GMT/UTC,无论机器上设置的时区是什么。有没有一种方便的方法可以在代码中实现这一点?
为了澄清,我正在使用数据库服务器时间进行所有操作,但它会根据本地时区格式化。
谢谢!
根据该问题的回答,要更改正在运行的JVM实例的默认时区,请设置user.timezone
系统属性:
java -Duser.timezone=GMT ... <main-class>
如果你需要从数据库ResultSet
中检索Date/Time/Timestamp对象时设置特定的时区,那么请使用带有Calendar
对象参数的getXXX
方法的第二种形式:
Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
ResultSet rs = ...;
while (rs.next()) {
Date dateValue = rs.getDate("DateColumn", tzCal);
// Other fields and calculations
}
或者,在 PreparedStatement 中设置日期:
Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
PreparedStatement ps = conn.createPreparedStatement("update ...");
ps.setDate("DateColumn", dateValue, tzCal);
// Other assignments
ps.executeUpdate();
当数据库列不包含时区信息时,这些内容将确保数据库中存储的值是一致的。
java.util.Date
和 java.sql.Date
类在UTC中存储实际时间(毫秒)。要将它们格式化为另一个时区的输出,请使用 SimpleDateFormat
。您还可以使用 Calendar 对象将时区与值关联起来:
TimeZone tz = TimeZone.getTimeZone("<local-time-zone>");
//...
Date dateValue = rs.getDate("DateColumn");
Calendar calValue = Calendar.getInstance(tz);
calValue.setTime(dateValue);
有用的参考资料
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
另外,如果您可以以这种方式设置JVM时区
System.setProperty("user.timezone", "EST");
或者在JVM参数中加入-Duser.timezone=GMT
。
System.setProperty("user.timezone" ...)
不起作用。 - wilmolTimeZone.getDefault()
之前从未被调用过的情况下,这个方法才有效。之后,系统属性将不再影响java.util.Date
和其他传统的日期类对当前时区的解释。从那时起,只有通过TimeZone.setDefault()
来改变它才有效。至于这是否是一个好主意,那是另外一个讨论。我们应该始终坚持使用适当的java.time.*
工具-尊重环境,而不是试图在万不得已时去改变它。 - undefinedTimeZone.setDefault(TimeZone.getTimeZone("GMT"))
我不得不为Windows 2003服务器设置JVM时区,因为它总是返回GMT时间,可以通过以下方式设置:
-Duser.timezone=America/Los_Angeles
或者你所在的正确时区。查找时区列表也有点具有挑战性...
这里有两个列表;
http://wrapper.tanukisoftware.com/doc/english/prop-timezone.html
String sql = "SELECT CURRENT_TIMESTAMP ;
…
OffsetDateTime odt = myResultSet.getObject( 1 , OffsetDateTime.class ) ;
建议您编写所有代码以明确指定所需/期望的时区,不需要依赖JVM当前的默认时区。请注意,JVM当前的默认时区可能会在运行时的任何时刻发生更改,并影响到任何应用程序中的任何线程中的任何代码调用TimeZone.setDefault
。这样的调用会立即影响该JVM中的所有应用程序。
ZoneId
。要表示与UTC偏移量,请使用ZoneOffset
。偏移量仅是相对于本初子午线提前或落后的小时-分钟-秒数。时区则远不止如此。时区是一个特定地区人民使用的偏移量的过去、现在和未来变化的历史记录。我需要强制任何与时间有关的操作为GMT / UTC
对于零小时-分钟-秒的偏移量,请使用常量ZoneOffset.UTC
。
Instant
Instant
。该类始终以UTC表示时刻,根据定义始终为UTC。Instant instant = Instant.now() ; // Capture current moment in UTC.
ZonedDateTime
为了以特定区域人们使用的挂钟时间查看同一时刻,需要调整到相应的时区。同一时刻,在时间线上相同的点,对应不同的挂钟时间。
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
您提到了一个数据库。
要从数据库检索一个时刻,您的列应该是类似于SQL标准的TIMESTAMP WITH TIME ZONE
数据类型。检索一个对象而不是一个字符串。在JDBC 4.2及更高版本中,我们可以与数据库交换java.time对象。JDBC需要OffsetDateTime
,而Instant
和ZonedDateTime
是可选的。
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Instant
: odt.toInstant()
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,如java.util.Date
、Calendar
和SimpleDateFormat
。
要了解更多信息,请参阅Oracle Tutorial。并在Stack Overflow上搜索许多示例和解释。规范为JSR 310。
Joda-Time项目现在处于维护模式,建议迁移到java.time类。
您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。无需字符串,也无需java.sql.*
类。
如何获取java.time类?
使用系统属性:
-Duser.timezone=UTC
对我而言,只需要快速掌握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"));
}
DateFormat formatDate = new SimpleDateFormat(ISO_DATE_TIME_PATTERN);
formatDate.setTimeZone(TimeZone.getTimeZone("UTC"));
formatDate.parse(dateString);