【问题标题】:Custom ZoneIds / Time Zones in JavaJava 中的自定义 ZoneIds / 时区
【发布时间】:2018-12-26 15:43:42
【问题描述】:

我正在尝试使用 Java 的 ZoneIdZoneOffsetTransitionRule 为 iCalendar VTIMEZONE 对象建模。

我的VTIMEZONE 对象看起来像

BEGIN:VTIMEZONE
TZID:Central European Standard Time
BEGIN:STANDARD
DTSTART:16010101T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T020000
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=1;BYDAY=MO
END:DAYLIGHT
END:VTIMEZONE

我需要创建自己的 ZoneId 来对此进行建模,因为据我所知,没有可用的 ZoneId 具有这些偏移量并且 DST 在 1 月的第一个星期一开始(而不是三月的某个星期日)。

我有以下用于创建ZoneOffsetTransitionRule

ZoneOffsetTransitionRule of =
  ZoneOffsetTransitionRule.of(Month.JANUARY, 1, DayOfWeek.MONDAY, LocalTime.of(2, 0),
    false, ZoneOffsetTransitionRule.TimeDefinition.STANDARD, ZoneOffset.ofHours(1),
    ZoneOffset.ofHours(1), ZoneOffset.ofHours(2));

但我不确定它是否正确或如何从中创建ZoneId

  • 该转换规则对我的VTIMEZONEDAYLIGHT 组件建模是否准确?
  • 如何从中创建ZoneId,以便最终创建ZonedDateTime

【问题讨论】:

  • 无论你在 iCalendar 中得到什么,我认为现实中不存在这样的时区。
  • 你可以看看你是否可以实现并注册一个ZoneRulesProvider
  • @OleV.V.:如何从ZoneRulesProvider 获得ZoneId
  • this answer底部有一个例子。当我阅读文档时,您必须为您的新 ZoneId 发明一个唯一名称。

标签: java time timezone icalendar


【解决方案1】:

获得ZoneId 的唯一方法(至少如果我们不是非常 hacky)是通过ZoneId 及其子类ZoneOffset 的工厂方法。乍一看,这似乎是内置的ZoneIds。但是,有一个后门可以指定额外的 ZoneIds,然后 ZoneId.of 可以生成。它被称为ZoneRulesProvider。我们需要指定一个新的唯一 ID,并且需要指定区域规则(因此名称为 ZoneRulesProvider)。

因此,您的ZoneOffsetTransitionRule 已经在路上了。不过,我们需要其中两个,一个用于过渡到 DST(这通常发生在春季),另一个用于在秋季进行。

当然,以下清单不是生产代码,只是为了证明开发和注册自己的 ZoneRulesProvider 是可行的。

    final String customZoneId = "Custom-CEST-1";

    final ZoneOffset standardOffset = ZoneOffset.ofHours(1);
    final ZoneOffset summerTimeOffset = ZoneOffset.ofHours(2);
    // At least one transistion is required
    ZoneOffsetTransition initialTransition = ZoneOffsetTransition.of(
            LocalDateTime.of(1601, 1, 1, 3, 0), summerTimeOffset, standardOffset);
    List<ZoneOffsetTransition> transitionList = List.of(initialTransition);

    // Rules for going to and from summer time (DST)
    ZoneOffsetTransitionRule springRule =
            ZoneOffsetTransitionRule.of(Month.JANUARY, 1, DayOfWeek.MONDAY, LocalTime.of(2, 0),
                    false, ZoneOffsetTransitionRule.TimeDefinition.STANDARD, standardOffset,
                    standardOffset, summerTimeOffset);
    ZoneOffsetTransitionRule fallRule =
            ZoneOffsetTransitionRule.of(Month.OCTOBER, -1, DayOfWeek.SUNDAY, LocalTime.of(2, 0),
                    false, ZoneOffsetTransitionRule.TimeDefinition.STANDARD, standardOffset,
                    summerTimeOffset, standardOffset);
    ZoneRules rules = ZoneRules.of(standardOffset, standardOffset,
            transitionList, transitionList, List.of(springRule, fallRule));

    // The heart of the magic: the ZoneRulesProvider
    ZoneRulesProvider customProvider = new ZoneRulesProvider() {

        @Override
        protected Set<String> provideZoneIds() {
            return Set.of(customZoneId);
        }

        @Override
        protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
            return new TreeMap<>(Map.of(customZoneId, rules));
        }

        @Override
        protected ZoneRules provideRules(String zoneId, boolean forCaching) {
            return rules;
        }
    };

    // Registering the ZoneRulesProvider is the key to ZoneId using it
    ZoneRulesProvider.registerProvider(customProvider);

    // Get an instance of our custom ZoneId
    ZoneId customZone = ZoneId.of(customZoneId);
    // Transition to standard time was Sunday, October 29, 2017,
    // so try the day before and the day after
    System.out.println(LocalDate.of(2017, Month.OCTOBER, 28).atStartOfDay(customZone));
    System.out.println(LocalDate.of(2017, Month.OCTOBER, 30).atStartOfDay(customZone));
    // The special thing about our custom ZoneID is that transition to DST
    // happened on Monday, January 1. Try the day before and the day after.
    System.out.println(LocalDate.of(2017, Month.DECEMBER, 31).atStartOfDay(customZone));
    System.out.println(LocalDate.of(2018, Month.JANUARY, 2).atStartOfDay(customZone));

代码打印:

2017-10-28T00:00+02:00[Custom-CEST-1]
2017-10-30T00:00+01:00[Custom-CEST-1]
2017-12-31T00:00+01:00[Custom-CEST-1]
2018-01-02T00:00+02:00[Custom-CEST-1]

我们看到我们在转换到标准时间之前和转换到夏令时之后得到了预期的 +02:00 的 DST 偏移量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-10
    • 1970-01-01
    • 1970-01-01
    • 2014-05-05
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    • 2017-06-26
    相关资源
    最近更新 更多