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

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

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

谢谢!

【问题讨论】:

  • 其实他的问题是我的一个子集,不过我找到了解决办法。
  • 查看重复的问题,并搜索“Joda”和“DateTimeZone”。

标签: java timezone utc gmt


【解决方案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 类?

          【讨论】:

            【解决方案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);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-05-16
              • 2011-01-06
              • 1970-01-01
              • 2023-03-26
              • 2012-02-10
              • 2019-04-19
              相关资源
              最近更新 更多