按时区偏移获取TimeZone

时间:2018-01-16 13:08:40

标签: java date datetime timezone timezone-offset

我需要一个时区,我有时区偏移。

例如,我有偏移= 14400000。我想有一个时区(任何地方,任何城市)。我可以在SimpleDateFormat中使用此时区,以便为此区域打印正确的时间。

This is what I found(我重构了一下):

private TimeZone getTimeZone(int offset) {
    final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC");
    String[] ids = TimeZone.getAvailableIDs(offset);
    if (ids == null || ids.length == 0) {
        return UTC_TIME_ZONE;
    }

    String matchingZoneId = ids[0];
    return TimeZone.getTimeZone(matchingZoneId);
}

我明白了:

sun.util.calendar.ZoneInfo[id="Asia/Baku",offset=14400000,dstSavings=0,useDaylight=false,transitions=68,lastRule=null]

我不喜欢这种方法,因为TimeZone会调用此处的ZoneInfoFile getZoneIds(int var1)。这是反编译的代码。

 public static String[] getZoneIds(int var0) {
    ArrayList var1 = new ArrayList();
    String[] var2 = getZoneIds();
    int var3 = var2.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        String var5 = var2[var4];
        ZoneInfo var6 = getZoneInfo(var5);
        if (var6.getRawOffset() == var0) {
            var1.add(var5);
        }
    }

    var2 = (String[])var1.toArray(new String[var1.size()]);
    Arrays.sort(var2);
    return var2;
}

你可以看到它在现有对象上循环以构建一个区域ID数组,而我只需要一个!

我的问题是,这是一种更有效的方式吗?我只需要任意时区ID或给定偏移量的单个时区。

3 个答案:

答案 0 :(得分:2)

您可以使用SimpleTimeZone

为任何偏移量构建TimeZone实例
new SimpleTimeZone(offset, "My TimeZone");

答案 1 :(得分:2)

TL;博士

不要做你正在尝试的事情。

没有意义,因为在某个时刻共享偏移的区域可能不会在过去或将来的其他时刻共享该偏移。

区域是偏移的历史

Answer by Ole V.V.是正确的。这是一个更多的讨论。

  

我需要一个时区,我有时区偏移。

不太可能。

offset-from-UTC只是UTC之前或之后的几小时,几分钟和几秒钟。

时区更多。区域是特定区域的人员使用的偏移的过去变化的历史。区域还具有针对该区域的偏移的当前和未来更改的规则。因此,时区总是优于仅仅偏移,但只有在确定已知时。在你的情况下,你不知道区域,不应该猜测;只需坚持使用偏移。

给定的偏移量无法引导您进入特定时区而不会产生歧义且没有错误。

  • 歧义,因为许多区域可能会重合共享相同的偏移量。例如,Europe/Gibralter&amp; Europe/Oslo&amp;今天,Africa/Lagos共享相同的偏移量,比UTC早一个小时。因此,只有+01:00的UTC偏移量,你怎么知道哪个合适?
  • 错误,因为没有日期,您无法知道什么偏移适用于时区。例如,上面列出的三个区域今天在2017-2018冬季共享相同的偏移量,但在夏季,Gibralter和Oslo观察夏令时(DST)意味着他们将一小时转移到两个提前几小时,而拉各斯在UTC之前仍然一个小时。而且从历史上看,由于世界各地的政治家都有重新定义区域的奇怪倾向,因此除了夏令时之外,其他原因可能会有所改变。例如,委内瑞拉在十年内两次将其抵消时间缩短了半小时。作为近年来的另一个例子,俄罗斯和土耳其一直在改变他们对DST的看法,恢复到之前的静态偏移,或者改为永久地保持在他们之前的静态偏移之前。

请注意ZoneRules类只有在您提供(日期时间值)作为参数传递时才能为特定区域提供偏移量。

ZoneOffset offset = ZoneId.of( "Africa/Tunis" )        // Instantiate a `ZoneId`.
                        .getRules()                    // Obtain a `ZoneRules` object from that `ZoneId` object.
                        .getOffset( Instant.now() ) ;  // Interrogate that `ZoneRules` for an offset-from-UTC in use at a specific moment in history.
  

我只需要任何时区id或给定偏移量的单个时区。

这是不可取的。如上所述,在一个日期上共享特定偏移的时区可能不会在另一个日期共享该偏移。每个区域根据偏移变化的历史而变化。所以任意挑选一个区域是不明智的。

java.time

如其他答案中所述,TimeZone&amp; ZoneInfo类现已成为遗产。它们是由 java.time 类取代的麻烦的旧日期时间类的一部分。

而是使用ZoneOffset(从UTC偏移)和ZoneId(时区)。

continent/region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用诸如ESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" );

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和&amp; SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?

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

答案 2 :(得分:1)

TimeZone类与您通常使用的类一起已经过时了; CalendarGregorianCalendarDateFormatSimpleDateFormat

我建议您开始使用java.time,而不是现代Java日期和时间API。通常这么好用。然后,您需要一个ZoneId对象作为您的时区。对于具有常量偏移量的ZoneId,我们创建ZoneOffset的实例,ZoneId的两个子类之一。

    int offsetMilliseconds = 14_400_000;
    int offsetSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(offsetMilliseconds);
    // to check that we haven’t lost any precision, convert back to milliseconds and compare
    if (TimeUnit.SECONDS.toMillis(offsetSeconds) != offsetMilliseconds) {
        throw new IllegalArgumentException("Millisecond precision not supported: " + offsetMilliseconds);
    }
    ZoneId tzid = ZoneOffset.ofTotalSeconds(offsetMilliseconds / 1000);

    System.out.println(tzid);
    // test the time zone we got
    System.out.println(ZonedDateTime.now(tzid));

在我的电脑上刚打印出来:

+04:00
2018-01-16T19:44:22.863760+04:00

ZoneOffset只有秒的精度,而不是毫秒,这对于14 400 000毫秒的情况很好。如果你有几毫秒没有化妆一整秒,你可能想要整数秒而不是抛出异常,你的决定。

只有当您需要将旧式TimeZone对象传递给某些您无法更改或不想立即更改的旧版API时,请转换我们上面提到的ZoneId

    TimeZone oldfashionedTimeZone = TimeZone.getTimeZone(tzid);

这将为您显示TimeZone,其显示名称为GMT+04:00,当然还有4小时的偏移量。但是,请注意,如果TimeZone无法识别您尝试转换的时区(如果您的偏移是奇数秒,则可能会出现这种情况),它会默认为您提供GMT(只有其中一个)旧班的问题)。所以你应该检查你得到的TimeZone对象,例如:

    if (oldfashionedTimeZone.getRawOffset() != offsetMilliseconds) {
        throw new IllegalStateException("Conversion to legacy TimeZone object failed");
    }