87 * An example would be when the offset changes from {@code +03:00} to {@code +04:00}.
88 * This might be described as 'the clocks will move forward one hour tonight at 1am'.
89 * <p>
90 * Overlaps occur where there are local date-times that exist twice.
91 * An example would be when the offset changes from {@code +04:00} to {@code +03:00}.
92 * This might be described as 'the clocks will move back one hour tonight at 2am'.
93 *
94 * @implSpec
95 * This class is immutable and thread-safe.
96 *
97 * @since 1.8
98 */
99 public final class ZoneOffsetTransition
100 implements Comparable<ZoneOffsetTransition>, Serializable {
101
102 /**
103 * Serialization version.
104 */
105 private static final long serialVersionUID = -6946044323557704546L;
106 /**
107 * The local transition date-time at the transition.
108 */
109 private final LocalDateTime transition;
110 /**
111 * The offset before transition.
112 */
113 private final ZoneOffset offsetBefore;
114 /**
115 * The offset after transition.
116 */
117 private final ZoneOffset offsetAfter;
118
119 //-----------------------------------------------------------------------
120 /**
121 * Obtains an instance defining a transition between two offsets.
122 * <p>
123 * Applications should normally obtain an instance from {@link ZoneRules}.
124 * This factory is only intended for use when creating {@link ZoneRules}.
125 *
126 * @param transition the transition date-time at the transition, which never
135 Objects.requireNonNull(transition, "transition");
136 Objects.requireNonNull(offsetBefore, "offsetBefore");
137 Objects.requireNonNull(offsetAfter, "offsetAfter");
138 if (offsetBefore.equals(offsetAfter)) {
139 throw new IllegalArgumentException("Offsets must not be equal");
140 }
141 if (transition.getNano() != 0) {
142 throw new IllegalArgumentException("Nano-of-second must be zero");
143 }
144 return new ZoneOffsetTransition(transition, offsetBefore, offsetAfter);
145 }
146
147 /**
148 * Creates an instance defining a transition between two offsets.
149 *
150 * @param transition the transition date-time with the offset before the transition, not null
151 * @param offsetBefore the offset before the transition, not null
152 * @param offsetAfter the offset at and after the transition, not null
153 */
154 ZoneOffsetTransition(LocalDateTime transition, ZoneOffset offsetBefore, ZoneOffset offsetAfter) {
155 this.transition = transition;
156 this.offsetBefore = offsetBefore;
157 this.offsetAfter = offsetAfter;
158 }
159
160 /**
161 * Creates an instance from epoch-second and offsets.
162 *
163 * @param epochSecond the transition epoch-second
164 * @param offsetBefore the offset before the transition, not null
165 * @param offsetAfter the offset at and after the transition, not null
166 */
167 ZoneOffsetTransition(long epochSecond, ZoneOffset offsetBefore, ZoneOffset offsetAfter) {
168 this.transition = LocalDateTime.ofEpochSecond(epochSecond, 0, offsetBefore);
169 this.offsetBefore = offsetBefore;
170 this.offsetAfter = offsetAfter;
171 }
172
173 //-----------------------------------------------------------------------
174 /**
175 * Defend against malicious streams.
176 *
177 * @param s the stream to read
178 * @throws InvalidObjectException always
179 */
180 private void readObject(ObjectInputStream s) throws InvalidObjectException {
181 throw new InvalidObjectException("Deserialization via serialization delegate");
182 }
183
184 /**
185 * Writes the object using a
186 * <a href="../../../serialized-form.html#java.time.zone.Ser">dedicated serialized form</a>.
187 * @serialData
192 *
193 * out.writeByte(2); // identifies a ZoneOffsetTransition
194 * out.writeEpochSec(toEpochSecond);
195 * out.writeOffset(offsetBefore);
196 * out.writeOffset(offsetAfter);
197 * }
198 * </pre>
199 * @return the replacing object, not null
200 */
201 private Object writeReplace() {
202 return new Ser(Ser.ZOT, this);
203 }
204
205 /**
206 * Writes the state to the stream.
207 *
208 * @param out the output stream, not null
209 * @throws IOException if an error occurs
210 */
211 void writeExternal(DataOutput out) throws IOException {
212 Ser.writeEpochSec(toEpochSecond(), out);
213 Ser.writeOffset(offsetBefore, out);
214 Ser.writeOffset(offsetAfter, out);
215 }
216
217 /**
218 * Reads the state from the stream.
219 *
220 * @param in the input stream, not null
221 * @return the created object, not null
222 * @throws IOException if an error occurs
223 */
224 static ZoneOffsetTransition readExternal(DataInput in) throws IOException {
225 long epochSecond = Ser.readEpochSec(in);
226 ZoneOffset before = Ser.readOffset(in);
227 ZoneOffset after = Ser.readOffset(in);
228 if (before.equals(after)) {
229 throw new IllegalArgumentException("Offsets must not be equal");
230 }
231 return new ZoneOffsetTransition(epochSecond, before, after);
232 }
236 * Gets the transition instant.
237 * <p>
238 * This is the instant of the discontinuity, which is defined as the first
239 * instant that the 'after' offset applies.
240 * <p>
241 * The methods {@link #getInstant()}, {@link #getDateTimeBefore()} and {@link #getDateTimeAfter()}
242 * all represent the same instant.
243 *
244 * @return the transition instant, not null
245 */
246 public Instant getInstant() {
247 return transition.toInstant(offsetBefore);
248 }
249
250 /**
251 * Gets the transition instant as an epoch second.
252 *
253 * @return the transition epoch second
254 */
255 public long toEpochSecond() {
256 return transition.toEpochSecond(offsetBefore);
257 }
258
259 //-------------------------------------------------------------------------
260 /**
261 * Gets the local transition date-time, as would be expressed with the 'before' offset.
262 * <p>
263 * This is the date-time where the discontinuity begins expressed with the 'before' offset.
264 * At this instant, the 'after' offset is actually used, therefore the combination of this
265 * date-time and the 'before' offset will never occur.
266 * <p>
267 * The combination of the 'before' date-time and offset represents the same instant
268 * as the 'after' date-time and offset.
269 *
270 * @return the transition date-time expressed with the before offset, not null
271 */
272 public LocalDateTime getDateTimeBefore() {
273 return transition;
274 }
275
276 /**
|
87 * An example would be when the offset changes from {@code +03:00} to {@code +04:00}.
88 * This might be described as 'the clocks will move forward one hour tonight at 1am'.
89 * <p>
90 * Overlaps occur where there are local date-times that exist twice.
91 * An example would be when the offset changes from {@code +04:00} to {@code +03:00}.
92 * This might be described as 'the clocks will move back one hour tonight at 2am'.
93 *
94 * @implSpec
95 * This class is immutable and thread-safe.
96 *
97 * @since 1.8
98 */
99 public final class ZoneOffsetTransition
100 implements Comparable<ZoneOffsetTransition>, Serializable {
101
102 /**
103 * Serialization version.
104 */
105 private static final long serialVersionUID = -6946044323557704546L;
106 /**
107 * The transition epoch-second.
108 */
109 private final long epochSecond;
110 /**
111 * The local transition date-time at the transition.
112 */
113 private final LocalDateTime transition;
114 /**
115 * The offset before transition.
116 */
117 private final ZoneOffset offsetBefore;
118 /**
119 * The offset after transition.
120 */
121 private final ZoneOffset offsetAfter;
122
123 //-----------------------------------------------------------------------
124 /**
125 * Obtains an instance defining a transition between two offsets.
126 * <p>
127 * Applications should normally obtain an instance from {@link ZoneRules}.
128 * This factory is only intended for use when creating {@link ZoneRules}.
129 *
130 * @param transition the transition date-time at the transition, which never
139 Objects.requireNonNull(transition, "transition");
140 Objects.requireNonNull(offsetBefore, "offsetBefore");
141 Objects.requireNonNull(offsetAfter, "offsetAfter");
142 if (offsetBefore.equals(offsetAfter)) {
143 throw new IllegalArgumentException("Offsets must not be equal");
144 }
145 if (transition.getNano() != 0) {
146 throw new IllegalArgumentException("Nano-of-second must be zero");
147 }
148 return new ZoneOffsetTransition(transition, offsetBefore, offsetAfter);
149 }
150
151 /**
152 * Creates an instance defining a transition between two offsets.
153 *
154 * @param transition the transition date-time with the offset before the transition, not null
155 * @param offsetBefore the offset before the transition, not null
156 * @param offsetAfter the offset at and after the transition, not null
157 */
158 ZoneOffsetTransition(LocalDateTime transition, ZoneOffset offsetBefore, ZoneOffset offsetAfter) {
159 this.epochSecond = transition.toEpochSecond(offsetBefore);
160 this.transition = transition;
161 this.offsetBefore = offsetBefore;
162 this.offsetAfter = offsetAfter;
163 }
164
165 /**
166 * Creates an instance from epoch-second and offsets.
167 *
168 * @param epochSecond the transition epoch-second
169 * @param offsetBefore the offset before the transition, not null
170 * @param offsetAfter the offset at and after the transition, not null
171 */
172 ZoneOffsetTransition(long epochSecond, ZoneOffset offsetBefore, ZoneOffset offsetAfter) {
173 this.epochSecond = epochSecond;
174 this.transition = LocalDateTime.ofEpochSecond(epochSecond, 0, offsetBefore);
175 this.offsetBefore = offsetBefore;
176 this.offsetAfter = offsetAfter;
177 }
178
179 //-----------------------------------------------------------------------
180 /**
181 * Defend against malicious streams.
182 *
183 * @param s the stream to read
184 * @throws InvalidObjectException always
185 */
186 private void readObject(ObjectInputStream s) throws InvalidObjectException {
187 throw new InvalidObjectException("Deserialization via serialization delegate");
188 }
189
190 /**
191 * Writes the object using a
192 * <a href="../../../serialized-form.html#java.time.zone.Ser">dedicated serialized form</a>.
193 * @serialData
198 *
199 * out.writeByte(2); // identifies a ZoneOffsetTransition
200 * out.writeEpochSec(toEpochSecond);
201 * out.writeOffset(offsetBefore);
202 * out.writeOffset(offsetAfter);
203 * }
204 * </pre>
205 * @return the replacing object, not null
206 */
207 private Object writeReplace() {
208 return new Ser(Ser.ZOT, this);
209 }
210
211 /**
212 * Writes the state to the stream.
213 *
214 * @param out the output stream, not null
215 * @throws IOException if an error occurs
216 */
217 void writeExternal(DataOutput out) throws IOException {
218 Ser.writeEpochSec(epochSecond, out);
219 Ser.writeOffset(offsetBefore, out);
220 Ser.writeOffset(offsetAfter, out);
221 }
222
223 /**
224 * Reads the state from the stream.
225 *
226 * @param in the input stream, not null
227 * @return the created object, not null
228 * @throws IOException if an error occurs
229 */
230 static ZoneOffsetTransition readExternal(DataInput in) throws IOException {
231 long epochSecond = Ser.readEpochSec(in);
232 ZoneOffset before = Ser.readOffset(in);
233 ZoneOffset after = Ser.readOffset(in);
234 if (before.equals(after)) {
235 throw new IllegalArgumentException("Offsets must not be equal");
236 }
237 return new ZoneOffsetTransition(epochSecond, before, after);
238 }
242 * Gets the transition instant.
243 * <p>
244 * This is the instant of the discontinuity, which is defined as the first
245 * instant that the 'after' offset applies.
246 * <p>
247 * The methods {@link #getInstant()}, {@link #getDateTimeBefore()} and {@link #getDateTimeAfter()}
248 * all represent the same instant.
249 *
250 * @return the transition instant, not null
251 */
252 public Instant getInstant() {
253 return transition.toInstant(offsetBefore);
254 }
255
256 /**
257 * Gets the transition instant as an epoch second.
258 *
259 * @return the transition epoch second
260 */
261 public long toEpochSecond() {
262 return epochSecond;
263 }
264
265 //-------------------------------------------------------------------------
266 /**
267 * Gets the local transition date-time, as would be expressed with the 'before' offset.
268 * <p>
269 * This is the date-time where the discontinuity begins expressed with the 'before' offset.
270 * At this instant, the 'after' offset is actually used, therefore the combination of this
271 * date-time and the 'before' offset will never occur.
272 * <p>
273 * The combination of the 'before' date-time and offset represents the same instant
274 * as the 'after' date-time and offset.
275 *
276 * @return the transition date-time expressed with the before offset, not null
277 */
278 public LocalDateTime getDateTimeBefore() {
279 return transition;
280 }
281
282 /**
|