在Java中将UTC时间转换为本地时区

我知道这个话题已被打死,但在搜索了几个小时之后我不得不问这个问题。

我的问题:根据客户端应用程序(iphone)的当前时区对服务器上的日期进行计算。 客户端应用程序会在几秒钟内告诉服务器它的时区离GMT有多远。 我想使用此信息对服务器中的日期进行计算。 服务器上的日期都存储为UTC时间。 因此,我希望在将UTC Date对象转换为此本地时区后获得它的HOUR。

我目前的尝试:

int hours = (int) Math.floor(secondsFromGMT / (60.0 * 60.0)); int mins = (int) Math.floor((secondsFromGMT - (hours * 60.0 * 60.0)) / 60.0); String sign = hours > 0 ? "+" : "-"; Calendar now = Calendar.getInstance(); TimeZone t = TimeZone.getTimeZone("GMT" + sign + hours + ":" + mins); now.setTimeZone(t); now.setTime(someDateTimeObject); int hourOfDay = now.get(Calendar.HOUR_OF_DAY); 

变量小时和分钟表示当地时区远离GMT的小时和分钟。 调试此代码后 – 变量小时,分钟和符号是正确的。

问题是hourOfDay没有返回正确的小时 – 它返回UTC时间而不是当地时间的小时。 想法?

您的时区字符串配置错误。 尝试这个,

 String sign = secondsFromGMT > 0 ? "+" : "-"; secondsFromGMT = Math.abs(secondsFromGMT); int hours = secondsFromGMT/3600; int mins = (secondsFromGMT%3600)/60; String zone = String.format("GMT%s%02d:%02d", sign, hours, mins); TimeZone t = TimeZone.getTimeZone(zone); 

TL;博士

在将UTC Date对象转换为此本地时区后获取它的HOUR

区域 (首选)

 ZonedDateTime.now( ZoneId.of( "Africa/Tunis" ) // Use a zone rather than a mere offset-from-UTC whenever possible. ).getHour() 

23

抵消

 OffsetDateTime.now( ZoneOffset.ofTotalSeconds( secondsFromGMT ) // Use a mere offset-from-UTC only when the appropriate zone is unavailable AND you have been assured this particular offset is correct for your input data. ).getHour() 

23

避免遗留日期时间类

您正在使用现在遗留下来的麻烦的旧日期时间类,取而代之的是现代业界领先的java.time类。

java.time

以UTC为单位获取当前时刻。 Instant类表示UTC时间轴上的一个时刻,分辨率为纳秒 (最多九(9)位小数)。

 Instant instant = Instant.now() ; 

调整到特定时区。

 Specify a [proper time zone name](https://en.wikipedia.org/wiki/List_of_tz_zones_by_name) in the format of `continent/region`, such as [`America/Montreal`](https://en.wikipedia.org/wiki/America/Montreal), [`Africa/Casablanca`](https://en.wikipedia.org/wiki/Africa/Casablanca), or `Pacific/Auckland`. Never use the 3-4 letter abbreviation such as `EST` or `IST` as they are *not* true time zones, not standardized, and not even unique(!). ZoneId z = ZoneId.of( "America/Montreal" ); 

如果在运行时您需要JVM的当前默认时区,请询问它。 请记住,这随时都可能发生变化。 因此,如果关键,您应该向用户询问或确认。

 ZoneId z = ZoneId.systemDefault() ; 

应用时区以生成ZonedDateTime对象。

 ZonedDateTime zdt = instant.atZone( z ) ; 

你说你只希望从这个分区日期时间开始。 询问特定方法。

 int hourOfDay = zdt.getHour() ; 

顺便说一句,您可能会发现LocalTime类很有用。

 LocalTime lt = zdt.toLocalTime() ; 

抵消与区域

变量小时和分钟表示当地时区远离GMT的小时和分钟

不,不是时区。 您有带偏移的混合区域。 理解这种区别非常重要。

  • 抵消
    与UTC的偏移量是UTC(GMT)之前/之后的小时数和分钟数 。 没有更多,没有更少。

  • 时区是特定区域人民使用的各种偏移的历史。 区域表示随时间 ,过去,现在和将来的偏移的所有变化 。 世界各地的政治家都表现出了经常改变他们时区偏移的倾向。

特定偏移可能在多个地方巧合使用。 例如,有时候都柏林,伦敦,卡萨布兰卡,拉各斯和布拉柴维尔都会在UTC前一小时共享+01:00的相同偏移量。 但在其他时候他们可能没有,例如在今年冬天,当拉各斯比UTC早一个小时,而其他三个在UTC之前切换到零时(即UTC本身)。

使用区域和时刻,您可以确定偏移量。 但不是朝另一个方向走; 如果只有偏移,则无法可靠地确定区域。

所以你的小时和分钟数只是一个补偿。 对于输入的特定日期,您的区域的偏移量可能不正确。 因此,总是喜欢仅仅偏移的时区。

OffsetDateTime

如果由于某种原因而您因某种原因而卡住您确定对于您的给定输入是正确的但您不知道时区,则使用ZoneOffsetOffsetDateTime而不是ZoneIdZonedDateTime 。 但如果可能的话,使用区域而不是偏移,如上所述。

 ZoneOffset offset = ZoneOffset.ofTotalSeconds( secondsFromGMT ) ; OffsetDateTime odt = OffsetDateTime.now( offset ) ; 

关于java.time

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

现在处于维护模式的Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参阅Oracle教程 。 并搜索Stack Overflow以获取许多示例和解释。 规范是JSR 310 。

从哪里获取java.time类?

  • Java SE 8Java SE 9及更高版本
    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小function和修复。
  • Java SE 6Java SE 7
    • 许多java.timefunction都被反向移植到ThreeTen-Backport中的 Java 6和7。
  • Android的
    • 更高版本的Android捆绑java.time类的实现。
    • 对于早期的Android, ThreeTenABP项目采用ThreeTen-Backport (如上所述)。 请参见如何使用ThreeTenABP ….

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目是未来可能添加到java.time的试验场。 您可以在这里找到一些有用的课程,如IntervalYearWeekYearQuarter等。