001/*
002 * Copyright (c) 2007-2013, Stephen Colebourne & Michael Nascimento Santos
003 *
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 *  * Redistributions of source code must retain the above copyright notice,
010 *    this list of conditions and the following disclaimer.
011 *
012 *  * Redistributions in binary form must reproduce the above copyright notice,
013 *    this list of conditions and the following disclaimer in the documentation
014 *    and/or other materials provided with the distribution.
015 *
016 *  * Neither the name of JSR-310 nor the names of its contributors
017 *    may be used to endorse or promote products derived from this software
018 *    without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032package org.threeten.bp.zone;
033
034import java.io.Serializable;
035import java.util.Collections;
036import java.util.List;
037import java.util.Objects;
038
039import org.threeten.bp.Duration;
040import org.threeten.bp.Instant;
041import org.threeten.bp.LocalDateTime;
042import org.threeten.bp.ZoneId;
043import org.threeten.bp.ZoneOffset;
044
045/**
046 * The rules defining how the zone offset varies for a single time-zone.
047 * <p>
048 * The rules model all the historic and future transitions for a time-zone.
049 * {@link ZoneOffsetTransition} is used for known transitions, typically historic.
050 * {@link ZoneOffsetTransitionRule} is used for future transitions that are based
051 * on the result of an algorithm.
052 * <p>
053 * The rules are loaded via {@link ZoneRulesProvider} using a {@link ZoneId}.
054 * The same rules may be shared internally between multiple zone IDs.
055 * <p>
056 * Serializing an instance of {@code ZoneRules} will store the entire set of rules.
057 * It does not store the zone ID as it is not part of the state of this object.
058 * <p>
059 * A rule implementation may or may not store full information about historic
060 * and future transitions, and the information stored is only as accurate as
061 * that supplied to the implementation by the rules provider.
062 * Applications should treat the data provided as representing the best information
063 * available to the implementation of this rule.
064 *
065 * <h3>Specification for implementors</h3>
066 * The supplied implementations of this class are immutable and thread-safe.
067 */
068public abstract class ZoneRules {
069
070    /**
071     * Obtains an instance of {@code ZoneRules} with full transition rules.
072     *
073     * @param baseStandardOffset  the standard offset to use before legal rules were set, not null
074     * @param baseWallOffset  the wall offset to use before legal rules were set, not null
075     * @param standardOffsetTransitionList  the list of changes to the standard offset, not null
076     * @param transitionList  the list of transitions, not null
077     * @param lastRules  the recurring last rules, size 16 or less, not null
078     * @return the zone rules, not null
079     */
080    public static ZoneRules of(ZoneOffset baseStandardOffset,
081                               ZoneOffset baseWallOffset,
082                               List<ZoneOffsetTransition> standardOffsetTransitionList,
083                               List<ZoneOffsetTransition> transitionList,
084                               List<ZoneOffsetTransitionRule> lastRules) {
085        Objects.requireNonNull(baseStandardOffset, "baseStandardOffset");
086        Objects.requireNonNull(baseWallOffset, "baseWallOffset");
087        Objects.requireNonNull(standardOffsetTransitionList, "standardOffsetTransitionList");
088        Objects.requireNonNull(transitionList, "transitionList");
089        Objects.requireNonNull(lastRules, "lastRules");
090        return new StandardZoneRules(baseStandardOffset, baseWallOffset,
091                             standardOffsetTransitionList, transitionList, lastRules);
092    }
093
094    /**
095     * Obtains an instance of {@code ZoneRules} that always uses the same offset.
096     * <p>
097     * The returned rules always have the same offset.
098     *
099     * @param offset  the offset, not null
100     * @return the zone rules, not null
101     */
102    public static ZoneRules of(ZoneOffset offset) {
103        Objects.requireNonNull(offset, "offset");
104        return new Fixed(offset);
105    }
106
107    /**
108     * Restricted constructor.
109     */
110    ZoneRules() {
111    }
112
113    //-----------------------------------------------------------------------
114    /**
115     * Checks of the zone rules are fixed, such that the offset never varies.
116     *
117     * @return true if the time-zone is fixed and the offset never changes
118     */
119    public abstract boolean isFixedOffset();
120
121    //-----------------------------------------------------------------------
122    /**
123     * Gets the offset applicable at the specified instant in these rules.
124     * <p>
125     * The mapping from an instant to an offset is simple, there is only
126     * one valid offset for each instant.
127     * This method returns that offset.
128     *
129     * @param instant  the instant to find the offset for, not null, but null
130     *  may be ignored if the rules have a single offset for all instants
131     * @return the offset, not null
132     */
133    public abstract ZoneOffset getOffset(Instant instant);
134
135    /**
136     * Gets a suitable offset for the specified local date-time in these rules.
137     * <p>
138     * The mapping from a local date-time to an offset is not straightforward.
139     * There are three cases:
140     * <p><ul>
141     * <li>Normal, with one valid offset. For the vast majority of the year, the normal
142     *  case applies, where there is a single valid offset for the local date-time.</li>
143     * <li>Gap, with zero valid offsets. This is when clocks jump forward typically
144     *  due to the spring daylight savings change from "winter" to "summer".
145     *  In a gap there are local date-time values with no valid offset.</li>
146     * <li>Overlap, with two valid offsets. This is when clocks are set back typically
147     *  due to the autumn daylight savings change from "summer" to "winter".
148     *  In an overlap there are local date-time values with two valid offsets.</li>
149     * </ul><p>
150     * Thus, for any given local date-time there can be zero, one or two valid offsets.
151     * This method returns the single offset in the Normal case, and in the Gap or Overlap
152     * case it returns the offset before the transition.
153     * <p>
154     * Since, in the case of Gap and Overlap, the offset returned is a "best" value, rather
155     * than the "correct" value, it should be treated with care. Applications that care
156     * about the correct offset should use a combination of this method,
157     * {@link #getValidOffsets(LocalDateTime)} and {@link #getTransition(LocalDateTime)}.
158     *
159     * @param localDateTime  the local date-time to query, not null, but null
160     *  may be ignored if the rules have a single offset for all instants
161     * @return the best available offset for the local date-time, not null
162     */
163    public abstract ZoneOffset getOffset(LocalDateTime localDateTime);
164
165    /**
166     * Gets the offset applicable at the specified local date-time in these rules.
167     * <p>
168     * The mapping from a local date-time to an offset is not straightforward.
169     * There are three cases:
170     * <p><ul>
171     * <li>Normal, with one valid offset. For the vast majority of the year, the normal
172     *  case applies, where there is a single valid offset for the local date-time.</li>
173     * <li>Gap, with zero valid offsets. This is when clocks jump forward typically
174     *  due to the spring daylight savings change from "winter" to "summer".
175     *  In a gap there are local date-time values with no valid offset.</li>
176     * <li>Overlap, with two valid offsets. This is when clocks are set back typically
177     *  due to the autumn daylight savings change from "summer" to "winter".
178     *  In an overlap there are local date-time values with two valid offsets.</li>
179     * </ul><p>
180     * Thus, for any given local date-time there can be zero, one or two valid offsets.
181     * This method returns that list of valid offsets, which is a list of size 0, 1 or 2.
182     * In the case where there are two offsets, the earlier offset is returned at index 0
183     * and the later offset at index 1.
184     * <p>
185     * There are various ways to handle the conversion from a {@code LocalDateTime}.
186     * One technique, using this method, would be:
187     * <pre>
188     *  List<ZoneOffset> validOffsets = rules.getOffset(localDT);
189     *  if (validOffsets.size() == 1) {
190     *    // Normal case: only one valid offset
191     *    zoneOffset = validOffsets.get(0);
192     *  } else {
193     *    // Gap or Overlap: determine what to do from transition (which will be non-null)
194     *    ZoneOffsetTransition trans = rules.getTransition(localDT);
195     *  }
196     * </pre>
197     * <p>
198     * In theory, it is possible for there to be more than two valid offsets.
199     * This would happen if clocks to be put back more than once in quick succession.
200     * This has never happened in the history of time-zones and thus has no special handling.
201     * However, if it were to happen, then the list would return more than 2 entries.
202     *
203     * @param localDateTime  the local date-time to query for valid offsets, not null, but null
204     *  may be ignored if the rules have a single offset for all instants
205     * @return the list of valid offsets, may be immutable, not null
206     */
207    public abstract List<ZoneOffset> getValidOffsets(LocalDateTime localDateTime);
208
209    /**
210     * Gets the offset transition applicable at the specified local date-time in these rules.
211     * <p>
212     * The mapping from a local date-time to an offset is not straightforward.
213     * There are three cases:
214     * <p><ul>
215     * <li>Normal, with one valid offset. For the vast majority of the year, the normal
216     *  case applies, where there is a single valid offset for the local date-time.</li>
217     * <li>Gap, with zero valid offsets. This is when clocks jump forward typically
218     *  due to the spring daylight savings change from "winter" to "summer".
219     *  In a gap there are local date-time values with no valid offset.</li>
220     * <li>Overlap, with two valid offsets. This is when clocks are set back typically
221     *  due to the autumn daylight savings change from "summer" to "winter".
222     *  In an overlap there are local date-time values with two valid offsets.</li>
223     * </ul><p>
224     * A transition is used to model the cases of a Gap or Overlap.
225     * The Normal case will return null.
226     * <p>
227     * There are various ways to handle the conversion from a {@code LocalDateTime}.
228     * One technique, using this method, would be:
229     * <pre>
230     *  ZoneOffsetTransition trans = rules.getTransition(localDT);
231     *  if (trans == null) {
232     *    // Gap or Overlap: determine what to do from transition
233     *  } else {
234     *    // Normal case: only one valid offset
235     *    zoneOffset = rule.getOffset(localDT);
236     *  }
237     * </pre>
238     *
239     * @param localDateTime  the local date-time to query for offset transition, not null, but null
240     *  may be ignored if the rules have a single offset for all instants
241     * @return the offset transition, null if the local date-time is not in transition
242     */
243    public abstract ZoneOffsetTransition getTransition(LocalDateTime localDateTime);
244
245    //-----------------------------------------------------------------------
246    /**
247     * Gets the standard offset for the specified instant in this zone.
248     * <p>
249     * This provides access to historic information on how the standard offset
250     * has changed over time.
251     * The standard offset is the offset before any daylight saving time is applied.
252     * This is typically the offset applicable during winter.
253     *
254     * @param instant  the instant to find the offset information for, not null, but null
255     *  may be ignored if the rules have a single offset for all instants
256     * @return the standard offset, not null
257     */
258    public abstract ZoneOffset getStandardOffset(Instant instant);
259
260    /**
261     * Gets the amount of daylight savings in use for the specified instant in this zone.
262     * <p>
263     * This provides access to historic information on how the amount of daylight
264     * savings has changed over time.
265     * This is the difference between the standard offset and the actual offset.
266     * Typically the amount is zero during winter and one hour during summer.
267     * Time-zones are second-based, so the nanosecond part of the duration will be zero.
268     *
269     * @param instant  the instant to find the daylight savings for, not null, but null
270     *  may be ignored if the rules have a single offset for all instants
271     * @return the difference between the standard and actual offset, not null
272     */
273    public abstract Duration getDaylightSavings(Instant instant);
274    //    default {
275    //        ZoneOffset standardOffset = getStandardOffset(instant);
276    //        ZoneOffset actualOffset = getOffset(instant);
277    //        return actualOffset.toDuration().minus(standardOffset.toDuration()).normalized();
278    //    }
279
280    /**
281     * Checks if the specified instant is in daylight savings.
282     * <p>
283     * This checks if the standard and actual offsets are the same at the specified instant.
284     *
285     * @param instant  the instant to find the offset information for, not null, but null
286     *  may be ignored if the rules have a single offset for all instants
287     * @return the standard offset, not null
288     */
289    public abstract boolean isDaylightSavings(Instant instant);
290    //    default {
291    //        return (getStandardOffset(instant).equals(getOffset(instant)) == false);
292    //    }
293
294    /**
295     * Checks if the offset date-time is valid for these rules.
296     * <p>
297     * To be valid, the local date-time must not be in a gap and the offset
298     * must match the valid offsets.
299     *
300     * @param localDateTime  the date-time to check, not null, but null
301     *  may be ignored if the rules have a single offset for all instants
302     * @param offset  the offset to check, null returns false
303     * @return true if the offset date-time is valid for these rules
304     */
305    public abstract boolean isValidOffset(LocalDateTime localDateTime, ZoneOffset offset);
306    //    default {
307    //        return getValidOffsets(dateTime).contains(offset);
308    //    }
309
310    //-----------------------------------------------------------------------
311    /**
312     * Gets the next transition after the specified instant.
313     * <p>
314     * This returns details of the next transition after the specified instant.
315     * For example, if the instant represents a point where "Summer" daylight savings time
316     * applies, then the method will return the transition to the next "Winter" time.
317     *
318     * @param instant  the instant to get the next transition after, not null, but null
319     *  may be ignored if the rules have a single offset for all instants
320     * @return the next transition after the specified instant, null if this is after the last transition
321     */
322    public abstract ZoneOffsetTransition nextTransition(Instant instant);
323
324    /**
325     * Gets the previous transition before the specified instant.
326     * <p>
327     * This returns details of the previous transition after the specified instant.
328     * For example, if the instant represents a point where "summer" daylight saving time
329     * applies, then the method will return the transition from the previous "winter" time.
330     *
331     * @param instant  the instant to get the previous transition after, not null, but null
332     *  may be ignored if the rules have a single offset for all instants
333     * @return the previous transition after the specified instant, null if this is before the first transition
334     */
335    public abstract ZoneOffsetTransition previousTransition(Instant instant);
336
337    /**
338     * Gets the complete list of fully defined transitions.
339     * <p>
340     * The complete set of transitions for this rules instance is defined by this method
341     * and {@link #getTransitionRules()}. This method returns those transitions that have
342     * been fully defined. These are typically historical, but may be in the future.
343     * <p>
344     * The list will be empty for fixed offset rules and for any time-zone where there has
345     * only ever been a single offset. The list will also be empty if the transition rules are unknown.
346     *
347     * @return an immutable list of fully defined transitions, not null
348     */
349    public abstract List<ZoneOffsetTransition> getTransitions();
350
351    /**
352     * Gets the list of transition rules for years beyond those defined in the transition list.
353     * <p>
354     * The complete set of transitions for this rules instance is defined by this method
355     * and {@link #getTransitions()}. This method returns instances of {@link ZoneOffsetTransitionRule}
356     * that define an algorithm for when transitions will occur.
357     * <p>
358     * For any given {@code ZoneRules}, this list contains the transition rules for years
359     * beyond those years that have been fully defined. These rules typically refer to future
360     * daylight saving time rule changes.
361     * <p>
362     * If the zone defines daylight savings into the future, then the list will normally
363     * be of size two and hold information about entering and exiting daylight savings.
364     * If the zone does not have daylight savings, or information about future changes
365     * is uncertain, then the list will be empty.
366     * <p>
367     * The list will be empty for fixed offset rules and for any time-zone where there is no
368     * daylight saving time. The list will also be empty if the transition rules are unknown.
369     *
370     * @return an immutable list of transition rules, not null
371     */
372    public abstract List<ZoneOffsetTransitionRule> getTransitionRules();
373
374    //-----------------------------------------------------------------------
375    /**
376     * Checks if this set of rules equals another.
377     * <p>
378     * Two rule sets are equal if they will always result in the same output
379     * for any given input instant or local date-time.
380     * Rules from two different groups may return false even if they are in fact the same.
381     * <p>
382     * This definition should result in implementations comparing their entire state.
383     *
384     * @param otherRules  the other rules, null returns false
385     * @return true if this rules is the same as that specified
386     */
387    @Override
388    public abstract boolean equals(Object otherRules);
389
390    /**
391     * Returns a suitable hash code given the definition of {@code #equals}.
392     *
393     * @return the hash code
394     */
395    @Override
396    public abstract int hashCode();
397
398    //-----------------------------------------------------------------------
399    /**
400     * Fixed time-zone.
401     */
402    static final class Fixed extends ZoneRules implements Serializable {
403        /** A serialization identifier for this class. */
404        private static final long serialVersionUID = -8733721350312276297L;
405        /** The offset. */
406        private final ZoneOffset offset;
407
408        /**
409         * Constructor.
410         *
411         * @param offset  the offset, not null
412         */
413        Fixed(ZoneOffset offset) {
414            this.offset = offset;
415        }
416
417        //-------------------------------------------------------------------------
418        @Override
419        public boolean isFixedOffset() {
420            return true;
421        }
422
423        @Override
424        public ZoneOffset getOffset(Instant instant) {
425            return offset;
426        }
427
428        @Override
429        public ZoneOffset getOffset(LocalDateTime localDateTime) {
430            return offset;
431        }
432
433        @Override
434        public List<ZoneOffset> getValidOffsets(LocalDateTime localDateTime) {
435            return Collections.singletonList(offset);
436        }
437
438        @Override
439        public ZoneOffsetTransition getTransition(LocalDateTime localDateTime) {
440            return null;
441        }
442
443        @Override
444        public boolean isValidOffset(LocalDateTime dateTime, ZoneOffset offset) {
445            return this.offset.equals(offset);
446        }
447
448        //-------------------------------------------------------------------------
449        @Override
450        public ZoneOffset getStandardOffset(Instant instant) {
451            return offset;
452        }
453
454        @Override
455        public Duration getDaylightSavings(Instant instant) {
456            return Duration.ZERO;
457        }
458
459        @Override
460        public boolean isDaylightSavings(Instant instant) {
461            return false;
462        }
463
464        //-------------------------------------------------------------------------
465        @Override
466        public ZoneOffsetTransition nextTransition(Instant instant) {
467            return null;
468        }
469
470        @Override
471        public ZoneOffsetTransition previousTransition(Instant instant) {
472            return null;
473        }
474
475        @Override
476        public List<ZoneOffsetTransition> getTransitions() {
477            return Collections.emptyList();
478        }
479
480        @Override
481        public List<ZoneOffsetTransitionRule> getTransitionRules() {
482            return Collections.emptyList();
483        }
484
485        //-----------------------------------------------------------------------
486        @Override
487        public boolean equals(Object obj) {
488            if (this == obj) {
489               return true;
490            }
491            if (obj instanceof Fixed) {
492                return offset.equals(((Fixed) obj).offset);
493            }
494            return false;
495        }
496
497        @Override
498        public int hashCode() {
499            return offset.hashCode() + 1;
500        }
501    }
502
503}