ZetCode

Java ZoneRulesProvider 类

最后修改时间:2025 年 4 月 16 日

java.time.zone.ZoneRulesProvider 类为系统提供时区规则。它充当时区规则的服务提供者。该类是抽象的,必须被子类化以提供自定义时区规则。

ZoneRulesProvider 管理可用的时区 ID 及其规则。默认提供者使用 IANA 时区数据库。可以注册多个提供者,后注册的提供者具有优先权。

ZoneRulesProvider 类概述

ZoneRulesProvider 提供注册提供者和访问时区规则的方法。关键操作包括获取可用的时区 ID 和特定区域的规则。该类是线程安全的,并且设计用于扩展。

public abstract class ZoneRulesProvider {
    public static Set<String> getAvailableZoneIds();
    public static ZoneRules getRules(String zoneId, boolean forCaching);
    public static void registerProvider(ZoneRulesProvider provider);
    public static void registerProvider(ZoneRulesProvider provider, int priority);
    protected abstract Set<String> provideZoneIds();
    protected abstract ZoneRules provideRules(String zoneId, boolean forCaching);
    protected abstract NavigableMap<String, ZoneRules> provideVersions(String zoneId);
}

上面的代码显示了 ZoneRulesProvider 的关键方法。抽象方法必须由具体提供者实现。该类通过 provideVersions 方法支持版本化的时区规则。

获取可用的时区 ID

getAvailableZoneIds 方法从所有注册的提供者返回所有可用的时区 ID。这对于发现系统支持哪些时区很有用。

Main.java
package com.zetcode;

import java.time.zone.ZoneRulesProvider;
import java.util.Set;

public class Main {

    public static void main(String[] args) {
        
        Set<String> zoneIds = ZoneRulesProvider.getAvailableZoneIds();
        
        System.out.println("Total zones available: " + zoneIds.size());
        System.out.println("First 5 zones:");
        
        zoneIds.stream()
               .limit(5)
               .forEach(System.out::println);
    }
}

此示例检索所有可用的时区 ID 并打印总数。然后它显示前五个时区 ID。输出将根据注册的提供者和 Java 版本而有所不同。

获取特定时区的规则

getRules 方法检索特定时区的规则。这些规则包括转换、偏移量和夏令时信息。

Main.java
package com.zetcode;

import java.time.zone.ZoneRules;
import java.time.zone.ZoneRulesProvider;

public class Main {

    public static void main(String[] args) {
        
        String zoneId = "America/New_York";
        ZoneRules rules = ZoneRulesProvider.getRules(zoneId, false);
        
        System.out.println("Rules for " + zoneId + ":");
        System.out.println("Fixed offset: " + rules.isFixedOffset());
        System.out.println("Standard offset: " + rules.getStandardOffset(
            java.time.Instant.now()));
        System.out.println("Daylight savings active now: " + 
            rules.isDaylightSavings(java.time.Instant.now()));
    }
}

此示例检索纽约时区的规则并打印一些关键属性。输出显示该时区是否具有固定偏移量和当前的夏令时状态。

创建自定义 ZoneRulesProvider

可以通过扩展 ZoneRulesProvider 来创建自定义提供者。这允许添加对 IANA 数据库中不存在的自定义时区规则的支持。

Main.java
package com.zetcode;

import java.time.zone.ZoneRules;
import java.time.zone.ZoneRulesProvider;
import java.util.Collections;
import java.util.NavigableMap;
import java.util.Set;
import java.time.Instant;
import java.time.ZoneOffset;

public class CustomZoneProvider extends ZoneRulesProvider {

    @Override
    protected Set<String> provideZoneIds() {
        return Collections.singleton("CUSTOM/UTC+2");
    }

    @Override
    protected ZoneRules provideRules(String zoneId, boolean forCaching) {
        if ("CUSTOM/UTC+2".equals(zoneId)) {
            return ZoneRules.of(ZoneOffset.ofHours(2));
        }
        return null;
    }

    @Override
    protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
        return null;
    }
}

public class Main {
    public static void main(String[] args) {
        ZoneRulesProvider.registerProvider(new CustomZoneProvider());
        
        ZoneRules rules = ZoneRulesProvider.getRules("CUSTOM/UTC+2", false);
        System.out.println("Custom zone offset: " + 
            rules.getOffset(Instant.now()));
    }
}

此示例为简单的固定偏移量时区创建自定义提供者。提供者已注册,然后用于获取自定义区域的规则。请注意,现实世界的提供者将处理更复杂的场景。

注册多个提供者

可以注册具有不同优先级的多个提供者。在解析时区规则时,首先检查较高优先级的提供者。

Main.java
package com.zetcode;

