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.jdk8;
033
034import static org.threeten.bp.temporal.ChronoField.INSTANT_SECONDS;
035import static org.threeten.bp.temporal.ChronoField.OFFSET_SECONDS;
036import static org.threeten.bp.temporal.ChronoUnit.NANOS;
037
038import java.util.Objects;
039
040import org.threeten.bp.DateTimeException;
041import org.threeten.bp.Instant;
042import org.threeten.bp.LocalTime;
043import org.threeten.bp.format.DateTimeFormatter;
044import org.threeten.bp.temporal.Chrono;
045import org.threeten.bp.temporal.ChronoField;
046import org.threeten.bp.temporal.ChronoLocalDate;
047import org.threeten.bp.temporal.ChronoZonedDateTime;
048import org.threeten.bp.temporal.TemporalAdder;
049import org.threeten.bp.temporal.TemporalAdjuster;
050import org.threeten.bp.temporal.TemporalField;
051import org.threeten.bp.temporal.TemporalQueries;
052import org.threeten.bp.temporal.TemporalQuery;
053import org.threeten.bp.temporal.TemporalSubtractor;
054import org.threeten.bp.temporal.TemporalUnit;
055import org.threeten.bp.temporal.ValueRange;
056
057/**
058 * A temporary class providing implementations that will become default interface
059 * methods once integrated into JDK 8.
060 *
061 * @param <C> the chronology of this date-time
062 */
063public abstract class DefaultInterfaceChronoZonedDateTime<C extends Chrono<C>>
064        extends DefaultInterfaceTemporal
065        implements ChronoZonedDateTime<C> {
066
067    @Override
068    public ValueRange range(TemporalField field) {
069        if (field instanceof ChronoField) {
070            if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) {
071                return field.range();
072            }
073            return getDateTime().range(field);
074        }
075        return field.doRange(this);
076    }
077
078    @Override
079    public int get(TemporalField field) {
080        if (field instanceof ChronoField) {
081            switch ((ChronoField) field) {
082                case INSTANT_SECONDS: throw new DateTimeException("Field too large for an int: " + field);
083                case OFFSET_SECONDS: return getOffset().getTotalSeconds();
084            }
085            return getDateTime().get(field);
086        }
087        return super.get(field);
088    }
089
090    @Override
091    public long getLong(TemporalField field) {
092        if (field instanceof ChronoField) {
093            switch ((ChronoField) field) {
094                case INSTANT_SECONDS: return toEpochSecond();
095                case OFFSET_SECONDS: return getOffset().getTotalSeconds();
096            }
097            return getDateTime().getLong(field);
098        }
099        return field.doGet(this);
100    }
101
102    //-----------------------------------------------------------------------
103    @Override
104    public ChronoLocalDate<C> getDate() {
105        return getDateTime().getDate();
106    }
107
108    @Override
109    public LocalTime getTime() {
110        return getDateTime().getTime();
111    }
112
113    //-----------------------------------------------------------------------
114    @Override
115    public ChronoZonedDateTime<C> with(TemporalAdjuster adjuster) {
116        return getDate().getChrono().ensureChronoZonedDateTime(super.with(adjuster));
117    }
118
119    @Override
120    public ChronoZonedDateTime<C> plus(TemporalAdder adjuster) {
121        return getDate().getChrono().ensureChronoZonedDateTime(super.plus(adjuster));
122    }
123
124    @Override
125    public ChronoZonedDateTime<C> minus(TemporalSubtractor adjuster) {
126        return getDate().getChrono().ensureChronoZonedDateTime(super.minus(adjuster));
127    }
128
129    @Override
130    public ChronoZonedDateTime<C> minus(long amountToSubtract, TemporalUnit unit) {
131        return getDate().getChrono().ensureChronoZonedDateTime(super.minus(amountToSubtract, unit));
132    }
133
134    //-------------------------------------------------------------------------
135    @SuppressWarnings("unchecked")
136    @Override
137    public <R> R query(TemporalQuery<R> query) {
138        if (query == TemporalQueries.zoneId()) {
139            return (R) getZone();
140        } else if (query == TemporalQueries.chrono()) {
141            return (R) getDate().getChrono();
142        } else if (query == TemporalQueries.precision()) {
143            return (R) NANOS;
144        } else if (query == TemporalQueries.offset()) {
145            return (R) getOffset();
146        }
147        return super.query(query);
148    }
149
150    //-------------------------------------------------------------------------
151    @Override
152    public Instant toInstant() {
153        return Instant.ofEpochSecond(toEpochSecond(), getTime().getNano());
154    }
155
156    @Override
157    public long toEpochSecond() {
158        long epochDay = getDate().toEpochDay();
159        long secs = epochDay * 86400 + getTime().toSecondOfDay();
160        secs -= getOffset().getTotalSeconds();
161        return secs;
162    }
163
164    //-------------------------------------------------------------------------
165    @Override
166    public int compareTo(ChronoZonedDateTime<?> other) {
167        int cmp = Long.compare(toEpochSecond(), other.toEpochSecond());
168        if (cmp == 0) {
169            cmp = getTime().getNano() - other.getTime().getNano();
170            if (cmp == 0) {
171                cmp = getDateTime().compareTo(other.getDateTime());
172                if (cmp == 0) {
173                    cmp = getZone().getId().compareTo(other.getZone().getId());
174                    if (cmp == 0) {
175                        cmp = getDate().getChrono().compareTo(other.getDate().getChrono());
176                    }
177                }
178            }
179        }
180        return cmp;
181    }
182
183    @Override
184    public boolean isAfter(ChronoZonedDateTime<?> other) {
185        long thisEpochSec = toEpochSecond();
186        long otherEpochSec = other.toEpochSecond();
187        return thisEpochSec > otherEpochSec ||
188            (thisEpochSec == otherEpochSec && getTime().getNano() > other.getTime().getNano());
189    }
190
191    @Override
192    public boolean isBefore(ChronoZonedDateTime<?> other) {
193        long thisEpochSec = toEpochSecond();
194        long otherEpochSec = other.toEpochSecond();
195        return thisEpochSec < otherEpochSec ||
196            (thisEpochSec == otherEpochSec && getTime().getNano() < other.getTime().getNano());
197    }
198
199    @Override
200    public boolean isEqual(ChronoZonedDateTime<?> other) {
201        return toEpochSecond() == other.toEpochSecond() &&
202                getTime().getNano() == other.getTime().getNano();
203    }
204
205    //-------------------------------------------------------------------------
206    @Override
207    public boolean equals(Object obj) {
208        if (this == obj) {
209            return true;
210        }
211        if (obj instanceof ChronoZonedDateTime) {
212            return compareTo((ChronoZonedDateTime<?>) obj) == 0;
213        }
214        return false;
215    }
216
217    @Override
218    public int hashCode() {
219        return getDateTime().hashCode() ^ getOffset().hashCode() ^ Integer.rotateLeft(getZone().hashCode(), 3);
220    }
221
222    //-------------------------------------------------------------------------
223    @Override
224    public String toString() {
225        String str = getDateTime().toString() + getOffset().toString();
226        if (getOffset() != getZone()) {
227            str += '[' + getZone().toString() + ']';
228        }
229        return str;
230    }
231
232    @Override
233    public String toString(DateTimeFormatter formatter) {
234        Objects.requireNonNull(formatter, "formatter");
235        return formatter.print(this);
236    }
237
238}