我正在尝试使用Java的VTIMEZONE
和ZoneId
为iCalendar ZoneOffsetTransitionRule
对象建模。
我的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月的第一个星期一开始(相对于3月的某个星期日)。
我具有以下用于创建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
。
DAYLIGHT
的{{1}}组件进行建模是否准确?VTIMEZONE
,以便最终创建一个ZoneId
?答案 0 :(得分:4)
获取ZoneId
的唯一方法(至少如果我们不是非常 hacky)是通过ZoneId
及其子类{{1}的工厂方法}。乍看起来似乎是内置ZoneOffset
留下的。但是,存在一个后门,用于指定其他ZoneId
然后可以产生的ZoneId
。称为ZoneId.of
。我们需要指定一个新的唯一ID,并且需要指定区域规则(因此名称为ZoneRulesProvider
)。
因此,您的ZoneRulesProvider
已经在路上。不过,我们需要其中两个,而过渡到DST的通常需要您的(春季通常会发生这种情况),而秋天则需要另外一个。
下面的列表当然不是生产代码,只是为了证明可以开发和注册自己的ZoneOffsetTransitionRule
。
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));
我们看到在转换为标准时间之前和转换为夏令时之后,我们都获得了预期的DST偏移+02:00。