有没有办法在 Java8中将 ZoneId 转换为 ZoneOffset?

我有一个纪元秒和一个 ZoneId(见下面的 method1)。

它可以通过系统默认 zoneId 转换为 LocalDateTime,但是我找不到将 epoch second 转换为 LocalDateTime的方法(参见下面的 method2) ,因为没有 ZoneOffset.systemDefault。我觉得很模糊。

import java.time.{Instant, LocalDateTime, ZoneId, ZoneOffset}


val epochSecond = System.currentTimeMillis() / 1000


// method1
LocalDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), ZoneId.systemDefault())


// method2
LocalDateTime.ofEpochSecond(epochSecond, 0, ZoneOffset.MAX)

NOTE

上面提供的源代码是 斯卡拉

91773 次浏览

正如 文件所说,“这主要用于低级别的转换,而不是一般的应用程序使用。”

通过 Instant对我来说非常有意义-你的纪元秒实际上是一个 Instant的不同表示,所以转换成一个 Instant,然后转换成一个特定的时区。

没有一对一的映射。ZoneId 定义一个地理区域,其中随着时间的推移使用一组不同的 ZoneOffset。如果时区使用夏时制,那么它的时区偏移量在夏季和冬季之间就会有所不同。

此外,夏时制规则可能会随着时间的推移而改变,因此在例如2015年13月10日与1980年13月10日之间,“区域偏移”可能会有所不同。

所以只能在特定的即时上找到 ZoneId 的 ZoneOffset。

参见 https://en.wikipedia.org/wiki/Tz_database

下面是如何从 ZoneId中获得 ZoneOffset:

Instant instant = Instant.now(); //can be LocalDateTime
ZoneId systemZone = ZoneId.systemDefault(); // my timezone
ZoneOffset currentOffsetForMyZone = systemZone.getRules().getOffset(instant);

NB: ZoneId can have different offset depending on point in time and the history of the particular place. So choosing different Instants would result in different offsets.

NB2: ZoneId.of() can return a ZoneOffset instead of ZoneId if UTC+3/GMT+2/etc is passed as opposed to a time zone like Africa/Cairo. So if UTC/GMT offsets are passed then historical/geographical/daylight-saving information of the Instant won't be taken into account - you'll simply work with the specified offset.

博士

ZonedDateTime.now(
ZoneId.of( "America/Montreal" )
)

... 当前的默认时区..。

ZonedDateTime.now(
ZoneId.systemDefault()
)

细节

回答: Stanislav Bshkyrtsev正确而直接地回答你的问题。

但是,正如 Answer by Jon Skeet所建议的那样,还有一些更大的问题需要解决。

LocalDateTime

我找不到将 epoch second 转换为 LocalDateTime 的方法

LocalDateTime故意没有时区或偏离 UTC 的概念。不像是你想要的。Local…意味着 任何地区,而不是任何一个特定的地区。这个课程的 没有代表一个时刻,只有 潜力的时刻沿着大约26-27小时的范围(全球各地的时区范围)。

Instant

如果您试图获取当前时间,则不需要以纪元秒开始。获取当前 InstantInstant类在 协调世界时中表示时间线上的一个时刻,其分辨率为 纳秒(最多为小数部分的9位数)。

Instant instant = Instant.now();

在这个 Instant里面是一个从时代开始的 纳秒计数,但是我们并不真的在乎。

再看看 Instant 和 LocalDateTime 有什么区别

ZonedDateTime

如果你想通过一个特定区域的时钟时间透镜来看到那个时刻,应用一个 ZoneId得到一个 ZonedDateTime

ZoneId z = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdt = instant.atZone( z );

作为一个快捷方式,你可以直接做到 ZonedDateTime

ZonedDateTime zdt = ZonedDateTime.now( z );

ZonedDateTime包含 Instant。调用 zdt.toInstant()获得与 协调世界时中基本值相同的时刻。无论是 ZonedDateTime还是 Instant都是相同的纳秒数。

