< prev index next >
make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesProvider.java
Print this page
rev 55814 : imported patch 8212970
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
@@ -29,28 +29,18 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.Map.Entry;
-import java.util.NavigableMap;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
import java.util.concurrent.ConcurrentSkipListMap;
import java.time.*;
import java.time.Year;
import java.time.chrono.IsoChronology;
import java.time.temporal.TemporalAdjusters;
-import java.time.zone.ZoneOffsetTransition;
-import java.time.zone.ZoneOffsetTransitionRule;
-import java.time.zone.ZoneOffsetTransitionRule.TimeDefinition;
+import build.tools.tzdb.ZoneOffsetTransitionRule.TimeDefinition;
import java.time.zone.ZoneRulesException;
/**
* Compile and build time-zone rules from IANA timezone data
*
@@ -270,12 +260,12 @@
/** The time of the cutover, in second of day */
int secsOfDay = 0;
/** Whether this is midnight end of day. */
boolean endOfDay;
- /** The time of the cutover. */
+ /** The time definition of the cutover. */
TimeDefinition timeDefinition = TimeDefinition.WALL;
void adjustToForwards(int year) {
if (adjustForwards == false && dayOfMonth > 0) {
// weekDay<=monthDay case, don't have it in tzdb data for now
@@ -341,10 +331,24 @@
secsOfDay = parseSecs(timeStr);
if (secsOfDay == 86400) {
// time must be midnight when end of day flag is true
endOfDay = true;
secsOfDay = 0;
+ } else if (secsOfDay < 0 || secsOfDay > 86400) {
+ // beyond 0:00-24:00 range. Adjust the cutover date.
+ int beyondDays = secsOfDay / 86400;
+ secsOfDay %= 86400;
+ if (secsOfDay < 0) {
+ secsOfDay = 86400 + secsOfDay;
+ beyondDays -= 1;
+ }
+ LocalDate date = LocalDate.of(2004, month, dayOfMonth).plusDays(beyondDays); // leap-year
+ month = date.getMonth();
+ dayOfMonth = date.getDayOfMonth();
+ if (dayOfWeek != null) {
+ dayOfWeek = dayOfWeek.plus(beyondDays);
+ }
}
timeDefinition = parseTimeDefinition(timeStr.charAt(timeStr.length() - 1));
}
}
}
@@ -495,13 +499,15 @@
/**
* Converts this to a transition rule.
*
* @param standardOffset the active standard offset, not null
* @param savingsBeforeSecs the active savings before the transition in seconds
+ * @param negativeSavings minimum savings in the rule, usually zero, but negative if negative DST is
+ * in effect.
* @return the transition, not null
*/
- ZoneOffsetTransitionRule toTransitionRule(ZoneOffset stdOffset, int savingsBefore) {
+ ZoneOffsetTransitionRule toTransitionRule(ZoneOffset stdOffset, int savingsBefore, int negativeSavings) {
// rule shared by different zones, so don't change it
Month month = this.month;
int dayOfMonth = this.dayOfMonth;
DayOfWeek dayOfWeek = this.dayOfWeek;
boolean endOfDay = this.endOfDay;
@@ -520,18 +526,19 @@
if (dayOfWeek != null) {
dayOfWeek = dayOfWeek.plus(1);
}
endOfDay = false;
}
+
// build rule
return ZoneOffsetTransitionRule.of(
//month, dayOfMonth, dayOfWeek, time, endOfDay, timeDefinition,
month, dayOfMonth, dayOfWeek,
LocalTime.ofSecondOfDay(secsOfDay), endOfDay, timeDefinition,
stdOffset,
ZoneOffset.ofTotalSeconds(stdOffset.getTotalSeconds() + savingsBefore),
- ZoneOffset.ofTotalSeconds(stdOffset.getTotalSeconds() + savingsAmount));
+ ZoneOffset.ofTotalSeconds(stdOffset.getTotalSeconds() + savingsAmount - negativeSavings));
}
RuleLine parse(String[] tokens) {
startYear = parseYear(tokens[2], 0);
endYear = parseYear(tokens[3], startYear);
@@ -641,16 +648,16 @@
this.rule = rule;
this.ldt = rule.toDateTime(year);
this.ldtSecs = ldt.toEpochSecond(ZoneOffset.UTC);
}
- ZoneOffsetTransition toTransition(ZoneOffset standardOffset, int savingsBeforeSecs) {
+ ZoneOffsetTransition toTransition(ZoneOffset standardOffset, int savingsBeforeSecs, int negativeSavings) {
// copy of code in ZoneOffsetTransitionRule to avoid infinite loop
ZoneOffset wallOffset = ZoneOffset.ofTotalSeconds(
standardOffset.getTotalSeconds() + savingsBeforeSecs);
ZoneOffset offsetAfter = ZoneOffset.ofTotalSeconds(
- standardOffset.getTotalSeconds() + rule.savingsAmount);
+ standardOffset.getTotalSeconds() + rule.savingsAmount - negativeSavings);
LocalDateTime dt = rule.timeDefinition
.createDateTime(ldt, standardOffset, wallOffset);
return ZoneOffsetTransition.of(dt, wallOffset, offsetAfter);
}
@@ -664,14 +671,16 @@
/**
* Tests if this a real transition with the active savings in seconds
*
* @param savingsBefore the active savings in seconds
+ * @param negativeSavings minimum savings in the rule, usually zero, but negative if negative DST is
+ * in effect.
* @return true, if savings changes
*/
- boolean isTransition(int savingsBefore) {
- return rule.savingsAmount != savingsBefore;
+ boolean isTransition(int savingsBefore, int negativeSavings) {
+ return rule.savingsAmount - negativeSavings != savingsBefore;
}
public int compareTo(TransRule other) {
return (ldtSecs < other.ldtSecs)? -1 : ((ldtSecs == other.ldtSecs) ? 0 : 1);
}
@@ -695,16 +704,26 @@
ZoneOffset wallOffset = ZoneOffset.ofTotalSeconds(stdOffset.getTotalSeconds() + savings);
// start ldt of each zone window
LocalDateTime zoneStart = LocalDateTime.MIN;
- // first stanard offset
+ // first standard offset
ZoneOffset firstStdOffset = stdOffset;
// first wall offset
ZoneOffset firstWallOffset = wallOffset;
for (ZoneLine zone : zones) {
+ // Adjust stdOffset, if negative DST is observed. It should be either
+ // fixed amount, or expressed in the named Rules.
+ int negativeSavings = Math.min(zone.fixedSavingsSecs, findNegativeSavings(zoneStart, zone));
+ if (negativeSavings < 0) {
+ zone.stdOffsetSecs += negativeSavings;
+ if (zone.fixedSavingsSecs < 0) {
+ zone.fixedSavingsSecs = 0;
+ }
+ }
+
// check if standard offset changed, update it if yes
ZoneOffset stdOffsetPrev = stdOffset; // for effectiveSavings check
if (zone.stdOffsetSecs != stdOffset.getTotalSeconds()) {
ZoneOffset stdOffsetNew = ZoneOffset.ofTotalSeconds(zone.stdOffsetSecs);
standardTransitionList.add(
@@ -789,20 +808,20 @@
Collections.sort(lastRules);
}
// sort the merged rules
Collections.sort(trules);
- effectiveSavings = 0;
+ effectiveSavings = -negativeSavings;
for (TransRule rule : trules) {
if (rule.toEpochSecond(stdOffsetPrev, savings) >
zoneStart.toEpochSecond(wallOffset)) {
// previous savings amount found, which could be the
// savings amount at the instant that the window starts
// (hence isAfter)
break;
}
- effectiveSavings = rule.rule.savingsAmount;
+ effectiveSavings = rule.rule.savingsAmount - negativeSavings;
}
}
// check if the start of the window represents a transition
ZoneOffset effectiveWallOffset =
ZoneOffset.ofTotalSeconds(stdOffset.getTotalSeconds() + effectiveSavings);
@@ -815,25 +834,25 @@
savings = effectiveSavings;
// apply rules within the window
if (trules != null) {
long zoneStartEpochSecs = zoneStart.toEpochSecond(wallOffset);
for (TransRule trule : trules) {
- if (trule.isTransition(savings)) {
+ if (trule.isTransition(savings, negativeSavings)) {
long epochSecs = trule.toEpochSecond(stdOffset, savings);
if (epochSecs < zoneStartEpochSecs ||
epochSecs >= zone.toDateTimeEpochSecond(savings)) {
continue;
}
- transitionList.add(trule.toTransition(stdOffset, savings));
- savings = trule.rule.savingsAmount;
+ transitionList.add(trule.toTransition(stdOffset, savings, negativeSavings));
+ savings = trule.rule.savingsAmount - negativeSavings;
}
}
}
if (lastRules != null) {
for (TransRule trule : lastRules) {
- lastTransitionRuleList.add(trule.rule.toTransitionRule(stdOffset, savings));
- savings = trule.rule.savingsAmount;
+ lastTransitionRuleList.add(trule.rule.toTransitionRule(stdOffset, savings, negativeSavings));
+ savings = trule.rule.savingsAmount - negativeSavings;
}
}
// finally we can calculate the true end of the window, passing it to the next window
wallOffset = ZoneOffset.ofTotalSeconds(stdOffset.getTotalSeconds() + savings);
@@ -846,6 +865,40 @@
standardTransitionList,
transitionList,
lastTransitionRuleList);
}
+ /**
+ * Find the minimum negative savings in named Rules for a Zone. Savings are only
+ * looked at for the period of the subject Zone.
+ *
+ * @param zoneStart start LDT of the zone
+ * @param zl ZoneLine to look at
+ */
+ private int findNegativeSavings(LocalDateTime zoneStart, ZoneLine zl) {
+ int negativeSavings = 0;
+ LocalDateTime zoneEnd = zl.toDateTime();
+
+ if (zl.savingsRule != null) {
+ List<RuleLine> rlines = rules.get(zl.savingsRule);
+ if (rlines == null) {
+ throw new IllegalArgumentException("<Rule> not found: " +
+ zl.savingsRule);
+ }
+
+ negativeSavings = Math.min(0, rlines.stream()
+ .filter(l -> windowOverlap(l, zoneStart.getYear(), zoneEnd.getYear()))
+ .map(l -> l.savingsAmount)
+ .min(Comparator.naturalOrder())
+ .orElse(0));
+ }
+
+ return negativeSavings;
+ }
+
+ private boolean windowOverlap(RuleLine ruleLine, int zoneStartYear, int zoneEndYear) {
+ boolean overlap = zoneStartYear <= ruleLine.startYear && zoneEndYear >= ruleLine.startYear ||
+ zoneStartYear <= ruleLine.endYear && zoneEndYear >= ruleLine.endYear;
+
+ return overlap;
+ }
}
< prev index next >