如何在Java中通过时区偏移量获取时区信息?

5

我知道如何获取相反的结果。也就是说,给定一个时区,可以通过以下代码片段获取时区偏移量:

TimeZone tz = TimeZone.getDefault();
System.out.println(tz.getOffset(System.currentTimeMillis()));

我希望知道如何从时区偏移量中获取时区名称。

已知,

时区偏移量 = 21600000(以毫秒为单位;+6.00偏移量)

我想要得到以下任意一种可能的时区名称:

(GMT+6:00) Antarctica/Vostok
(GMT+6:00) Asia/Almaty
(GMT+6:00) Asia/Bishkek
(GMT+6:00) Asia/Dacca
(GMT+6:00) Asia/Dhaka
(GMT+6:00) Asia/Qyzylorda
(GMT+6:00) Asia/Thimbu
(GMT+6:00) Asia/Thimphu
(GMT+6:00) Asia/Yekaterinburg
(GMT+6:00) BST
(GMT+6:00) Etc/GMT-6
(GMT+6:00) Indian/Chagos
2个回答

3

简而言之

Instant instant = Instant.now();
List < String > names =
        ZoneId
                .getAvailableZoneIds()
                .stream()
                .filter(
                        ( String name ) ->
                                ZoneId
                                        .of( name )
                                        .getRules()
                                        .getOffset( instant )
                                        .equals(
                                                ZoneOffset.ofHours( 6 )
                                        )
                )
                .collect( Collectors.toList() )
;

在Java 14.0.1中:

names = [亚洲/喀什, Etc/GMT-6, 亚洲/阿拉木图, 亚洲/达卡, 亚洲/鄂木斯克, 亚洲/达卡, 印度/查戈斯, 亚洲/科斯塔纳伊, 亚洲/比什凯克, 南极洲/沃斯托克, 亚洲/乌鲁木齐, 亚洲/廷布, 亚洲/廷布]

java.time

现代解决方案使用java.time类,多年前替换了可怕的遗留日期时间类。具体而言,TimeZone被以下内容替换: UTC偏移仅是格林威治本初子午线之前或之后的小时-分钟-秒数。时区则更为复杂。时区是某个特定地区居民过去、现在和未来偏移变化的历史。时区以大陆/地区格式命名。请参见时区列表

获取 JVM 的当前时区。

ZoneId z = ZoneId.systemDefault() ;

获取当前定义的所有ZoneId对象列表。请注意,时区经常被政治家更改。因此,这个列表和它们包含的规则将会发生变化。请保持您的tzdata更新。
Set< String > zones = ZoneId.getAvailableZoneIds() ;

你提问:

我想知道如何从时区偏移量中获取时区名称。

在任何给定的时刻,许多时区可能巧合地共享相同的偏移量。在下面的代码中,我们循环所有已知的时区,询问每个时区的偏移量。

由于时区规则随时间改变(由政治家确定),您必须提供您想要每个区域使用的偏移量的时刻。这里我们在运行时使用当前时刻。

// Convert your milliseconds to an offset-from-UTC.
int milliseconds = 21_600_000;
int seconds = Math.toIntExact( TimeUnit.MILLISECONDS.toSeconds( milliseconds ) );
ZoneOffset targetOffset = ZoneOffset.ofTotalSeconds( seconds );

// Capture the current moment as seen in UTC (an offset of zero hours-minutes-seconds).
Instant now = Instant.now();

// Loop through all know time zones, comparing each one’s zone to the target zone.
List < ZoneId > hits = new ArrayList <>();
Set < String > zoneNames = ZoneId.getAvailableZoneIds();
for ( String zoneName : zoneNames )
{
    ZoneId zoneId = ZoneId.of( zoneName );

    ZoneRules rules = zoneId.getRules();
    // ZoneRules rules = zoneId.getRules() ;
    ZoneOffset offset = rules.getOffset( now );
    if ( offset.equals( targetOffset ) )
    {
        hits.add( zoneId );
    }
}

将内容输出到控制台。请参考 IdeOne.com上的实时代码运行

// Report results.
System.out.println( "java.version " + System.getProperty("java.version") ) ;
System.out.println( "java.vendor " + System.getProperty("java.vendor") ) ;
System.out.println() ;
System.out.println( "targetOffset = " + targetOffset );
System.out.println( "hits: " + hits );

在IdeOne.com上查看此代码的实时运行情况。

java.version 12.0.1

java.vendor Oracle Corporation

targetOffset = +06:00

命中次数:[Asia/Kashgar,Etc/GMT-6,Asia/Almaty,Asia/Dacca,Asia/Omsk,Asia/Dhaka,Indian/Chagos,Asia/Qyzylorda,Asia/Bishkek,Antarctica/Vostok,Asia/Urumqi,Asia/Thimbu,Asia/Thimphu]


关于 java.time

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

要了解更多,请参阅 Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是 JSR 310Joda-Time 项目现在处于 维护模式,建议迁移到 java.time 类。
你可以直接使用 java.time 对象与数据库进行交换。使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序。无需字符串,不需要 java.sql.* 类。Hibernate 5 和 JPA 2.2 支持 java.time
如何获取 java.time 类?

2

使用 TimeZone#getAvailableIDs(int) 方法。

import java.util.*;
class Hello
{
   public static void main (String[] args) throws java.lang.Exception
   {
     TimeZone tz = TimeZone.getDefault();
     int offset = 21600000;
     String[] availableIDs = tz.getAvailableIDs(offset);
     for(int i = 0; i < availableIDs.length; i++) {
       System.out.println(availableIDs[i]);
     }
   }
}

1
很好!使用这种策略有什么陷阱吗?我的意思是与夏令时相关的事情? - Anonymous One
非常感谢您抽出时间。 - Anonymous One
3
请注意,此仅返回其标准时间偏移匹配的条目。例如,传递“-25200000”不会返回使用该偏移值的夏令时期间的“America/Los_Angeles”。 - Matt Johnson-Pint
4
你为什么要使用 TimeZone.getDefault()?这段代码应该改为 String[] ids = TimeZone.getAvailableIds(21600000L);。避免像实例方法一样调用静态方法。 - Jon Skeet
1
@AnonymousOne: 我相信是的,没错。 - Jon Skeet
显示剩余4条评论

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