diff --git a/classlib/src/main/java/org/threeten/bp/zone/ZoneRulesBuilder.java b/classlib/src/main/java/org/threeten/bp/zone/ZoneRulesBuilder.java deleted file mode 100644 index 9af416594..000000000 --- a/classlib/src/main/java/org/threeten/bp/zone/ZoneRulesBuilder.java +++ /dev/null @@ -1,796 +0,0 @@ -/* - * Copyright 2020 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of JSR-310 nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.threeten.bp.zone; - -import static org.threeten.bp.temporal.ChronoField.YEAR; -import static org.threeten.bp.temporal.TemporalAdjusters.nextOrSame; -import static org.threeten.bp.temporal.TemporalAdjusters.previousOrSame; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import org.threeten.bp.DateTimeException; -import org.threeten.bp.DayOfWeek; -import org.threeten.bp.LocalDate; -import org.threeten.bp.LocalDateTime; -import org.threeten.bp.LocalTime; -import org.threeten.bp.Month; -import org.threeten.bp.Year; -import org.threeten.bp.ZoneOffset; -import org.threeten.bp.chrono.IsoChronology; -import org.threeten.bp.zone.ZoneOffsetTransitionRule.TimeDefinition; - -/** - * A mutable builder used to create all the rules for a historic time-zone. - *

- * The rules of a time-zone describe how the offset changes over time. - * The rules are created by building windows on the time-line within which - * the different rules apply. The rules may be one of two kinds: - *

- * - *

Specification for implementors

- * This class is a mutable builder used to create zone instances. - * It must only be used from a single thread. - * The created instances are immutable and thread-safe. - */ -class ZoneRulesBuilder { - - /** - * The list of windows. - */ - private List windowList = new ArrayList(); - /** - * A map for deduplicating the output. - */ - private Map deduplicateMap; - - //----------------------------------------------------------------------- - /** - * Constructs an instance of the builder that can be used to create zone rules. - *

- * The builder is used by adding one or more windows representing portions - * of the time-line. The standard offset from UTC/Greenwich will be constant - * within a window, although two adjacent windows can have the same standard offset. - *

- * Within each window, there can either be a - * {@link #setFixedSavingsToWindow fixed savings amount} or a - * {@link #addRuleToWindow list of rules}. - */ - public ZoneRulesBuilder() { - } - - //----------------------------------------------------------------------- - /** - * Adds a window to the builder that can be used to filter a set of rules. - *

- * This method defines and adds a window to the zone where the standard offset is specified. - * The window limits the effect of subsequent additions of transition rules - * or fixed savings. If neither rules or fixed savings are added to the window - * then the window will default to no savings. - *

- * Each window must be added sequentially, as the start instant of the window - * is derived from the until instant of the previous window. - * - * @param standardOffset the standard offset, not null - * @param until the date-time that the offset applies until, not null - * @param untilDefinition the time type for the until date-time, not null - * @return this, for chaining - * @throws IllegalStateException if the window order is invalid - */ - public ZoneRulesBuilder addWindow( - ZoneOffset standardOffset, - LocalDateTime until, - TimeDefinition untilDefinition) { - Objects.requireNonNull(standardOffset, "standardOffset"); - Objects.requireNonNull(until, "until"); - Objects.requireNonNull(untilDefinition, "untilDefinition"); - TZWindow window = new TZWindow(standardOffset, until, untilDefinition); - if (windowList.size() > 0) { - TZWindow previous = windowList.get(windowList.size() - 1); - window.validateWindowOrder(previous); - } - windowList.add(window); - return this; - } - - /** - * Adds a window that applies until the end of time to the builder that can be - * used to filter a set of rules. - *

- * This method defines and adds a window to the zone where the standard offset is specified. - * The window limits the effect of subsequent additions of transition rules - * or fixed savings. If neither rules or fixed savings are added to the window - * then the window will default to no savings. - *

- * This must be added after all other windows. - * No more windows can be added after this one. - * - * @param standardOffset the standard offset, not null - * @return this, for chaining - * @throws IllegalStateException if a forever window has already been added - */ - public ZoneRulesBuilder addWindowForever(ZoneOffset standardOffset) { - return addWindow(standardOffset, LocalDateTime.MAX, TimeDefinition.WALL); - } - - //----------------------------------------------------------------------- - /** - * Sets the previously added window to have fixed savings. - *

- * Setting a window to have fixed savings simply means that a single daylight - * savings amount applies throughout the window. The window could be small, - * such as a single summer, or large, such as a multi-year daylight savings. - *

- * A window can either have fixed savings or rules but not both. - * - * @param fixedSavingAmountSecs the amount of saving to use for the whole window, not null - * @return this, for chaining - * @throws IllegalStateException if no window has yet been added - * @throws IllegalStateException if the window already has rules - */ - public ZoneRulesBuilder setFixedSavingsToWindow(int fixedSavingAmountSecs) { - if (windowList.isEmpty()) { - throw new IllegalStateException("Must add a window before setting the fixed savings"); - } - TZWindow window = windowList.get(windowList.size() - 1); - window.setFixedSavings(fixedSavingAmountSecs); - return this; - } - - //----------------------------------------------------------------------- - /** - * Adds a single transition rule to the current window. - *

- * This adds a rule such that the offset, expressed as a daylight savings amount, - * changes at the specified date-time. - * - * @param transitionDateTime the date-time that the transition occurs as defined by timeDefintion, not null - * @param timeDefinition the definition of how to convert local to actual time, not null - * @param savingAmountSecs the amount of saving from the standard offset after the transition in seconds - * @return this, for chaining - * @throws IllegalStateException if no window has yet been added - * @throws IllegalStateException if the window already has fixed savings - * @throws IllegalStateException if the window has reached the maximum capacity of 2000 rules - */ - public ZoneRulesBuilder addRuleToWindow( - LocalDateTime transitionDateTime, - TimeDefinition timeDefinition, - int savingAmountSecs) { - Objects.requireNonNull(transitionDateTime, "transitionDateTime"); - return addRuleToWindow( - transitionDateTime.getYear(), transitionDateTime.getYear(), - transitionDateTime.getMonth(), transitionDateTime.getDayOfMonth(), - null, transitionDateTime.toLocalTime(), false, timeDefinition, savingAmountSecs); - } - - /** - * Adds a single transition rule to the current window. - *

- * This adds a rule such that the offset, expressed as a daylight savings amount, - * changes at the specified date-time. - * - * @param year the year of the transition, from MIN_VALUE to MAX_VALUE - * @param month the month of the transition, not null - * @param dayOfMonthIndicator the day-of-month of the transition, adjusted by dayOfWeek, - * from 1 to 31 adjusted later, or -1 to -28 adjusted earlier from the last day of the month - * @param time the time that the transition occurs as defined by timeDefintion, not null - * @param timeEndOfDay whether midnight is at the end of day - * @param timeDefinition the definition of how to convert local to actual time, not null - * @param savingAmountSecs the amount of saving from the standard offset after the transition in seconds - * @return this, for chaining - * @throws DateTimeException if a date-time field is out of range - * @throws IllegalStateException if no window has yet been added - * @throws IllegalStateException if the window already has fixed savings - * @throws IllegalStateException if the window has reached the maximum capacity of 2000 rules - */ - public ZoneRulesBuilder addRuleToWindow( - int year, - Month month, - int dayOfMonthIndicator, - LocalTime time, - boolean timeEndOfDay, - TimeDefinition timeDefinition, - int savingAmountSecs) { - return addRuleToWindow(year, year, month, dayOfMonthIndicator, null, time, timeEndOfDay, - timeDefinition, savingAmountSecs); - } - - /** - * Adds a multi-year transition rule to the current window. - *

- * This adds a rule such that the offset, expressed as a daylight savings amount, - * changes at the specified date-time for each year in the range. - * - * @param startYear the start year of the rule, from MIN_VALUE to MAX_VALUE - * @param endYear the end year of the rule, from MIN_VALUE to MAX_VALUE - * @param month the month of the transition, not null - * @param dayOfMonthIndicator the day-of-month of the transition, adjusted by dayOfWeek, - * from 1 to 31 adjusted later, or -1 to -28 adjusted earlier from the last day of the month - * @param dayOfWeek the day-of-week to adjust to, null if day-of-month should not be adjusted - * @param time the time that the transition occurs as defined by timeDefintion, not null - * @param timeEndOfDay whether midnight is at the end of day - * @param timeDefinition the definition of how to convert local to actual time, not null - * @param savingAmountSecs the amount of saving from the standard offset after the transition in seconds - * @return this, for chaining - * @throws DateTimeException if a date-time field is out of range - * @throws IllegalArgumentException if the day of month indicator is invalid - * @throws IllegalArgumentException if the end of day midnight flag does not match the time - * @throws IllegalStateException if no window has yet been added - * @throws IllegalStateException if the window already has fixed savings - * @throws IllegalStateException if the window has reached the maximum capacity of 2000 rules - */ - public ZoneRulesBuilder addRuleToWindow( - int startYear, - int endYear, - Month month, - int dayOfMonthIndicator, - DayOfWeek dayOfWeek, - LocalTime time, - boolean timeEndOfDay, - TimeDefinition timeDefinition, - int savingAmountSecs) { - Objects.requireNonNull(month, "month"); - Objects.requireNonNull(time, "time"); - Objects.requireNonNull(timeDefinition, "timeDefinition"); - YEAR.checkValidValue(startYear); - YEAR.checkValidValue(endYear); - if (dayOfMonthIndicator < -28 || dayOfMonthIndicator > 31 || dayOfMonthIndicator == 0) { - throw new IllegalArgumentException("Day of month indicator must be between -28 and 31 " - + "inclusive excluding zero"); - } - if (timeEndOfDay && !time.equals(LocalTime.MIDNIGHT)) { - throw new IllegalArgumentException("Time must be midnight when end of day flag is true"); - } - if (windowList.isEmpty()) { - throw new IllegalStateException("Must add a window before adding a rule"); - } - TZWindow window = windowList.get(windowList.size() - 1); - window.addRule(startYear, endYear, month, dayOfMonthIndicator, dayOfWeek, time, timeEndOfDay ? 1 : 0, - timeDefinition, savingAmountSecs); - return this; - } - - ZoneRulesBuilder addRuleToWindow( - int startYear, - int endYear, - Month month, - int dayOfMonthIndicator, - DayOfWeek dayOfWeek, - LocalTime time, - int adjustDays, - TimeDefinition timeDefinition, - int savingAmountSecs) { - Objects.requireNonNull(month, "month"); - Objects.requireNonNull(timeDefinition, "timeDefinition"); - YEAR.checkValidValue(startYear); - YEAR.checkValidValue(endYear); - if (dayOfMonthIndicator < -28 || dayOfMonthIndicator > 31 || dayOfMonthIndicator == 0) { - throw new IllegalArgumentException("Day of month indicator must be between -28 and 31 " - + "inclusive excluding zero"); - } - if (windowList.isEmpty()) { - throw new IllegalStateException("Must add a window before adding a rule"); - } - TZWindow window = windowList.get(windowList.size() - 1); - window.addRule(startYear, endYear, month, dayOfMonthIndicator, dayOfWeek, time, adjustDays, - timeDefinition, savingAmountSecs); - return this; - } - - //----------------------------------------------------------------------- - /** - * Completes the build converting the builder to a set of time-zone rules. - *

- * Calling this method alters the state of the builder. - * Further rules should not be added to this builder once this method is called. - * - * @param zoneId the time-zone ID, not null - * @return the zone rules, not null - * @throws IllegalStateException if no windows have been added - * @throws IllegalStateException if there is only one rule defined as being forever for any given window - */ - public ZoneRules toRules(String zoneId) { - return toRules(zoneId, new HashMap()); - } - - /** - * Completes the build converting the builder to a set of time-zone rules. - *

- * Calling this method alters the state of the builder. - * Further rules should not be added to this builder once this method is called. - * - * @param zoneId the time-zone ID, not null - * @param deduplicateMap a map for deduplicating the values, not null - * @return the zone rules, not null - * @throws IllegalStateException if no windows have been added - * @throws IllegalStateException if there is only one rule defined as being forever for any given window - */ - ZoneRules toRules(String zoneId, Map deduplicateMap) { - Objects.requireNonNull(zoneId, "zoneId"); - this.deduplicateMap = deduplicateMap; - if (windowList.isEmpty()) { - throw new IllegalStateException("No windows have been added to the builder"); - } - - final List standardTransitionList = new ArrayList(4); - final List transitionList = new ArrayList(256); - final List lastTransitionRuleList = new ArrayList(2); - - // initialize the standard offset calculation - final TZWindow firstWindow = windowList.get(0); - ZoneOffset loopStandardOffset = firstWindow.standardOffset; - int loopSavings = 0; - if (firstWindow.fixedSavingAmountSecs != null) { - loopSavings = firstWindow.fixedSavingAmountSecs; - } - final ZoneOffset firstWallOffset = deduplicate(ZoneOffset.ofTotalSeconds( - loopStandardOffset.getTotalSeconds() + loopSavings)); - LocalDateTime loopWindowStart = deduplicate(LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0)); - ZoneOffset loopWindowOffset = firstWallOffset; - - // build the windows and rules to interesting data - for (TZWindow window : windowList) { - // tidy the state - window.tidy(loopWindowStart.getYear()); - - // calculate effective savings at the start of the window - Integer effectiveSavings = window.fixedSavingAmountSecs; - if (effectiveSavings == null) { - // apply rules from this window together with the standard offset and - // savings from the last window to find the savings amount applicable - // at start of this window - effectiveSavings = 0; - for (TZRule rule : window.ruleList) { - ZoneOffsetTransition trans = rule.toTransition(loopStandardOffset, loopSavings); - if (trans.toEpochSecond() > loopWindowStart.toEpochSecond(loopWindowOffset)) { - // previous savings amount found, which could be the savings amount at - // the instant that the window starts (hence isAfter) - break; - } - effectiveSavings = rule.savingAmountSecs; - } - } - - // check if standard offset changed, and update it - if (!loopStandardOffset.equals(window.standardOffset)) { - standardTransitionList.add(deduplicate( - new ZoneOffsetTransition( - LocalDateTime.ofEpochSecond(loopWindowStart.toEpochSecond(loopWindowOffset), 0, - loopStandardOffset), - loopStandardOffset, window.standardOffset))); - loopStandardOffset = deduplicate(window.standardOffset); - } - - // check if the start of the window represents a transition - ZoneOffset effectiveWallOffset = deduplicate(ZoneOffset.ofTotalSeconds( - loopStandardOffset.getTotalSeconds() + effectiveSavings)); - if (!loopWindowOffset.equals(effectiveWallOffset)) { - ZoneOffsetTransition trans = deduplicate( - new ZoneOffsetTransition(loopWindowStart, loopWindowOffset, effectiveWallOffset)); - transitionList.add(trans); - } - loopSavings = effectiveSavings; - - // apply rules within the window - for (TZRule rule : window.ruleList) { - ZoneOffsetTransition trans = deduplicate(rule.toTransition(loopStandardOffset, loopSavings)); - if (trans.toEpochSecond() >= loopWindowStart.toEpochSecond(loopWindowOffset) - && trans.toEpochSecond() < window.createDateTimeEpochSecond(loopSavings) - && !trans.getOffsetBefore().equals(trans.getOffsetAfter())) { - transitionList.add(trans); - loopSavings = rule.savingAmountSecs; - } - } - - // calculate last rules - for (TZRule lastRule : window.lastRuleList) { - ZoneOffsetTransitionRule transitionRule = deduplicate(lastRule.toTransitionRule(loopStandardOffset, - loopSavings)); - lastTransitionRuleList.add(transitionRule); - loopSavings = lastRule.savingAmountSecs; - } - - // finally we can calculate the true end of the window, passing it to the next window - loopWindowOffset = deduplicate(window.createWallOffset(loopSavings)); - loopWindowStart = deduplicate(LocalDateTime.ofEpochSecond( - window.createDateTimeEpochSecond(loopSavings), 0, loopWindowOffset)); - } - return new StandardZoneRules( - firstWindow.standardOffset, firstWallOffset, standardTransitionList, - transitionList, lastTransitionRuleList); - } - - /** - * Deduplicates an object instance. - * - * @param the generic type - * @param object the object to deduplicate - * @return the deduplicated object - */ - @SuppressWarnings("unchecked") - T deduplicate(T object) { - if (!deduplicateMap.containsKey(object)) { - deduplicateMap.put(object, object); - } - return (T) deduplicateMap.get(object); - } - - //----------------------------------------------------------------------- - /** - * A definition of a window in the time-line. - * The window will have one standard offset and will either have a - * fixed DST savings or a set of rules. - */ - class TZWindow { - /** The standard offset during the window, not null. */ - private final ZoneOffset standardOffset; - /** The end local time, not null. */ - private final LocalDateTime windowEnd; - /** The type of the end time, not null. */ - private final TimeDefinition timeDefinition; - - /** The fixed amount of the saving to be applied during this window. */ - private Integer fixedSavingAmountSecs; - /** The rules for the current window. */ - private List ruleList = new ArrayList(); - /** The latest year that the last year starts at. */ - private int maxLastRuleStartYear = Year.MIN_VALUE; - /** The last rules. */ - private List lastRuleList = new ArrayList(); - - /** - * Constructor. - * - * @param standardOffset the standard offset applicable during the window, not null - * @param windowEnd the end of the window, relative to the time definition, null if forever - * @param timeDefinition the time definition for calculating the true end, not null - */ - TZWindow( - ZoneOffset standardOffset, - LocalDateTime windowEnd, - TimeDefinition timeDefinition) { - super(); - this.windowEnd = windowEnd; - this.timeDefinition = timeDefinition; - this.standardOffset = standardOffset; - } - - /** - * Sets the fixed savings amount for the window. - * - * @param fixedSavingAmount the amount of daylight saving to apply throughout the window, may be null - * @throws IllegalStateException if the window already has rules - */ - void setFixedSavings(int fixedSavingAmount) { - if (ruleList.size() > 0 || lastRuleList.size() > 0) { - throw new IllegalStateException("Window has DST rules, so cannot have fixed savings"); - } - this.fixedSavingAmountSecs = fixedSavingAmount; - } - - /** - * Adds a rule to the current window. - * - * @param startYear the start year of the rule, from MIN_VALUE to MAX_VALUE - * @param endYear the end year of the rule, from MIN_VALUE to MAX_VALUE - * @param month the month of the transition, not null - * @param dayOfMonthIndicator the day-of-month of the transition, adjusted by dayOfWeek, - * from 1 to 31 adjusted later, or -1 to -28 adjusted earlier from the last day of the month - * @param dayOfWeek the day-of-week to adjust to, null if day-of-month should not be adjusted - * @param time the time that the transition occurs as defined by timeDefintion, not null - * @param adjustDays the time days adjustment - * @param timeDefinition the definition of how to convert local to actual time, not null - * @param savingAmountSecs the amount of saving from the standard offset in seconds - * @throws IllegalStateException if the window already has fixed savings - * @throws IllegalStateException if the window has reached the maximum capacity of 2000 rules - */ - void addRule( - int startYear, - int endYear, - Month month, - int dayOfMonthIndicator, - DayOfWeek dayOfWeek, - LocalTime time, - int adjustDays, - TimeDefinition timeDefinition, - int savingAmountSecs) { - - if (fixedSavingAmountSecs != null) { - throw new IllegalStateException("Window has a fixed DST saving, so cannot have DST rules"); - } - if (ruleList.size() >= 2000) { - throw new IllegalStateException("Window has reached the maximum number of allowed rules"); - } - boolean lastRule = false; - if (endYear == Year.MAX_VALUE) { - lastRule = true; - endYear = startYear; - } - int year = startYear; - while (year <= endYear) { - TZRule rule = new TZRule(year, month, dayOfMonthIndicator, dayOfWeek, time, adjustDays, - timeDefinition, savingAmountSecs); - if (lastRule) { - lastRuleList.add(rule); - maxLastRuleStartYear = Math.max(startYear, maxLastRuleStartYear); - } else { - ruleList.add(rule); - } - year++; - } - } - - /** - * Validates that this window is after the previous one. - * - * @param previous the previous window, not null - * @throws IllegalStateException if the window order is invalid - */ - void validateWindowOrder(TZWindow previous) { - if (windowEnd.isBefore(previous.windowEnd)) { - throw new IllegalStateException("Windows must be added in date-time order: " - + windowEnd + " < " + previous.windowEnd); - } - } - - /** - * Adds rules to make the last rules all start from the same year. - * Also add one more year to avoid weird case where penultimate year has odd offset. - * - * @param windowStartYear the window start year - * @throws IllegalStateException if there is only one rule defined as being forever - */ - void tidy(int windowStartYear) { - if (lastRuleList.size() == 1) { - throw new IllegalStateException("Cannot have only one rule defined as being forever"); - } - - // handle last rules - if (windowEnd.equals(LocalDateTime.MAX)) { - // setup at least one real rule, which closes off other windows nicely - maxLastRuleStartYear = Math.max(maxLastRuleStartYear, windowStartYear) + 1; - for (TZRule lastRule : lastRuleList) { - addRule(lastRule.year, maxLastRuleStartYear, lastRule.month, lastRule.dayOfMonthIndicator, - lastRule.dayOfWeek, lastRule.time, lastRule.adjustDays, lastRule.timeDefinition, - lastRule.savingAmountSecs); - lastRule.year = maxLastRuleStartYear + 1; - } - if (maxLastRuleStartYear == Year.MAX_VALUE) { - lastRuleList.clear(); - } else { - maxLastRuleStartYear++; - } - } else { - // convert all within the endYear limit - int endYear = windowEnd.getYear(); - for (TZRule lastRule : lastRuleList) { - addRule(lastRule.year, endYear + 1, lastRule.month, lastRule.dayOfMonthIndicator, - lastRule.dayOfWeek, lastRule.time, lastRule.adjustDays, lastRule.timeDefinition, - lastRule.savingAmountSecs); - } - lastRuleList.clear(); - maxLastRuleStartYear = Year.MAX_VALUE; - } - - // ensure lists are sorted - Collections.sort(ruleList); - Collections.sort(lastRuleList); - - // default fixed savings to zero - if (ruleList.size() == 0 && fixedSavingAmountSecs == null) { - fixedSavingAmountSecs = 0; - } - } - - /** - * Checks if the window is empty. - * - * @return true if the window is only a standard offset - */ - boolean isSingleWindowStandardOffset() { - return windowEnd.equals(LocalDateTime.MAX) && timeDefinition == TimeDefinition.WALL - && fixedSavingAmountSecs == null && lastRuleList.isEmpty() && ruleList.isEmpty(); - } - - /** - * Creates the wall offset for the local date-time at the end of the window. - * - * @param savingsSecs the amount of savings in use in seconds - * @return the created date-time epoch second in the wall offset, not null - */ - ZoneOffset createWallOffset(int savingsSecs) { - return ZoneOffset.ofTotalSeconds(standardOffset.getTotalSeconds() + savingsSecs); - } - - /** - * Creates the offset date-time for the local date-time at the end of the window. - * - * @param savingsSecs the amount of savings in use in seconds - * @return the created date-time epoch second in the wall offset, not null - */ - long createDateTimeEpochSecond(int savingsSecs) { - ZoneOffset wallOffset = createWallOffset(savingsSecs); - LocalDateTime ldt = timeDefinition.createDateTime(windowEnd, standardOffset, wallOffset); - return ldt.toEpochSecond(wallOffset); - } - } - - //----------------------------------------------------------------------- - /** - * A definition of the way a local time can be converted to an offset time. - */ - class TZRule implements Comparable { - /** The year. */ - private int year; - /** The month. */ - private Month month; - /** The day-of-month. */ - private int dayOfMonthIndicator; - /** The day-of-month. */ - private DayOfWeek dayOfWeek; - /** The local time. */ - private LocalTime time; - /** The local time days adjustment. */ - private int adjustDays; - /** The type of the time. */ - private TimeDefinition timeDefinition; - /** The amount of the saving to be applied after this point. */ - private int savingAmountSecs; - - /** - * Constructor. - * - * @param year the year - * @param month the month, not null - * @param dayOfMonthIndicator the day-of-month of the transition, adjusted by dayOfWeek, - * from 1 to 31 adjusted later, or -1 to -28 adjusted earlier from the last day of the month - * @param dayOfWeek the day-of-week, null if day-of-month is exact - * @param time the time, not null - * @param adjustDays the time day adjustment - * @param timeDefinition the time definition, not null - * @param savingAfterSecs the savings amount in seconds - */ - TZRule(int year, Month month, int dayOfMonthIndicator, - DayOfWeek dayOfWeek, LocalTime time, int adjustDays, - TimeDefinition timeDefinition, int savingAfterSecs) { - super(); - this.year = year; - this.month = month; - this.dayOfMonthIndicator = dayOfMonthIndicator; - this.dayOfWeek = dayOfWeek; - this.time = time; - this.adjustDays = adjustDays; - this.timeDefinition = timeDefinition; - this.savingAmountSecs = savingAfterSecs; - } - - /** - * Converts this to a transition. - * - * @param standardOffset the active standard offset, not null - * @param savingsBeforeSecs the active savings in seconds - * @return the transition, not null - */ - ZoneOffsetTransition toTransition(ZoneOffset standardOffset, int savingsBeforeSecs) { - // copy of code in ZoneOffsetTransitionRule to avoid infinite loop - LocalDate date = toLocalDate(); - date = deduplicate(date); - LocalDateTime ldt = deduplicate(LocalDateTime.of(date.plusDays(adjustDays), time)); - ZoneOffset wallOffset = deduplicate(ZoneOffset.ofTotalSeconds(standardOffset.getTotalSeconds() - + savingsBeforeSecs)); - LocalDateTime dt = deduplicate(timeDefinition.createDateTime(ldt, standardOffset, wallOffset)); - ZoneOffset offsetAfter = deduplicate(ZoneOffset.ofTotalSeconds(standardOffset.getTotalSeconds() - + savingAmountSecs)); - return new ZoneOffsetTransition(dt, wallOffset, offsetAfter); - } - - /** - * 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 standardOffset, int savingsBeforeSecs) { - // optimize stored format - if (dayOfMonthIndicator < 0) { - if (month != Month.FEBRUARY) { - dayOfMonthIndicator = month.maxLength() - 6; - } - } - - // build rule - ZoneOffsetTransition trans = toTransition(standardOffset, savingsBeforeSecs); - return new ZoneOffsetTransitionRule( - month, dayOfMonthIndicator, dayOfWeek, time, adjustDays, timeDefinition, - standardOffset, trans.getOffsetBefore(), trans.getOffsetAfter()); - } - - @Override - public int compareTo(TZRule other) { - int cmp = year - other.year; - cmp = cmp == 0 ? month.compareTo(other.month) : cmp; - if (cmp == 0) { - // convert to date to handle dow/domIndicator/timeEndOfDay - LocalDate thisDate = toLocalDate(); - LocalDate otherDate = other.toLocalDate(); - cmp = thisDate.compareTo(otherDate); - } - if (cmp != 0) { - return cmp; - } - long timeSecs1 = time.toSecondOfDay() + adjustDays * 86400; - long timeSecs2 = other.time.toSecondOfDay() + other.adjustDays * 86400; - return Long.compare(timeSecs1, timeSecs2); - } - - private LocalDate toLocalDate() { - LocalDate date; - if (dayOfMonthIndicator < 0) { - int monthLen = month.length(IsoChronology.INSTANCE.isLeapYear(year)); - date = LocalDate.of(year, month, monthLen + 1 + dayOfMonthIndicator); - if (dayOfWeek != null) { - date = date.with(previousOrSame(dayOfWeek)); - } - } else { - date = LocalDate.of(year, month, dayOfMonthIndicator); - if (dayOfWeek != null) { - date = date.with(nextOrSame(dayOfWeek)); - } - } - return date; - } - - // no equals() or hashCode() - } - -} diff --git a/tests/src/test/java/org/teavm/classlib/java/time/zone/TestZoneRulesBuilder.java b/tests/src/test/java/org/teavm/classlib/java/time/zone/TestZoneRulesBuilder.java deleted file mode 100644 index c37c69f41..000000000 --- a/tests/src/test/java/org/teavm/classlib/java/time/zone/TestZoneRulesBuilder.java +++ /dev/null @@ -1,1158 +0,0 @@ -/* - * Copyright 2020 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos -* - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of JSR-310 nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.threeten.bp.zone; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.threeten.bp.DayOfWeek.FRIDAY; -import static org.threeten.bp.DayOfWeek.MONDAY; -import static org.threeten.bp.DayOfWeek.SATURDAY; -import static org.threeten.bp.DayOfWeek.SUNDAY; -import static org.threeten.bp.DayOfWeek.THURSDAY; -import static org.threeten.bp.DayOfWeek.TUESDAY; -import static org.threeten.bp.Month.APRIL; -import static org.threeten.bp.Month.AUGUST; -import static org.threeten.bp.Month.FEBRUARY; -import static org.threeten.bp.Month.MARCH; -import static org.threeten.bp.Month.MAY; -import static org.threeten.bp.Month.NOVEMBER; -import static org.threeten.bp.Month.OCTOBER; -import static org.threeten.bp.Month.SEPTEMBER; -import static org.threeten.bp.zone.ZoneOffsetTransitionRule.TimeDefinition.STANDARD; -import static org.threeten.bp.zone.ZoneOffsetTransitionRule.TimeDefinition.UTC; -import static org.threeten.bp.zone.ZoneOffsetTransitionRule.TimeDefinition.WALL; -import java.util.List; -import org.junit.runner.RunWith; -import org.teavm.junit.TeaVMTestRunner; -import org.teavm.junit.WholeClassCompilation; -import org.testng.annotations.Test; -import org.threeten.bp.DateTimeException; -import org.threeten.bp.LocalDateTime; -import org.threeten.bp.LocalTime; -import org.threeten.bp.Month; -import org.threeten.bp.Year; -import org.threeten.bp.ZoneOffset; -import org.threeten.bp.zone.ZoneOffsetTransitionRule.TimeDefinition; - -/** - * Test ZoneRulesBuilder. - */ -@Test -@RunWith(TeaVMTestRunner.class) -@WholeClassCompilation -public class TestZoneRulesBuilder { - - private static final ZoneOffset OFFSET_1 = ZoneOffset.ofHours(1); - private static final ZoneOffset OFFSET_2 = ZoneOffset.ofHours(2); - private static final ZoneOffset OFFSET_1_15 = ZoneOffset.ofHoursMinutes(1, 15); - private static final ZoneOffset OFFSET_2_30 = ZoneOffset.ofHoursMinutes(2, 30); - private static final int PERIOD_0 = 0; - private static final int PERIOD_1HOUR = 60 * 60; - private static final int PERIOD_1HOUR30MIN = ((1 * 60) + 30) * 60; - private static final LocalDateTime DATE_TIME_FIRST = dateTime(Year.MIN_VALUE, 1, 1, 0, 0); - private static final LocalDateTime DATE_TIME_LAST = dateTime(Year.MAX_VALUE, 12, 31, 23, 59); - private static final LocalDateTime DATE_TIME_2008_01_01 = dateTime(2008, 1, 1, 0, 0); - private static final LocalDateTime DATE_TIME_2008_07_01 = dateTime(2008, 7, 1, 0, 0); - - //----------------------------------------------------------------------- - // toRules() - //----------------------------------------------------------------------- - @Test(expectedExceptions = IllegalStateException.class) - public void test_toRules_noWindows() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.toRules("Europe/London"); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_toRules_null() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_2_30); - b.toRules(null); - } - - //----------------------------------------------------------------------- - // Combined - //----------------------------------------------------------------------- - @Test - public void test_combined_singleCutover() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(1950, 1, 1, 1, 0), STANDARD); - b.addWindowForever(OFFSET_2); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertGap(test, 1950, 1, 1, 1, 30, OFFSET_1, OFFSET_2); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_2); - } - - @Test - public void test_combined_localFixedRules() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1_15, dateTime(1920, 1, 1, 1, 0), WALL); - b.addWindow(OFFSET_1, dateTime(1950, 1, 1, 1, 0), WALL); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - b.addRuleToWindow(2000, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1_15); - assertOverlap(test, 1920, 1, 1, 0, 55, OFFSET_1_15, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_1); - assertOffsetInfo(test, dateTime(1800, 7, 1, 1, 0), OFFSET_1_15); - assertOffsetInfo(test, dateTime(1920, 1, 1, 1, 0), OFFSET_1); - assertOffsetInfo(test, dateTime(1960, 1, 1, 1, 0), OFFSET_1); - assertOffsetInfo(test, dateTime(2000, 1, 1, 1, 0), OFFSET_1); - assertOffsetInfo(test, DATE_TIME_2008_01_01, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_2008_07_01, OFFSET_2_30); - assertGap(test, 2008, 3, 30, 1, 20, OFFSET_1, OFFSET_2_30); - assertOverlap(test, 2008, 10, 26, 0, 20, OFFSET_2_30, OFFSET_1); - } - - @Test - public void test_combined_windowChangeDuringDST() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(2000, 7, 1, 1, 0), WALL); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2000, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(2, 0), false, WALL, PERIOD_0); - ZoneRules test = b.toRules("Europe/Dublin"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_1); - - assertOffsetInfo(test, dateTime(2000, 1, 1, 0, 0), OFFSET_1); - assertOffsetInfo(test, dateTime(2000, 7, 1, 0, 0), OFFSET_1); - assertGap(test, 2000, 7, 1, 1, 20, OFFSET_1, OFFSET_2); - assertOffsetInfo(test, dateTime(2000, 7, 1, 3, 0), OFFSET_2); - assertOverlap(test, 2000, 10, 29, 1, 20, OFFSET_2, OFFSET_1); - assertOffsetInfo(test, dateTime(2000, 12, 1, 0, 0), OFFSET_1); - } - - @Test - public void test_combined_windowChangeWithinDST() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(2000, 7, 1, 1, 0), WALL); - b.addWindow(OFFSET_1, dateTime(2000, 8, 1, 2, 0), WALL); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2000, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(2, 0), false, WALL, PERIOD_0); - b.addWindowForever(OFFSET_1); - ZoneRules test = b.toRules("Europe/Dublin"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_1); - - assertOffsetInfo(test, dateTime(2000, 1, 1, 0, 0), OFFSET_1); - assertOffsetInfo(test, dateTime(2000, 7, 1, 0, 0), OFFSET_1); - assertGap(test, 2000, 7, 1, 1, 20, OFFSET_1, OFFSET_2); - assertOffsetInfo(test, dateTime(2000, 7, 1, 3, 0), OFFSET_2); - assertOverlap(test, 2000, 8, 1, 1, 20, OFFSET_2, OFFSET_1); - assertOffsetInfo(test, dateTime(2000, 12, 1, 0, 0), OFFSET_1); - } - - @Test - public void test_combined_endsInSavings() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1_15, dateTime(1920, 1, 1, 1, 0), WALL); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_0); - b.addRuleToWindow(2000, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR); - ZoneRules test = b.toRules("Pacific/Auckland"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1_15); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_2); - assertOverlap(test, 1920, 1, 1, 0, 55, OFFSET_1_15, OFFSET_1); - assertOffsetInfo(test, dateTime(2000, 3, 26, 0, 59), OFFSET_1); - assertOffsetInfo(test, dateTime(2000, 3, 26, 1, 00), OFFSET_1); - assertGap(test, 2000, 10, 29, 1, 20, OFFSET_1, OFFSET_2); - assertOverlap(test, 2001, 3, 25, 0, 20, OFFSET_2, OFFSET_1); - assertGap(test, 2001, 10, 28, 1, 20, OFFSET_1, OFFSET_2); - } - - @Test - public void test_combined_closeTransitions() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(1920, 1, 1, 1, 0), WALL); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, MARCH, 20, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2000, MARCH, 20, time(4, 2), false, WALL, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_1); - assertOffsetInfo(test, dateTime(2000, 3, 20, 1, 59), OFFSET_1); - assertGap(test, 2000, 3, 20, 2, 0, OFFSET_1, OFFSET_2); - assertGap(test, 2000, 3, 20, 2, 59, OFFSET_1, OFFSET_2); - assertOffsetInfo(test, dateTime(2000, 3, 20, 3, 0), OFFSET_2); - assertOffsetInfo(test, dateTime(2000, 3, 20, 3, 1), OFFSET_2); - assertOverlap(test, 2000, 3, 20, 3, 2, OFFSET_2, OFFSET_1); - assertOverlap(test, 2000, 3, 20, 4, 1, OFFSET_2, OFFSET_1); - assertOffsetInfo(test, dateTime(2000, 3, 20, 4, 2), OFFSET_1); - } - - @Test - public void test_combined_closeTransitionsMeet() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(1920, 1, 1, 1, 0), WALL); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, MARCH, 20, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2000, MARCH, 20, time(4, 0), false, WALL, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_1); - assertOffsetInfo(test, dateTime(2000, 3, 20, 1, 59), OFFSET_1); - assertGap(test, 2000, 3, 20, 2, 0, OFFSET_1, OFFSET_2); - assertGap(test, 2000, 3, 20, 2, 59, OFFSET_1, OFFSET_2); - assertOverlap(test, 2000, 3, 20, 3, 0, OFFSET_2, OFFSET_1); - assertOverlap(test, 2000, 3, 20, 3, 59, OFFSET_2, OFFSET_1); - assertOffsetInfo(test, dateTime(2000, 3, 20, 4, 0), OFFSET_1); - } - -// TODO: handle conflicting gap/overlap -// public void test_combined_closeTransitionsConflictPartly() { -// TransitionRulesBuilder b = new TransitionRulesBuilder(OFFSET_1, dateTime(1920, 1, 1, 1, 0), WALL); -// b.addWindowForever(OFFSET_1); -// b.addRuleToWindow(2000, MARCH, 20, time(2, 0), WALL, PERIOD_1HOUR); -// b.addRuleToWindow(2000, MARCH, 20, time(3, 30), WALL, PERIOD_0); -// ZoneRules test = b.toRules("Europe/London"); -// assertOffsetInfo(test, DATE_TIME_FIRST), OFFSET_1); -// assertOffsetInfo(test, DATE_TIME_LAST), OFFSET_1); -// assertOffsetInfo(test, dateTime(2000, 3, 20, 1, 59)), OFFSET_1); -// assertGap(test, 2000, 3, 20, 2, 0, OFFSET_1, OFFSET_2); -// assertGap(test, 2000, 3, 20, 2, 29, OFFSET_1, OFFSET_2); -// assertOffsetInfo(test, dateTime(2000, 3, 20, 2, 30)), OFFSET_1); -// assertOffsetInfo(test, dateTime(2000, 3, 20, 2, 59)), OFFSET_1); -// assertOverlap(test, 2000, 3, 20, 3, 0, OFFSET_2, OFFSET_1); -// assertOverlap(test, 2000, 3, 20, 3, 29, OFFSET_2, OFFSET_1); -// assertOffsetInfo(test, dateTime(2000, 3, 20, 3, 30)), OFFSET_1); -// } - - @Test - public void test_combined_weirdSavingsBeforeLast() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(1920, 1, 1, 1, 0), WALL); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(1998, MARCH, 20, time(2, 0), false, WALL, PERIOD_1HOUR30MIN); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, 20, null, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2000, Year.MAX_VALUE, OCTOBER, 20, null, time(2, 0), false, WALL, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_1); - - assertOffsetInfo(test, dateTime(1999, 1, 1, 0, 0), OFFSET_2_30); - assertOverlap(test, 2000, 3, 20, 1, 30, OFFSET_2_30, OFFSET_2); - assertOverlap(test, 2000, 10, 20, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2001, 3, 20, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2001, 10, 20, 1, 30, OFFSET_2, OFFSET_1); - } - - @Test - public void test_combined_differentLengthLastRules1() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(1920, 1, 1, 1, 0), WALL); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(1998, MARCH, 20, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(1998, Year.MAX_VALUE, OCTOBER, 30, null, time(2, 0), false, WALL, PERIOD_0); - b.addRuleToWindow(1999, MARCH, 21, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2000, MARCH, 22, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2001, MARCH, 23, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2002, Year.MAX_VALUE, MARCH, 24, null, time(2, 0), false, WALL, PERIOD_1HOUR); - ZoneRules test = b.toRules("Europe/London"); - - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_1); - - assertGap(test, 1998, 3, 20, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 1998, 10, 30, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 1999, 3, 21, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 1999, 10, 30, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2000, 3, 22, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2000, 10, 30, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2001, 3, 23, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2001, 10, 30, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2002, 3, 24, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2002, 10, 30, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2003, 3, 24, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2003, 10, 30, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2004, 3, 24, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2004, 10, 30, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2005, 3, 24, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2005, 10, 30, 1, 30, OFFSET_2, OFFSET_1); - } - - @Test - public void test_combined_differentLengthLastRules2() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(1920, 1, 1, 1, 0), WALL); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(1998, Year.MAX_VALUE, MARCH, 30, null, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(1998, OCTOBER, 20, time(2, 0), false, WALL, PERIOD_0); - b.addRuleToWindow(1999, OCTOBER, 21, time(2, 0), false, WALL, PERIOD_0); - b.addRuleToWindow(2000, OCTOBER, 22, time(2, 0), false, WALL, PERIOD_0); - b.addRuleToWindow(2001, OCTOBER, 23, time(2, 0), false, WALL, PERIOD_0); - b.addRuleToWindow(2002, Year.MAX_VALUE, OCTOBER, 24, null, time(2, 0), false, WALL, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_1); - - assertGap(test, 1998, 3, 30, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 1998, 10, 20, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 1999, 3, 30, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 1999, 10, 21, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2000, 3, 30, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2000, 10, 22, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2001, 3, 30, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2001, 10, 23, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2002, 3, 30, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2002, 10, 24, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2003, 3, 30, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2003, 10, 24, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2004, 3, 30, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2004, 10, 24, 1, 30, OFFSET_2, OFFSET_1); - assertGap(test, 2005, 3, 30, 2, 30, OFFSET_1, OFFSET_2); - assertOverlap(test, 2005, 10, 24, 1, 30, OFFSET_2, OFFSET_1); - } - - @Test - public void test_twoChangesSameDay() { - // ensures that TZRule.compare works - ZoneOffset plus2 = ZoneOffset.ofHours(2); - ZoneOffset plus3 = ZoneOffset.ofHours(3); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(plus2); - b.addRuleToWindow(2010, 2010, SEPTEMBER, 10, null, time(12, 0), false, STANDARD, PERIOD_1HOUR); - b.addRuleToWindow(2010, 2010, SEPTEMBER, 10, null, time(23, 0), false, STANDARD, PERIOD_0); - ZoneRules test = b.toRules("Africa/Cairo"); - - assertOffsetInfo(test, DATE_TIME_FIRST, plus2); - assertOffsetInfo(test, DATE_TIME_LAST, plus2); - - assertGap(test, 2010, 9, 10, 12, 0, plus2, plus3); // jump forward from 12:00 to 13:00 on Tue 10th Sep - // overlaps from Wed 11th Sep 00:00 back to Tue 10th Sep 23:00 - assertOverlap(test, 2010, 9, 10, 23, 0, plus3, plus2); - } - - @Test - public void test_twoChangesDifferentDefinition() { - // ensures that TZRule.compare works - ZoneOffset plus2 = ZoneOffset.ofHours(2); - ZoneOffset plus3 = ZoneOffset.ofHours(3); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(plus2); - b.addRuleToWindow(2010, 2010, SEPTEMBER, -1, TUESDAY, time(0, 0), false, STANDARD, PERIOD_1HOUR); - b.addRuleToWindow(2010, 2010, SEPTEMBER, 29, null, time(23, 0), false, STANDARD, PERIOD_0); - ZoneRules test = b.toRules("Africa/Cairo"); - - assertOffsetInfo(test, DATE_TIME_FIRST, plus2); - assertOffsetInfo(test, DATE_TIME_LAST, plus2); - - assertGap(test, 2010, 9, 28, 0, 0, plus2, plus3); // jump forward from 00:00 to 01:00 on Tue 28th Sep - // overlaps from Thu 30th Sep 00:00 back to Wed 29th Sep 23:00 - assertOverlap(test, 2010, 9, 29, 23, 0, plus3, plus2); - } - - //----------------------------------------------------------------------- - @Test - public void test_argentina() { - // # On October 3, 1999, 0:00 local, Argentina implemented daylight savings time, - // # which did not result in the switch of a time zone, as they stayed 9 hours - // # from the International Date Line. - // Rule Arg 1989 1993 - Mar Sun>=1 0:00 0 - - // Rule Arg 1989 1992 - Oct Sun>=15 0:00 1:00 S - // Rule Arg 1999 only - Oct Sun>=1 0:00 1:00 S - // Rule Arg 2000 only - Mar 3 0:00 0 - - // Zone America/Argentina/Tucuman -4:20:52 - LMT 1894 Oct 31 - // -3:00 Arg AR%sT 1999 Oct 3 - // -4:00 Arg AR%sT 2000 Mar 3 - - ZoneOffset minus3 = ZoneOffset.ofHours(-3); - ZoneOffset minus4 = ZoneOffset.ofHours(-4); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(minus3, dateTime(1900, 1, 1, 0, 0), WALL); - b.addWindow(minus3, dateTime(1999, 10, 3, 0, 0), WALL); - b.addRuleToWindow(1993, MARCH, 3, time(0, 0), false, WALL, PERIOD_0); - b.addRuleToWindow(1999, OCTOBER, 3, time(0, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2000, MARCH, 3, time(0, 0), false, WALL, PERIOD_0); - b.addWindow(minus4, dateTime(2000, 3, 3, 0, 0), WALL); - b.addRuleToWindow(1993, MARCH, 3, time(0, 0), false, WALL, PERIOD_0); - b.addRuleToWindow(1999, OCTOBER, 3, time(0, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2000, MARCH, 3, time(0, 0), false, WALL, PERIOD_0); - b.addWindowForever(minus3); - ZoneRules test = b.toRules("America/Argentina/Tucuman"); - - assertOffsetInfo(test, DATE_TIME_FIRST, minus3); - assertOffsetInfo(test, DATE_TIME_LAST, minus3); - - assertOffsetInfo(test, dateTime(1999, 10, 2, 22, 59), minus3); - assertOffsetInfo(test, dateTime(1999, 10, 2, 23, 59), minus3); - assertOffsetInfo(test, dateTime(1999, 10, 3, 0, 0), minus3); - assertOffsetInfo(test, dateTime(1999, 10, 3, 1, 0), minus3); - - assertOffsetInfo(test, dateTime(2000, 3, 2, 22, 59), minus3); - assertOffsetInfo(test, dateTime(2000, 3, 2, 23, 59), minus3); - assertOffsetInfo(test, dateTime(2000, 3, 3, 0, 0), minus3); - assertOffsetInfo(test, dateTime(2000, 3, 3, 1, 0), minus3); - } - - @Test - public void test_cairo_dateChange() { - // Rule Egypt 2008 max - Apr lastFri 0:00s 1:00 S - // Rule Egypt 2008 max - Aug lastThu 23:00s 0 - - // Zone Africa/Cairo 2:05:00 - LMT 1900 Oct - // 2:00 Egypt EE%sT - ZoneOffset plus2 = ZoneOffset.ofHours(2); - ZoneOffset plus3 = ZoneOffset.ofHours(3); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(plus2); - b.addRuleToWindow(2008, Year.MAX_VALUE, APRIL, -1, FRIDAY, time(0, 0), false, STANDARD, PERIOD_1HOUR); - b.addRuleToWindow(2008, Year.MAX_VALUE, AUGUST, -1, THURSDAY, time(23, 0), false, STANDARD, PERIOD_0); - ZoneRules test = b.toRules("Africa/Cairo"); - - assertOffsetInfo(test, DATE_TIME_FIRST, plus2); - assertOffsetInfo(test, DATE_TIME_LAST, plus2); - - assertGap(test, 2009, 4, 24, 0, 0, plus2, plus3); - assertOverlap(test, 2009, 8, 27, 23, 0, plus3, plus2); // overlaps from Fri 00:00 back to Thu 23:00 - } - - @Test - public void test_cairo_twoChangesSameMonth() { - // 2011i - // Rule Egypt 2010 only - Aug 11 0:00 0 - - // Rule Egypt 2010 only - Sep 10 0:00 1:00 S - // Rule Egypt 2010 only - Sep lastThu 23:00s 0 - - // Zone Africa/Cairo 2:05:00 - LMT 1900 Oct - // 2:00 Egypt EE%sT - ZoneOffset plus2 = ZoneOffset.ofHours(2); - ZoneOffset plus3 = ZoneOffset.ofHours(3); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(plus2); - b.addRuleToWindow(2010, 2010, AUGUST, 11, null, time(0, 0), false, STANDARD, PERIOD_0); - b.addRuleToWindow(2010, 2010, SEPTEMBER, 10, null, time(0, 0), false, STANDARD, PERIOD_1HOUR); - b.addRuleToWindow(2010, 2010, SEPTEMBER, -1, THURSDAY, time(23, 0), false, STANDARD, PERIOD_0); - ZoneRules test = b.toRules("Africa/Cairo"); - - assertOffsetInfo(test, DATE_TIME_FIRST, plus2); - assertOffsetInfo(test, DATE_TIME_LAST, plus2); - - assertGap(test, 2010, 9, 10, 0, 0, plus2, plus3); // jump forward from 00:00 to 01:00 on Fri 10th Sep - // overlaps from Fri 1st Oct 00:00 back to Thu 30th Sep 23:00 (!!!) - assertOverlap(test, 2010, 9, 30, 23, 0, plus3, plus2); - } - - @Test - public void test_sofia_lastRuleClash() { - // UTC rule change in 1996 occurs after Wall change - // need to ensure that last rule is only applied to last window - // Rule E-Eur 1981 max - Mar lastSun 0:00 1:00 S - // Rule E-Eur 1996 max - Oct lastSun 0:00 0 - - // Rule EU 1981 max - Mar lastSun 1:00u 1:00 S - // Rule EU 1996 max - Oct lastSun 1:00u 0 - - // Zone Europe/Sofia - // 2:00 E-Eur EE%sT 1997 - // 2:00 EU EE%sT - ZoneOffset plus2 = ZoneOffset.ofHours(2); - ZoneOffset plus3 = ZoneOffset.ofHours(3); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(plus2, dateTime(1997, 1, 1, 0, 0), WALL); - b.addRuleToWindow(1996, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(1996, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_0); - b.addWindowForever(plus2); - b.addRuleToWindow(1996, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, UTC, PERIOD_1HOUR); - b.addRuleToWindow(1996, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(1, 0), false, UTC, PERIOD_0); - ZoneRules test = b.toRules("Europe/Sofia"); - - assertOffsetInfo(test, DATE_TIME_FIRST, plus2); - assertOffsetInfo(test, DATE_TIME_LAST, plus2); - - assertGap(test, 1996, 3, 31, 1, 0, plus2, plus3); - assertOverlap(test, 1996, 10, 27, 0, 0, plus3, plus2); - assertOffsetInfo(test, dateTime(1996, 10, 27, 1, 0), plus2); - assertOffsetInfo(test, dateTime(1996, 10, 27, 2, 0), plus2); - assertOffsetInfo(test, dateTime(1996, 10, 27, 3, 0), plus2); - assertOffsetInfo(test, dateTime(1996, 10, 27, 4, 0), plus2); - } - - @Test - public void test_prague() { - // need to calculate savings applicable at window start based on - // first rule being transition from no savings to DST - // Rule C-Eur 1944 1945 - Apr Mon>=1 2:00s 1:00 S - // Rule C-Eur 1944 only - Oct 2 2:00s 0 - - // Rule C-Eur 1945 only - Sep 16 2:00s 0 - - // Rule Czech 1945 only - Apr 8 2:00s 1:00 S - // Rule Czech 1945 only - Nov 18 2:00s 0 - - // Zone Europe/Prague 0:57:44 - LMT 1850 - // 0:57:44 - PMT 1891 Oct - // 1:00 C-Eur CE%sT 1944 Sep 17 2:00s - // 1:00 Czech CE%sT 1979 - // 1:00 EU CE%sT - ZoneOffset plus1 = ZoneOffset.ofHours(1); - ZoneOffset plus2 = ZoneOffset.ofHours(2); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(plus1, dateTime(1944, 9, 17, 2, 0), STANDARD); - b.addRuleToWindow(1944, 1945, APRIL, 1, MONDAY, time(2, 0), false, STANDARD, PERIOD_1HOUR); - b.addRuleToWindow(1944, OCTOBER, 2, time(2, 0), false, STANDARD, PERIOD_0); - b.addRuleToWindow(1945, SEPTEMBER, 16, time(2, 0), false, STANDARD, PERIOD_0); - b.addWindow(plus1, dateTime(1979, 1, 1, 0, 0), WALL); - b.addRuleToWindow(1945, APRIL, 8, time(2, 0), false, STANDARD, PERIOD_1HOUR); - b.addRuleToWindow(1945, NOVEMBER, 18, time(2, 0), false, STANDARD, PERIOD_0); - b.addWindowForever(plus1); - ZoneRules test = b.toRules("Europe/Sofia"); - - assertOffsetInfo(test, DATE_TIME_FIRST, plus1); - assertOffsetInfo(test, DATE_TIME_LAST, plus1); - - assertGap(test, 1944, 4, 3, 2, 30, plus1, plus2); - assertOverlap(test, 1944, 9, 17, 2, 30, plus2, plus1); - assertOffsetInfo(test, dateTime(1944, 9, 17, 3, 30), plus1); - assertOffsetInfo(test, dateTime(1944, 9, 17, 4, 30), plus1); - assertGap(test, 1945, 4, 8, 2, 30, plus1, plus2); - assertOverlap(test, 1945, 11, 18, 2, 30, plus2, plus1); - } - - @Test - public void test_tbilisi() { - // has transition into and out of 1 year of permanent DST (Mar96-Oct97) - // where the date in the window and rule are the same - // this is weird because the wall time in the rule is amended by the actual - // wall time from the zone lines - // Rule E-EurAsia 1981 max - Mar lastSun 0:00 1:00 S - // Rule E-EurAsia 1979 1995 - Sep lastSun 0:00 0 - - // Rule E-EurAsia 1996 max - Oct lastSun 0:00 0 - - // Zone Asia/Tbilisi 2:59:16 - LMT 1880 - // 4:00 E-EurAsia GE%sT 1996 Oct lastSun - // 4:00 1:00 GEST 1997 Mar lastSun - // 4:00 E-EurAsia GE%sT 2004 Jun 27 - // 3:00 RussiaAsia GE%sT 2005 Mar lastSun 2:00 - // 4:00 - GET - ZoneOffset plus4 = ZoneOffset.ofHours(4); - ZoneOffset plus5 = ZoneOffset.ofHours(5); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(plus4, dateTime(1996, 10, 27, 0, 0), WALL); - b.addRuleToWindow(1996, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(0, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(1996, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(0, 0), false, WALL, PERIOD_0); - b.addWindow(plus4, dateTime(1997, 3, 30, 0, 0), WALL); - b.setFixedSavingsToWindow(PERIOD_1HOUR); - b.addWindowForever(plus4); - b.addRuleToWindow(1996, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(0, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(1996, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(0, 0), false, WALL, PERIOD_0); - ZoneRules test = b.toRules("Europe/Sofia"); - - assertOffsetInfo(test, DATE_TIME_FIRST, plus4); - assertOffsetInfo(test, DATE_TIME_LAST, plus4); - - assertGap(test, 1996, 3, 31, 0, 30, plus4, plus5); - // assertOverlap(test, 1996, 10, 26, 23, 30, plus5, plus4); // fixed DST blocks overlap - assertOffsetInfo(test, dateTime(1996, 10, 26, 22, 30), plus5); - assertOffsetInfo(test, dateTime(1996, 10, 26, 23, 30), plus5); - assertOffsetInfo(test, dateTime(1996, 10, 27, 0, 30), plus5); - // assertOverlap(test, 1997, 3, 30, 0, 30, plus5, plus4); // end of fixed blocks overlap - assertOffsetInfo(test, dateTime(1997, 3, 29, 22, 30), plus5); - assertOffsetInfo(test, dateTime(1997, 3, 29, 23, 30), plus5); - assertOffsetInfo(test, dateTime(1997, 3, 30, 0, 30), plus5); - assertOffsetInfo(test, dateTime(1997, 3, 30, 1, 30), plus5); - assertOffsetInfo(test, dateTime(1997, 3, 30, 2, 30), plus5); - assertOverlap(test, 1997, 10, 25, 23, 30, plus5, plus4); - } - - @Test - public void test_vincennes() { - // need to ensure that at least one real rule is added to expand on the last rule - // Rule US 2007 max - Mar Sun>=8 2:00 1:00 D - // Rule US 2007 max - Nov Sun>=1 2:00 0 S - // -5:00 - EST 2006 Apr 2 2:00 - // -6:00 US C%sT 2007 Nov 4 2:00 - // -5:00 US E%sT - ZoneOffset minus5 = ZoneOffset.ofHours(-5); - ZoneOffset minus6 = ZoneOffset.ofHours(-6); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(minus6, dateTime(2007, 11, 4, 2, 0), WALL); - b.addRuleToWindow(2007, Year.MAX_VALUE, MARCH, 8, SUNDAY, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2007, Year.MAX_VALUE, NOVEMBER, 1, SUNDAY, time(2, 0), false, WALL, PERIOD_0); - b.addWindowForever(minus5); - b.addRuleToWindow(2007, Year.MAX_VALUE, MARCH, 8, SUNDAY, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2007, Year.MAX_VALUE, NOVEMBER, 1, SUNDAY, time(2, 0), false, WALL, PERIOD_0); - ZoneRules test = b.toRules("America/Indiana/Vincennes"); - - assertOffsetInfo(test, DATE_TIME_FIRST, minus6); - assertOffsetInfo(test, DATE_TIME_LAST, minus5); - - assertOffsetInfo(test, dateTime(2007, 3, 11, 0, 0), minus6); - assertOffsetInfo(test, dateTime(2007, 3, 11, 1, 0), minus6); - assertGap(test, 2007, 3, 11, 2, 0, minus6, minus5); - assertOffsetInfo(test, dateTime(2007, 3, 11, 3, 0), minus5); - assertOffsetInfo(test, dateTime(2007, 3, 11, 4, 0), minus5); - assertOffsetInfo(test, dateTime(2007, 3, 11, 5, 0), minus5); - } - - @Test - public void test_iqaluit() { - // two hour overlap due to end of daylight and change of standard offset - // Rule Canada 1987 2006 - Apr Sun>=1 2:00 1:00 D - // Rule Canada 1974 2006 - Oct lastSun 2:00 0 S - // Rule NT_YK 1987 2006 - Apr Sun>=1 2:00 1:00 D - // Rule NT_YK 1980 2006 - Oct lastSun 2:00 0 S - // -5:00 NT_YK E%sT 1999 Oct 31 2:00 - // -6:00 Canada C%sT - ZoneOffset minus4 = ZoneOffset.ofHours(-4); - ZoneOffset minus5 = ZoneOffset.ofHours(-5); - ZoneOffset minus6 = ZoneOffset.ofHours(-6); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(minus5, dateTime(1999, 10, 31, 2, 0), WALL); - b.addRuleToWindow(1987, Year.MAX_VALUE, APRIL, 1, SUNDAY, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(1987, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(2, 0), false, WALL, PERIOD_0); - b.addWindowForever(minus6); - b.addRuleToWindow(1987, Year.MAX_VALUE, APRIL, 1, SUNDAY, time(2, 0), false, WALL, PERIOD_1HOUR); - b.addRuleToWindow(1987, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(2, 0), false, WALL, PERIOD_0); - ZoneRules test = b.toRules("America/Iqaluit"); - - assertOffsetInfo(test, DATE_TIME_FIRST, minus5); - assertOffsetInfo(test, DATE_TIME_LAST, minus6); - - assertOffsetInfo(test, dateTime(1999, 10, 30, 23, 0), minus4); - assertOverlap(test, 1999, 10, 31, 0, 0, minus4, minus6); - assertOverlap(test, 1999, 10, 31, 1, 0, minus4, minus6); - assertOverlap(test, 1999, 10, 31, 1, 59, minus4, minus6); - assertOffsetInfo(test, dateTime(1999, 10, 31, 2, 0), minus6); - assertOffsetInfo(test, dateTime(1999, 10, 31, 3, 0), minus6); - } - - @Test - public void test_jordan2400() { - // rule is 24:00 - this is simplified from the TZDB - // Rule Jordan 2002 max - Mar lastThu 24:00 1:00 S - // Rule Jordan 2002 max - Sep lastFri 0:00s 0 - - // # Zone NAME GMTOFF RULES FORMAT [UNTIL] - // 2:00 Jordan EE%sT - ZoneOffset plus2 = ZoneOffset.ofHours(2); - ZoneOffset plus3 = ZoneOffset.ofHours(3); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(plus2); - b.addRuleToWindow(2002, Year.MAX_VALUE, MARCH, -1, THURSDAY, time(0, 0), true, WALL, PERIOD_1HOUR); - b.addRuleToWindow(2002, Year.MAX_VALUE, SEPTEMBER, -1, FRIDAY, time(0, 0), false, STANDARD, PERIOD_0); - ZoneRules test = b.toRules("Asia/Amman"); - - assertOffsetInfo(test, DATE_TIME_FIRST, plus2); - assertOffsetInfo(test, DATE_TIME_LAST, plus2); - - assertGap(test, 2002, 3, 29, 0, 0, plus2, plus3); - assertOffsetInfo(test, dateTime(2002, 3, 28, 23, 0), plus2); - assertOffsetInfo(test, dateTime(2002, 3, 29, 1, 0), plus3); - - assertOverlap(test, 2002, 9, 27, 0, 0, plus3, plus2); - assertOffsetInfo(test, dateTime(2002, 9, 26, 23, 0), plus3); - assertOffsetInfo(test, dateTime(2002, 9, 27, 1, 0), plus2); - } - - @Test - public void test_japan2500() { - // cutover time overflows into next day - // Rule Japan 1948 only - May Sat>=1 24:00 1:00 D - // Rule Japan 1948 1951 - Sep Sat>=8 25:00 0 S - // Rule Japan 1949 only - Apr Sat>=1 24:00 1:00 D - // Rule Japan 1950 1951 - May Sat>=1 24:00 1:00 D - ZoneOffset plus9 = ZoneOffset.ofHours(9); - ZoneOffset plus10 = ZoneOffset.ofHours(10); - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(plus9); - b.addRuleToWindow(1948, 1948, MAY, 1, SATURDAY, time(0, 0), true, WALL, PERIOD_1HOUR); - b.addRuleToWindow(1948, 1951, SEPTEMBER, 8, SATURDAY, time(1, 0), 1, WALL, PERIOD_0); - b.addRuleToWindow(1949, 1949, APRIL, 1, SATURDAY, time(0, 0), true, WALL, PERIOD_1HOUR); - b.addRuleToWindow(1950, 1951, MAY, 1, SATURDAY, time(0, 0), true, WALL, PERIOD_1HOUR); - ZoneRules test = b.toRules("Japan"); - assertOffsetInfo(test, DATE_TIME_FIRST, plus9); - assertOffsetInfo(test, DATE_TIME_LAST, plus9); - // Sat>=1 => May 1st - assertGap(test, 1948, 5, 2, 0, 0, plus9, plus10); - assertOffsetInfo(test, dateTime(1948, 5, 1, 23, 0), plus9); - assertOffsetInfo(test, dateTime(1948, 5, 2, 1, 0), plus10); - // Sat>=8 => September 11th - assertOverlap(test, 1948, 9, 12, 0, 0, plus10, plus9); - assertOffsetInfo(test, dateTime(1948, 9, 11, 23, 0), plus10); - assertOffsetInfo(test, dateTime(1948, 9, 12, 1, 0), plus9); - // Sat>=1 => May 2nd - assertGap(test, 1949, 4, 3, 0, 0, plus9, plus10); - assertOffsetInfo(test, dateTime(1949, 4, 2, 23, 0), plus9); - assertOffsetInfo(test, dateTime(1949, 4, 3, 1, 0), plus10); - } - - //----------------------------------------------------------------------- - // addWindow() - //----------------------------------------------------------------------- - @Test - public void test_addWindow_constrainedRules() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(1800, 7, 1, 0, 0), WALL); - b.addWindow(OFFSET_1, dateTime(2008, 6, 30, 0, 0), STANDARD); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - b.addRuleToWindow(2000, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_2_30); - assertOffsetInfo(test, DATE_TIME_2008_01_01, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_2008_07_01, OFFSET_2_30); - assertGap(test, 2000, 3, 26, 1, 30, OFFSET_1, OFFSET_2_30); - assertOverlap(test, 2000, 10, 29, 0, 30, OFFSET_2_30, OFFSET_1); - assertGap(test, 2008, 3, 30, 1, 30, OFFSET_1, OFFSET_2_30); - assertOffsetInfo(test, dateTime(2008, 10, 26, 0, 30), OFFSET_2_30); - } - - @Test - public void test_addWindow_noRules() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(1800, 7, 1, 0, 0), WALL); - b.addWindow(OFFSET_1, dateTime(2008, 6, 30, 0, 0), STANDARD); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_2008_01_01, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_2008_07_01, OFFSET_1); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addWindow_nullOffset() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow((ZoneOffset) null, dateTime(2008, 6, 30, 0, 0), STANDARD); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addWindow_nullTime() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, (LocalDateTime) null, STANDARD); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addWindow_nullTimeDefinition() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(2008, 6, 30, 0, 0), (TimeDefinition) null); - } - - //----------------------------------------------------------------------- - // addWindowForever() - //----------------------------------------------------------------------- - @Test - public void test_addWindowForever_noRules() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_2008_01_01, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_2008_07_01, OFFSET_1); - } - - @Test - public void test_addWindowForever_rules() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - b.addRuleToWindow(2000, Year.MAX_VALUE, OCTOBER, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_2008_01_01, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_2008_07_01, OFFSET_2_30); - assertGap(test, 2008, 3, 30, 1, 20, OFFSET_1, OFFSET_2_30); - assertOverlap(test, 2008, 10, 26, 0, 20, OFFSET_2_30, OFFSET_1); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addWindowForever_nullOffset() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever((ZoneOffset) null); - } - - //----------------------------------------------------------------------- - // setFixedSavings() - //----------------------------------------------------------------------- - @Test - public void test_setFixedSavingsToWindow() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindow(OFFSET_1, dateTime(1800, 7, 1, 0, 0), WALL); - b.addWindowForever(OFFSET_1); - b.setFixedSavingsToWindow(PERIOD_1HOUR30MIN); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_1); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_2_30); - assertOffsetInfo(test, DATE_TIME_2008_01_01, OFFSET_2_30); - assertOffsetInfo(test, DATE_TIME_2008_07_01, OFFSET_2_30); - assertGap(test, 1800, 7, 1, 0, 0, OFFSET_1, OFFSET_2_30); - } - - @Test - public void test_setFixedSavingsToWindow_first() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.setFixedSavingsToWindow(PERIOD_1HOUR30MIN); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, DATE_TIME_FIRST, OFFSET_2_30); - assertOffsetInfo(test, DATE_TIME_LAST, OFFSET_2_30); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void test_setFixedSavingsToWindow_noWindow() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.setFixedSavingsToWindow(PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void test_setFixedSavingsToWindow_cannotMixSavingsWithRule() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, 2020, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - b.setFixedSavingsToWindow(PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void test_setFixedSavingsToWindow_cannotMixSavingsWithLastRule() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - b.setFixedSavingsToWindow(PERIOD_1HOUR30MIN); - } - - //----------------------------------------------------------------------- - // addRuleToWindow() - //----------------------------------------------------------------------- - @Test - public void test_addRuleToWindow_endOfMonth() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, 2001, MARCH, -1, SUNDAY, time(1, 0), false, UTC, PERIOD_1HOUR); - b.addRuleToWindow(2000, 2001, OCTOBER, -1, SUNDAY, time(1, 0), false, UTC, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, dateTime(1999, 7, 1, 0, 0), OFFSET_1); - - assertOffsetInfo(test, dateTime(2000, 1, 1, 0, 0), OFFSET_1); - assertGap(test, 2000, 3, 26, 2, 30, OFFSET_1, OFFSET_2); - assertOffsetInfo(test, dateTime(2000, 7, 1, 0, 0), OFFSET_2); - assertOverlap(test, 2000, 10, 29, 2, 30, OFFSET_2, OFFSET_1); - - assertOffsetInfo(test, dateTime(2001, 1, 1, 0, 0), OFFSET_1); - assertGap(test, 2001, 3, 25, 2, 30, OFFSET_1, OFFSET_2); - assertOffsetInfo(test, dateTime(2001, 7, 1, 0, 0), OFFSET_2); - assertOverlap(test, 2001, 10, 28, 2, 30, OFFSET_2, OFFSET_1); - - assertOffsetInfo(test, dateTime(2002, 7, 1, 0, 0), OFFSET_1); - } - - @Test - public void test_addRuleToWindow_endOfMonthFeb() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2004, 2005, FEBRUARY, -1, SUNDAY, time(1, 0), false, UTC, PERIOD_1HOUR); - b.addRuleToWindow(2004, 2005, OCTOBER, -1, SUNDAY, time(1, 0), false, UTC, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, dateTime(2003, 7, 1, 0, 0), OFFSET_1); - - assertOffsetInfo(test, dateTime(2004, 1, 1, 0, 0), OFFSET_1); - assertGap(test, 2004, 2, 29, 2, 30, OFFSET_1, OFFSET_2); // leap - assertOffsetInfo(test, dateTime(2004, 7, 1, 0, 0), OFFSET_2); - assertOverlap(test, 2004, 10, 31, 2, 30, OFFSET_2, OFFSET_1); - - assertOffsetInfo(test, dateTime(2005, 1, 1, 0, 0), OFFSET_1); - assertGap(test, 2005, 2, 27, 2, 30, OFFSET_1, OFFSET_2); - assertOffsetInfo(test, dateTime(2005, 7, 1, 0, 0), OFFSET_2); - assertOverlap(test, 2005, 10, 30, 2, 30, OFFSET_2, OFFSET_1); - - assertOffsetInfo(test, dateTime(2006, 7, 1, 0, 0), OFFSET_1); - } - - @Test - public void test_addRuleToWindow_fromDayOfMonth() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, 2001, MARCH, 10, SUNDAY, time(1, 0), false, UTC, PERIOD_1HOUR); - b.addRuleToWindow(2000, 2001, OCTOBER, 10, SUNDAY, time(1, 0), false, UTC, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, dateTime(1999, 7, 1, 0, 0), OFFSET_1); - - assertOffsetInfo(test, dateTime(2000, 1, 1, 0, 0), OFFSET_1); - assertGap(test, 2000, 3, 12, 2, 30, OFFSET_1, OFFSET_2); - assertOffsetInfo(test, dateTime(2000, 7, 1, 0, 0), OFFSET_2); - assertOverlap(test, 2000, 10, 15, 2, 30, OFFSET_2, OFFSET_1); - - assertOffsetInfo(test, dateTime(2001, 1, 1, 0, 0), OFFSET_1); - assertGap(test, 2001, 3, 11, 2, 30, OFFSET_1, OFFSET_2); - assertOffsetInfo(test, dateTime(2001, 7, 1, 0, 0), OFFSET_2); - assertOverlap(test, 2001, 10, 14, 2, 30, OFFSET_2, OFFSET_1); - - assertOffsetInfo(test, dateTime(2002, 7, 1, 0, 0), OFFSET_1); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void test_addRuleToWindow_noWindow() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void test_addRuleToWindow_cannotMixRuleWithSavings() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.setFixedSavingsToWindow(PERIOD_1HOUR30MIN); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = DateTimeException.class) - public void test_addRuleToWindow_illegalYear1() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(Year.MIN_VALUE - 1, 2008, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = DateTimeException.class) - public void test_addRuleToWindow_illegalYear2() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, Year.MIN_VALUE - 1, MARCH, -1, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void test_addRuleToWindow_illegalDayOfMonth_tooSmall() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, 2008, MARCH, -29, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void test_addRuleToWindow_illegalDayOfMonth_zero() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, 2008, MARCH, 0, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void test_addRuleToWindow_illegalDayOfMonth_tooLarge() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, 2008, MARCH, 32, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addRuleToWindow_nullMonth() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, Year.MAX_VALUE, (Month) null, 31, SUNDAY, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addRuleToWindow_nullTime() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, -1, SUNDAY, (LocalTime) null, false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void test_addRuleToWindow_illegalEndOfDayTime() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, 2008, MARCH, 1, SUNDAY, time(1, 0), true, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addRuleToWindow_nullTimeDefinition() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, Year.MAX_VALUE, MARCH, -1, SUNDAY, time(1, 0), false, (TimeDefinition) null, - PERIOD_1HOUR30MIN); - } - - //----------------------------------------------------------------------- - // addRuleToWindow() - single year object - //----------------------------------------------------------------------- - @Test - public void test_addRuleToWindow_singleYearObject() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(dateTime(2000, MARCH, 26, 1, 0), UTC, PERIOD_1HOUR); - b.addRuleToWindow(dateTime(2000, OCTOBER, 29, 1, 0), UTC, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, dateTime(1999, 7, 1, 0, 0), OFFSET_1); - - assertOffsetInfo(test, dateTime(2000, 1, 1, 0, 0), OFFSET_1); - assertGap(test, 2000, 3, 26, 2, 30, OFFSET_1, OFFSET_2); - assertOffsetInfo(test, dateTime(2000, 7, 1, 0, 0), OFFSET_2); - assertOverlap(test, 2000, 10, 29, 2, 30, OFFSET_2, OFFSET_1); - - assertOffsetInfo(test, dateTime(2001, 7, 1, 0, 0), OFFSET_1); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addRuleToWindow_singleYearObject_nullTime() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow((LocalDateTime) null, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addRuleToWindow_singleYearObject_nullTimeDefinition() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(dateTime(2000, MARCH, 31, 1, 0), (TimeDefinition) null, PERIOD_1HOUR30MIN); - } - - //----------------------------------------------------------------------- - // addRuleToWindow() - single year - //----------------------------------------------------------------------- - @Test - public void test_addRuleToWindow_singleYear() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, MARCH, 26, time(1, 0), false, UTC, PERIOD_1HOUR); - b.addRuleToWindow(2000, OCTOBER, 29, time(1, 0), false, UTC, PERIOD_0); - ZoneRules test = b.toRules("Europe/London"); - assertOffsetInfo(test, dateTime(1999, 7, 1, 0, 0), OFFSET_1); - - assertOffsetInfo(test, dateTime(2000, 1, 1, 0, 0), OFFSET_1); - assertGap(test, 2000, 3, 26, 2, 30, OFFSET_1, OFFSET_2); - assertOffsetInfo(test, dateTime(2000, 7, 1, 0, 0), OFFSET_2); - assertOverlap(test, 2000, 10, 29, 2, 30, OFFSET_2, OFFSET_1); - - assertOffsetInfo(test, dateTime(2001, 7, 1, 0, 0), OFFSET_1); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void test_addRuleToWindow_singleYear_noWindow() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addRuleToWindow(2000, MARCH, 31, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void test_addRuleToWindow_singleYear_cannotMixRuleWithSavings() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.setFixedSavingsToWindow(PERIOD_1HOUR30MIN); - b.addRuleToWindow(2000, MARCH, 31, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = DateTimeException.class) - public void test_addRuleToWindow_singleYear_illegalYear() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(Year.MIN_VALUE - 1, MARCH, 31, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void test_addRuleToWindow_singleYear_illegalDayOfMonth_tooSmall() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, MARCH, -29, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void test_addRuleToWindow_singleYear_illegalDayOfMonth_zero() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, MARCH, 0, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void test_addRuleToWindow_singleYear_illegalDayOfMonth_tooLarge() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, MARCH, 32, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addRuleToWindow_singleYear_nullMonth() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, (Month) null, 31, time(1, 0), false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addRuleToWindow_singleYear_nullTime() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, MARCH, 31, (LocalTime) null, false, WALL, PERIOD_1HOUR30MIN); - } - - @Test(expectedExceptions = NullPointerException.class) - public void test_addRuleToWindow_singleYear_nullTimeDefinition() { - ZoneRulesBuilder b = new ZoneRulesBuilder(); - b.addWindowForever(OFFSET_1); - b.addRuleToWindow(2000, MARCH, 31, time(1, 0), false, (TimeDefinition) null, PERIOD_1HOUR30MIN); - } - - //----------------------------------------------------------------------- - private static void assertGap(ZoneRules test, int y, int m, int d, int hr, int min, ZoneOffset before, - ZoneOffset after) { - LocalDateTime dt = dateTime(y, m, d, hr, min); - ZoneOffsetTransition zot = test.getTransition(dt); - assertNotNull(zot); - assertEquals(zot.isGap(), true); - assertEquals(zot.getOffsetBefore(), before); - assertEquals(zot.getOffsetAfter(), after); - } - - private static void assertOverlap(ZoneRules test, int y, int m, int d, int hr, int min, ZoneOffset before, - ZoneOffset after) { - LocalDateTime dt = dateTime(y, m, d, hr, min); - ZoneOffsetTransition zot = test.getTransition(dt); - assertNotNull(zot); - assertEquals(zot.isOverlap(), true); - assertEquals(zot.getOffsetBefore(), before); - assertEquals(zot.getOffsetAfter(), after); - } - - private void assertOffsetInfo(ZoneRules test, LocalDateTime dateTime, ZoneOffset offset) { - List offsets = test.getValidOffsets(dateTime); - assertEquals(offsets.size(), 1); - assertEquals(offsets.get(0), offset); - } - - //----------------------------------------------------------------------- - private static LocalTime time(int h, int m) { - return LocalTime.of(h, m); - } - - private static LocalDateTime dateTime(int year, int month, int day, int h, int m) { - return LocalDateTime.of(year, month, day, h, m); - } - - private static LocalDateTime dateTime(int year, Month month, int day, int h, int m) { - return LocalDateTime.of(year, month, day, h, m); - } - -}