获取时区的 TimeZoneIDs

Getting the TimeZoneIDs for a Time Zone

我正在处理 Java 中的时间和日期。

我的日期是:2018-08-22T22:00:00-0500 我这里的时区偏移量是 -0500 如何获取可用时区 ID 的列表?

我这里的主要 objective 是将日期设置为特定时区。但是我不知道时区,因为它嵌入在这种日期格式中。

更新: 我的问题不同于 Java TimeZone offset 根据该问题的公认答案,我需要时区信息: "Europe/Oslo" 。但是我只有抵消。请参阅下面已接受的答案,它解决了我的问题。

您可以将日期转换为日历。 Calendar.getTimeZone() 会 return 时区匹配日历。然后 TimeZone.getAvailableIDs() 会给你可用的 ID。

tl;博士

eachZoneId.getRules().getOffset(
    OffsetDateTime.parse( 
        "2018-08-22T22:00:00-0500" , 
        DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ssX" ) 
    ).toInstant()
).equals( myTargetZoneOffset )

偏移与区域

I do not know the time zone as it is embedded in this date format.

不,您输入的 2018-08-22T22:00:00-0500 字符串没有时区。它只有区区offset-from-UTC

偏移量只是提前或落后 UTC 的小时数、分钟数和秒数。你的显示偏移量比 UTC 晚五个小时。相比之下,time zone是某个地区的人们使用的偏移量的过去、现在和未来变化的历史。

OffsetDateTime

java.time中,我们用OffsetDateTimeclass表示一个带有偏移量的时刻。

您输入的字符串是标准 ISO 8601 格式。所以我们应该能够在不指定格式模式的情况下直接解析,因为java.timeclasses使用ISO 8601 parsing/generating 字符串时默认格式化。但是,您的输入在小时和分钟之间的偏移量中缺少冒号。虽然标准允许,OffsetDateTime class Java 8 和 9 中的一个小错误默认情况下无法解析此类值。解决方法是指定 DateTimeFormatter.

String input = "2018-08-22T22:00:00-0000";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ssX" );  // Specify formatting pattern to match input string.
OffsetDateTime odt = OffsetDateTime.parse( input , f );  // Parse from dumb string to smart `OffsetDateTime` object.

odt.toString(): 2018-08-22T22:00-05:00

时区名称

How can I get the list of available Time Zone IDs?

不确定“时区 ID”是什么意思。我猜您要的是在那个特定时刻使用该特定 offset-from-UTC 的所有时区的列表。

一个 proper time zone name has the format of continent/region, such as America/Montreal, Africa/Casablanca,或 Pacific/Auckland。切勿使用 3-4 个字母的缩写,例如 ESTIST,因为它们 不是 真正的时区,不是标准化的,甚至不是唯一的(!)。

我们使用 ZoneId 表示时区。 TimeZone class 现在是遗留的,应该避免。

要获取在该日期使用该偏移量的 ZoneId 对象列表,我们需要首先从 [=17= 中提取偏移量 (ZoneOffset) ].

ZoneOffset offset = odt.getOffset() ;

offset.toString(): -05:00

下一阶段是询问所有已知时区,询问每个时区在 OffsetDateTime 时刻有效的偏移量。那一刻的参数必须是 UTC,一个 Instant 对象。所以我们必须从我们的 OffsetDateTime 中提取一个 Instant。仍然是同一时刻,时间轴上的同一点,但通过不同 wall-clock 时间的镜头看到。

Instant instant = odt.toInstant() ;  // Extract a UTC value (`Instant`) from our `OffsetDateTime` object.

instant.toString(): 2018-08-23T03:00:00Z

最后的ZZulu的缩写,表示UTC。

创建一个空列表以收集所需区域。

List< ZoneId > hits = new ArrayList<>() ;  // Make an empty list of `ZoneId` objects found to have our desired offset-from-UTC.

现在获取所有已知区域。存在一种方法,可提供一组所有区域名称,但不提供区域对象。因此,对于每次迭代,我们必须实例化 ZoneId。然后我们向区域询问其规则,该区域随时间变化的影响列表。根据规则,我们传递了我们的时间 (Instant),并取回当时有效的 ZoneOffset。如果此偏移量与我们的目标偏移量相匹配,我们将该区域添加到我们的列表中。

请注意,许多区域可能本质上是重复的或已弃用。区域列表有一个支离破碎的历史,有许多变化,有些只是其他的别名。

    Set < String > names = ZoneId.getAvailableZoneIds();  // Get a collection of all known time zones’ names.
    for ( String name : names )                           // Loop each name.
    {
        ZoneId z = ZoneId.of( name );                     // Instantiate a `ZoneId` for that zone name.
        ZoneRules rules = z.getRules();                   // Get the history of past, present, and future changes in offset used by the people of this particular region (time zone).
        ZoneOffset o = rules.getOffset( instant );        // Get the offset-from-UTC in effect at this moment for the people of this region.
        if( o.equals( offset )) {                         // Compare this particular offset to see if it is the same number of hours, minutes, and seconds as our target offset.
            hits.add( z );                                // If we have a hit, add to our collection of `ZoneId` objects.
        }
    }

将我们的 hits 集合转储到控制台。

[America/Panama, America/Chicago, America/Eirunepe, Etc/GMT+5, Pacific/Easter, Mexico/General, America/Porto_Acre, America/Guayaquil, America/Rankin_Inlet, US/Central, America/Rainy_River, America/Indiana/Knox, America/North_Dakota/Beulah, America/Monterrey, America/Jamaica, America/Atikokan, America/Coral_Harbour, America/North_Dakota/Center, America/Cayman, America/Indiana/Tell_City, Chile/EasterIsland, America/Mexico_City, America/Matamoros, CST6CDT, America/Knox_IN, America/Bogota, America/Menominee, America/Resolute, SystemV/EST5, Canada/Central, Brazil/Acre, America/Cancun, America/Lima, America/Bahia_Banderas, US/Indiana-Starke, America/Rio_Branco, SystemV/CST6CDT, Jamaica, America/Merida, America/North_Dakota/New_Salem, America/Winnipeg]

请注意,此区域列表仅对我们选择的特定时刻有效。在较早的时间或较晚的时间,其中一些区域可能正在使用其他一些 offset-from-UTC。相反,在其他时候,一些不在此列表中的区域可能正在使用我们想要的偏移量。


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与您的数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

  • Java SE 8, Java SE 9,及以后
    • Built-in。
    • 标准 Java API 的一部分,带有捆绑实施。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 and Java SE 7
    • java.time 的大部分功能是 back-ported 到 Java ThreeTen-Backport 中的 6 和 7。
  • Android
    • Android java.time classes.
    • 捆绑实施的更高版本
    • 对于较早的 Android,ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.