强制 Java 时区为 GMT/UTC

Posted

技术标签:

【中文标题】强制 Java 时区为 GMT/UTC【英文标题】:Force Java timezone as GMT/UTC 【发布时间】:2011-02-07 08:26:07 【问题描述】:

无论机器上设置的时区如何,我都需要将任何与时间相关的操作强制为 GMT/UTC。在代码中有什么方便的方法吗?

为了澄清,我使用数据库服务器时间进行所有操作,但它根据本地时区格式化。

谢谢!

【问题讨论】:

其实他的问题是我的一个子集,不过我找到了解决办法。 查看重复的问题,并搜索“Joda”和“DateTimeZone”。 【参考方案1】:

我会以原始形式(长时间戳或 java 的日期)从数据库中检索时间,然后使用 SimpleDateFormat 对其进行格式化,或使用 Calendar 对其进行操作。在这两种情况下,您都应该在使用对象之前设置其时区。

详情请参阅SimpleDateFormat.setTimeZone(..)Calendar.setTimeZone(..)

【讨论】:

【参考方案2】:

OP 回答了这个问题以更改正在运行的 JVM 的单个实例的默认时区,设置 user.timezone 系统属性:

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

如果您在从数据库ResultSet 检索日期/时间/时间戳对象时需要设置特定时区,请使用采用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.Datejava.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 设置时区需要注意的一点是,它会影响一切,包括日志记录等。如果这是想要的效果,请记住一些事情。 是的,很好。需要提一点,我实际上是直接在代码中设置了 user.timezone,而不是通过 -D 参数。 @SyBer:这可行,但您必须确保在任何方法调用 TimeZone.getDefault() 方法之前设置它。如果不是,您会有些困惑,因为默认值是在第一次执行后缓存的。这也意味着如果您在 API/库中执行此操作(从普通视图中隐藏)可能会出现问题 - 请务必大量记录您正在执行的操作。【参考方案3】:

我必须为 Windows 2003 Server 设置 JVM 时区,因为它总是为 new Date() 返回 GMT;

-Duser.timezone=America/Los_Angeles

或您的适当时区。事实证明,查找时区列表也有点挑战性......

这里有两个列表;

http://wrapper.tanukisoftware.com/doc/english/prop-timezone.html

http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Frzatz%2F51%2Fadmin%2Freftz.htm

【讨论】:

【参考方案4】:

您可以使用TimeZone.setDefault() 更改时区:

TimeZone.setDefault(TimeZone.getTimeZone("GMT"))

【讨论】:

无意冒犯,但临时修改全局静态状态听起来是个非常糟糕的主意... 你可以,但你不应该。这是一个非常糟糕的建议。整个 JVM 的时区发生了变化。 有时这正是您想要实现的。例如。我已经看到基于 Java 的微服务始终使用 UTC 运行以避免时区问题。在这些系统中,数据库后端也使用 UTC 运行,因此很自然地将 JVM 也“强制”为 UTC。重要的是:你必须知道你在做什么以及后果是什么...... 绝对不是。如果您希望整个系统采用 UTC(一个非常好的主意),请将系统时区设置为 UTC 并保留 Java 时区。 除非您有多个具有不同要求的 JVM。我们可以争论不休,但每种情况都是独一无二的:有时您需要设置整个系统的时区,有时您只设置一个 JVM。让我们感到高兴的是,今天的系统如此灵活,我们可以两者兼得 ?【参考方案5】:

如果你可以这样设置 JVM 时区

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

或 JVM 参数中的 -Duser.timezone=GMT

【讨论】:

System.setProperty("user.timezone" ...) 不起作用。 System.setProperty("user.timezone", "EST");这工作正常。谢谢【参考方案6】:

对我来说,只需快速 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"));
  

然后用不同的时区格式化日期。

【讨论】:

【参考方案7】:

创建一对客户端/服务器,以便执行后,客户端服务器发送正确的时间和日期。然后,客户端在下午 GMT 时询问服务器,服务器发回正确的答案。

【讨论】:

【参考方案8】:

如果您只想使用 intiger 获取 GMT 时间: 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)

【讨论】:

在我看来就像 javascript (!= Java)。【参考方案9】:

哇。我知道这是一个古老的线程,但我只能说不要在任何用户级代码中调用 TimeZone.setDefault()。这总是为整个 JVM 设置时区,几乎总是是一个非常糟糕的主意。学习使用 joda.time 库或 Java 8 中与 joda.time 库非常相似的新 DateTime 类。

【讨论】:

我用它来做测试。我永远不知道我的队友在他们的电脑上使用什么时区,也不知道 CI 上会设置什么。【参考方案10】:

使用系统属性:

-Duser.timezone=UTC

【讨论】:

为什么要使用它?请在您的答案中添加一些解释,以便其他人可以从中学习【参考方案11】:

tl;博士

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

避免依赖主机操作系统或 JVM 默认时区

我建议您编写所有代码以明确说明所需/预期的时区。您无需依赖 JVM 当前的默认时区。请注意,JVM 当前的默认时区可以在运行期间随时更改,任何应用程序的任何线程中的任何代码都会调用TimeZone.setDefault。这样的调用会立即影响该 JVM 中的所有应用程序。

java.time

其他一些答案是正确的,但现在已经过时了。与最早的 Java 版本捆绑在一起的糟糕的日期时间类存在缺陷,由不了解日期时间处理的复杂性和微妙之处的人编写。

旧的日期时间类已被 JSR 310 中定义的 java.time 类取代。

要表示时区,请使用ZoneId。要表示与 UTC 的偏移量,请使用 ZoneOffset。偏移量只是本初子午线之前或之后的小时-分钟-秒数。时区更多。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的历史。

我需要将任何与时间相关的操作强制为 GMT/UTC

对于零时分秒的偏移量,请使用常量ZoneOffset.UTC

Instant

要以 UTC 时间记录当前时刻,请使用 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,而InstantZonedDateTime 是可选的。

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

在大多数数据库和驱动程序中,我猜你会得到 UTC 中看到的时刻。但如果没有,您可以通过以下两种方式之一进行调整:

提取Instant:odt.toInstant() 从给定偏移量调整到零偏移量:OffsetDateTime odtUtc = odt.withOffsetSameInstant(ZoneOffset.UTC) ;`.


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

Java SE 8Java SE 9Java SE 10Java SE 11 和更高版本 - 具有捆绑实现的标准 Java API 的一部分。 Java 9 添加了一些小功能和修复。 Java SE 6Java SE 7 大部分 java.time 功能在ThreeTen-Backport 中向后移植到 Java 6 和 7。 Android java.time 类的 android 捆绑包实现的更高版本。 对于早期的 Android (ThreeTenABP 项目适应 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…

【讨论】:

【参考方案12】:

mysql 中执行此行以重置您的时区:

SET @@global.time_zone = '+00:00';

【讨论】:

如何解决 Java 中的时区问题?【参考方案13】:

希望下面的代码对你有所帮助。

    DateFormat formatDate = new SimpleDateFormat(ISO_DATE_TIME_PATTERN);
    formatDate.setTimeZone(TimeZone.getTimeZone("UTC")); 
    formatDate.parse(dateString);

【讨论】:

以上是关于强制 Java 时区为 GMT/UTC的主要内容,如果未能解决你的问题,请参考以下文章

关于GMT UTC CST和Linux时区设置

关于时区的时间的详解,比如UTCGMT等

如何在 Android 设备中使用 GMT 获取时区格式?

jdbc连接mysq之serverTimezone设定

慕课秒杀总结

如何在Python日志记录中的GMT / UTC上设置时间戳?