< prev index next >
make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesProvider.java
Print this page
rev 55794 : [mq]: 8212970
*** 1,7 ****
/*
! * Copyright (c) 2014, 2018, 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
--- 1,7 ----
/*
! * 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,56 ****
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.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 java.time.zone.ZoneRulesException;
/**
* Compile and build time-zone rules from IANA timezone data
*
--- 29,46 ----
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.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentSkipListMap;
import java.time.*;
import java.time.Year;
import java.time.chrono.IsoChronology;
import java.time.temporal.TemporalAdjusters;
! import build.tools.tzdb.ZoneOffsetTransitionRule.TimeDefinition;
import java.time.zone.ZoneRulesException;
/**
* Compile and build time-zone rules from IANA timezone data
*
*** 270,281 ****
/** 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. */
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
--- 260,271 ----
/** The time of the cutover, in second of day */
int secsOfDay = 0;
/** Whether this is midnight end of day. */
boolean endOfDay;
+ /** 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,350 ****
--- 331,354 ----
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,507 ****
/**
* Converts this to a transition rule.
*
* @param standardOffset the active standard offset, not null
* @param savingsBeforeSecs the active savings before the transition in seconds
* @return the transition, not null
*/
! ZoneOffsetTransitionRule toTransitionRule(ZoneOffset stdOffset, int savingsBefore) {
// 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;
--- 499,513 ----
/**
* 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, 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,537 ****
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));
}
RuleLine parse(String[] tokens) {
startYear = parseYear(tokens[2], 0);
endYear = parseYear(tokens[3], startYear);
--- 526,544 ----
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 - negativeSavings));
}
RuleLine parse(String[] tokens) {
startYear = parseYear(tokens[2], 0);
endYear = parseYear(tokens[3], startYear);
*** 641,656 ****
this.rule = rule;
this.ldt = rule.toDateTime(year);
this.ldtSecs = ldt.toEpochSecond(ZoneOffset.UTC);
}
! ZoneOffsetTransition toTransition(ZoneOffset standardOffset, int savingsBeforeSecs) {
// copy of code in ZoneOffsetTransitionRule to avoid infinite loop
ZoneOffset wallOffset = ZoneOffset.ofTotalSeconds(
standardOffset.getTotalSeconds() + savingsBeforeSecs);
ZoneOffset offsetAfter = ZoneOffset.ofTotalSeconds(
! standardOffset.getTotalSeconds() + rule.savingsAmount);
LocalDateTime dt = rule.timeDefinition
.createDateTime(ldt, standardOffset, wallOffset);
return ZoneOffsetTransition.of(dt, wallOffset, offsetAfter);
}
--- 648,663 ----
this.rule = rule;
this.ldt = rule.toDateTime(year);
this.ldtSecs = ldt.toEpochSecond(ZoneOffset.UTC);
}
! 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 - negativeSavings);
LocalDateTime dt = rule.timeDefinition
.createDateTime(ldt, standardOffset, wallOffset);
return ZoneOffsetTransition.of(dt, wallOffset, offsetAfter);
}
*** 664,677 ****
/**
* Tests if this a real transition with the active savings in seconds
*
* @param savingsBefore the active savings in seconds
* @return true, if savings changes
*/
! boolean isTransition(int savingsBefore) {
! return rule.savingsAmount != savingsBefore;
}
public int compareTo(TransRule other) {
return (ldtSecs < other.ldtSecs)? -1 : ((ldtSecs == other.ldtSecs) ? 0 : 1);
}
--- 671,686 ----
/**
* 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, int negativeSavings) {
! return rule.savingsAmount - negativeSavings != savingsBefore;
}
public int compareTo(TransRule other) {
return (ldtSecs < other.ldtSecs)? -1 : ((ldtSecs == other.ldtSecs) ? 0 : 1);
}
*** 695,710 ****
ZoneOffset wallOffset = ZoneOffset.ofTotalSeconds(stdOffset.getTotalSeconds() + savings);
// start ldt of each zone window
LocalDateTime zoneStart = LocalDateTime.MIN;
! // first stanard offset
ZoneOffset firstStdOffset = stdOffset;
// first wall offset
ZoneOffset firstWallOffset = wallOffset;
for (ZoneLine zone : zones) {
// 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(
--- 704,729 ----
ZoneOffset wallOffset = ZoneOffset.ofTotalSeconds(stdOffset.getTotalSeconds() + savings);
// start ldt of each zone window
LocalDateTime zoneStart = LocalDateTime.MIN;
! // 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,808 ****
Collections.sort(lastRules);
}
// sort the merged rules
Collections.sort(trules);
! effectiveSavings = 0;
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;
}
}
// check if the start of the window represents a transition
ZoneOffset effectiveWallOffset =
ZoneOffset.ofTotalSeconds(stdOffset.getTotalSeconds() + effectiveSavings);
--- 808,827 ----
Collections.sort(lastRules);
}
// sort the merged rules
Collections.sort(trules);
! 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 - negativeSavings;
}
}
// check if the start of the window represents a transition
ZoneOffset effectiveWallOffset =
ZoneOffset.ofTotalSeconds(stdOffset.getTotalSeconds() + effectiveSavings);
*** 815,839 ****
savings = effectiveSavings;
// apply rules within the window
if (trules != null) {
long zoneStartEpochSecs = zoneStart.toEpochSecond(wallOffset);
for (TransRule trule : trules) {
! if (trule.isTransition(savings)) {
long epochSecs = trule.toEpochSecond(stdOffset, savings);
if (epochSecs < zoneStartEpochSecs ||
epochSecs >= zone.toDateTimeEpochSecond(savings)) {
continue;
}
! transitionList.add(trule.toTransition(stdOffset, savings));
! savings = trule.rule.savingsAmount;
}
}
}
if (lastRules != null) {
for (TransRule trule : lastRules) {
! lastTransitionRuleList.add(trule.rule.toTransitionRule(stdOffset, savings));
! savings = trule.rule.savingsAmount;
}
}
// finally we can calculate the true end of the window, passing it to the next window
wallOffset = ZoneOffset.ofTotalSeconds(stdOffset.getTotalSeconds() + savings);
--- 834,858 ----
savings = effectiveSavings;
// apply rules within the window
if (trules != null) {
long zoneStartEpochSecs = zoneStart.toEpochSecond(wallOffset);
for (TransRule trule : trules) {
! 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, negativeSavings));
! savings = trule.rule.savingsAmount - negativeSavings;
}
}
}
if (lastRules != null) {
for (TransRule trule : lastRules) {
! 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,851 ****
--- 865,904 ----
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 >