时代以来的第二次

If you are given a count of seconds-since-epoch, and the epoch is the first moment of 1970 in 协调世界时 (1970-01-01T00:00:00Z), then feed that number to Instant.

long secondsSinceEpoch = 1_484_063_246L ;
Instant instant = Instant.ofEpochSecond( secondsSinceEpoch ) ;

Table of date-time types in Java, both modern and legacy.


关于 爪哇时间

< em > java.time 框架内置于 Java8及更高版本中。这些类取代了令人讨厌的旧的 遗产日期时间类,如 java.util.DateCalendarSimpleDateFormat

To learn more, see the < em > Oracle 教程 . And search Stack Overflow for many examples and explanations. Specification is JSR 310.

现在在 维修模式中的 尤达时间项目建议迁移到 爪哇时间类。

您可以直接与数据库交换 爪哇时间对象。使用与 JDBC 4.2或更高版本兼容的 JDBC 驱动程序。不需要字符串,不需要 java.sql.*类。

从哪里获得 java.time 类?

< strong > Three Ten-Ultra 项目使用其他类扩展 java.time。这个项目是将来可能添加 java.time 的试验场。您可以在这里找到一些有用的类,比如 IntervalYearWeekYearQuarter更多

我希望下面的解决方案的前两行是有帮助的。我的问题是,我有一个 LocalDateTime和一个时区的名称,我需要一个 instant,这样我就可以构建一个 java.util.Date,因为这就是 MongoDB 想要的。我的代码是 Scala,但是它与 Java 非常接近,我认为理解它应该没有问题:

val zid = ZoneId.of(tzName)                                // "America/Los_Angeles"
val zo: ZoneOffset = zid.getRules.getOffset(localDateTime) // ⇒ -07:00
// 2017-03-16T18:03


val odt = OffsetDateTime.of(localDateTime, zo) // ⇒ 2017-03-16T18:03:00-07:00
val instant = odt.toInstant                    // ⇒ 2017-03-17T01:03:00Z
val issued = Date.from(instant)

下面返回以毫秒为单位添加到 UTC 以获得此时区内的标准时间的时间量:

TimeZone.getTimeZone(ZoneId.of("Europe/Amsterdam")).getRawOffset()

我有一个纪元秒和一个 zoneId。有没有办法在 java 8中将 ZoneId 转换为 ZoneOffset?

  1. 从第二纪元和区域 ID 获取 ZonedDateTime
  2. ZonedDateTime中获取 ZoneOffset

演示:

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;


public class Main {
public static void main(String[] args) {
// Get ZonedDateTime from epoch second and Zone Id
ZonedDateTime zdt = Instant.ofEpochSecond(1597615462L).atZone(ZoneId.of("Europe/London"));


// Get ZoneOffset from ZonedDateTime
ZoneOffset offset = zdt.getOffset();


System.out.println(offset);
}
}

产出:

+01:00

因为您正在查找默认区域偏移量

ZonedDateTime.now().getOffset()

这样做不需要创建 Instant 或类似的对象来将其拉出。

public static ZoneOffset offset() {
return offset(ZoneId.systemDefault());                 // Default system zone id
}
public static ZoneOffset offset(ZoneId id) {
return ZoneOffset.ofTotalSeconds((int)
TimeUnit.MILLISECONDS.toSeconds(
TimeZone.getTimeZone(id).getRawOffset()        // Returns offset in milliseconds
)
);
}

这是我们用的。首先将区域 id 转换为时区,然后获取以毫秒为单位的偏移量,然后转换为秒,然后创建一个 ZoneOffset。

public static final ZoneOffset toOffset(ZoneId zoneId) {
return Optional.ofNullable(zoneId)
.map(TimeZone::getTimeZone)
.map(TimeZone::getRawOffset)
.map(Duration::ofMillis)
.map(Duration::getSeconds)
.map(Number::intValue)
.map(ZoneOffset::ofTotalSeconds)
.orElse(null);
}