强制 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.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 设置时区需要注意的一点是,它会影响一切,包括日志记录等。如果这是想要的效果,请记住一些事情。 是的,很好。需要提一点,我实际上是直接在代码中设置了 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
,而Instant
和ZonedDateTime
是可选的。
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.Date
、Calendar
和 SimpleDateFormat
。
要了解更多信息,请参阅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 8、Java SE 9、Java SE 10、Java SE 11 和更高版本 - 具有捆绑实现的标准 Java API 的一部分。 Java 9 添加了一些小功能和修复。 Java SE 6 和 Java 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的主要内容,如果未能解决你的问题,请参考以下文章