import java.time.zone.ZoneRules;
import java.time.zone.ZoneRulesProvider;
import java.util.Collections;
import java.util.NavigableMap;
import java.util.Set;
import java.time.Instant;
import java.time.ZoneOffset;

class HighPriorityProvider extends ZoneRulesProvider {
    @Override protected Set<String> provideZoneIds() {
        return Collections.singleton("PRIORITY/TEST");
    }
    @Override protected ZoneRules provideRules(String zoneId, boolean forCaching) {
        return ZoneRules.of(ZoneOffset.ofHours(3));
    }
    @Override protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
        return null;
    }
}

class LowPriorityProvider extends ZoneRulesProvider {
    @Override protected Set<String> provideZoneIds() {
        return Collections.singleton("PRIORITY/TEST");
    }
    @Override protected ZoneRules provideRules(String zoneId, boolean forCaching) {
        return ZoneRules.of(ZoneOffset.ofHours(4));
    }
    @Override protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
        return null;
    }
}

public class Main {
    public static void main(String[] args) {
        ZoneRulesProvider.registerProvider(new LowPriorityProvider(), 1);
        ZoneRulesProvider.registerProvider(new HighPriorityProvider(), 2);
        
        ZoneRules rules = ZoneRulesProvider.getRules("PRIORITY/TEST", false);
        System.out.println("Effective offset: " + 
            rules.getOffset(Instant.now())); // Will show +03:00
    }
}

此示例演示提供者优先级。高优先级提供者的规则优先。输出显示来自较高优先级提供者的偏移量。

检查提供者注册

虽然没有直接的 API 可以列出已注册的提供者,但我们可以通过检查可用的时区 ID 和规则行为来推断它们的存在。

Main.java
package com.zetcode;

import java.time.zone.ZoneRulesProvider;
import java.util.Set;

public class Main {

    public static void main(String[] args) {
        
        // Count zones before registration
        Set<String> beforeIds = ZoneRulesProvider.getAvailableZoneIds();
        System.out.println("Zones before: " + beforeIds.size());
        
        // Register a custom provider
        ZoneRulesProvider.registerProvider(new CustomZoneProvider());
        
        // Count zones after registration
        Set<String> afterIds = ZoneRulesProvider.getAvailableZoneIds();
        System.out.println("Zones after: " + afterIds.size());
        
        // Check if our custom zone is available
        if (afterIds.contains("CUSTOM/UTC+2")) {
            System.out.println("Custom provider is active");
        }
    }
    
    static class CustomZoneProvider extends ZoneRulesProvider {
        @Override protected Set<String> provideZoneIds() {
            return Set.of("CUSTOM/UTC+2");
        }
        @Override protected ZoneRules provideRules(String zoneId, boolean forCaching) {
            return null;
        }
        @Override protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
            return null;
        }
    }
}

此示例通过比较时区 ID 集间接检查提供者注册。它显示了如何验证自定义提供者是否已成功注册。

处理时区版本

provideVersions 方法允许提供者支持多个时区规则版本。这对于历史准确性很有用。

Main.java
package com.zetcode;

import java.time.zone.ZoneRules;
import java.time.zone.ZoneRulesProvider;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;
import java.time.Instant;
import java.time.ZoneOffset;

public class VersionedZoneProvider extends ZoneRulesProvider {

    @Override
    protected Set<String> provideZoneIds() {
        return Set.of("VERSIONED/TEST");
    }

    @Override
    protected ZoneRules provideRules(String zoneId, boolean forCaching) {
        return ZoneRules.of(ZoneOffset.ofHours(2));
    }

    @Override
    protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
        if ("VERSIONED/TEST".equals(zoneId)) {
            NavigableMap<String, ZoneRules> versions = new TreeMap<>();
            versions.put("2023", ZoneRules.of(ZoneOffset.ofHours(2)));
            versions.put("2024", ZoneRules.of(ZoneOffset.ofHours(3)));
            return versions;
        }
        return null;
    }
}

public class Main {
    public static void main(String[] args) {
        ZoneRulesProvider.registerProvider(new VersionedZoneProvider());
        
        NavigableMap<String, ZoneRules> versions = 
            ZoneRulesProvider.getVersions("VERSIONED/TEST");
        System.out.println("Available versions: " + versions.keySet());
    }
}

此示例创建了一个具有版本化规则的提供者。provideVersions 方法为不同的年份返回不同的规则。请注意,标准的 Java API 不直接使用这些版本。

来源

Java ZoneRulesProvider 类文档

在本文中,我们介绍了 Java ZoneRulesProvider 类的基本方法和特性。理解这些概念对于 Java 应用程序中的高级时区处理至关重要。

作者

我叫 Jan Bodnar,是一位经验丰富的程序员,在该领域拥有多年的经验。我从 2007 年开始撰写编程文章,至今已撰写了 1,400 多篇文章和 8 本电子书。凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程