在Java 8中,有没有将ZoneId转换为ZoneOffset的方法?

132

我有一个时间戳和一个时区ID(见下面的method1)。

它可以使用系统默认时区ID转换为LocalDateTime,但我没有找到将时间戳转换为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)

注意

上面呈现的源代码是用Scala编写的。


2
你使用什么语言/方言来使批量导入 import java.time.{Instant, LocalDateTime, ZoneId, ZoneOffset} 工作?在 Java 11 中,它对我来说是一个 unexpected token - Snackoverflow
3
可以使用Scala,但这并不是在这里真正重要的。 - donatas M
1
我在这里详细讨论了java.time的理论:https://dev59.com/PWEh5IYBdhLWcg3wYSvt#56508200 - Ondra Žižka
10个回答

159

以下是从 ZoneId 获取 ZoneOffset 的方法:

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

注意:根据时间点和特定地点的历史,ZoneId 可能具有不同的偏移量。因此选择不同的 Instants 将导致不同的偏移量。

注意2:ZoneId.of() 如果传递的是类似于 UTC+3/GMT+2/等时区而不是像 Africa/Cairo 的时区,则可能返回 ZoneOffset 而不是 ZoneId。 因此,如果传递的是 UTC/GMT 偏移量,则不会考虑 Instant 的历史、地理和夏令时信息 - 您将只使用指定的偏移量。


1
如果当前时间处于夏令时,但在计算时没有考虑夏令时,则会得到错误的值。 - msangel
我检查了一下,它的工作取决于另一个条件。已经更新了答案。 - msangel

55

简述

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

...当前默认时区...

ZonedDateTime.now( 
    ZoneId.systemDefault() 
)

详情

Stanislav Bshkyrtsev的回答正确并直接回答了您的问题。

但是,涉及到更大的问题,正如Jon Skeet的回答所建议的那样。

LocalDateTime

我找不到将纪元秒转换为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()可以获得与UTC中基本值相同的时间瞬间。以纳秒为单位的时代值数量是相同的,无论是ZonedDateTime还是Instant

给定秒数的时代值

如果您已经获得了自1970年1月1日UTC的第一刻起经过的秒数,那么请将该数字提供给Instant
long secondsSinceEpoch = 1_484_063_246L ;
Instant instant = Instant.ofEpochSecond( secondsSinceEpoch ) ;

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


关于 java.time

java.time 框架内置于 Java 8 及以上版本。这些类替代了老旧的 传统 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

想要了解更多,请查看 Oracle 教程。在 Stack Overflow 上搜索许多示例和解释。规范为 JSR 310

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

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

如何获取java.time类?

ThreeTen-Extra 项目通过增加类来扩展 java.time。该项目是 java.time 可能未来添加的试验场。您可能会在这里找到一些有用的类,例如 Interval, YearWeek, YearQuartermore


47

没有一对一的映射关系。ZoneId定义了一个地理范围,在这个范围内,一组不同的ZoneOffset随着时间的推移而使用。如果时区使用夏令时,则其ZoneOffset将在夏季和冬季之间不同。

此外,夏令时规则可能会随着时间而改变,因此例如2015年10月13日与1980年10月13日相比,ZoneOffset可能是不同的。

因此,您只能找到特定时间点上ZoneId的ZoneOffset。

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


12
我知道,但怎么做呢?如果我已经有了LocaDate和ZoneId,如何将它转换为ZoneOffset?我的用例是一个包含时间和时区的字符串。我想将该字符串解析为OffsetTime,而不会丢失时区信息。 - Kai Moritz

11
根据文档所述,"这主要是用于低级别的转换而不是一般应用程序使用。"
通过 Instant 转换对我来说非常有意义 - 您的时代秒基本上是 Instant 的另一种表示形式,因此转换为 Instant,然后将其转换为特定的时区。

9
我希望下面我解决问题的前两行对您有所帮助。我的问题是我有一个 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)

1
我在这里发布了这个答案,因为当我正在寻找一种方法来做我最终想出的事情时,这就是出现的页面。 - gknauth

3
以下内容返回从UTC添加到此时区以获取标准时间所需的毫秒数:
TimeZone.getTimeZone(ZoneId.of("Europe/Amsterdam")).getRawOffset()

localZoneOffset = ZoneOffset.ofTotalSeconds(TimeZone.getTimeZone(localTimeZone).getRawOffset() / 1000) - Vitamon

3

我有一个纪元秒和一个区域ID。在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

1

这是我们使用的方法。首先将区域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);
}

0

这可以在不创建实例或类似对象来提取它的情况下完成。

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 
        )
    );
}

0

由于您正在寻找默认的区域偏移量

ZonedDateTime.now().getOffset()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接