1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.joda.time.chrono;
17
18 import java.util.HashMap;
19 import java.util.Locale;
20
21 import org.joda.time.Chronology;
22 import org.joda.time.DateTimeConstants;
23 import org.joda.time.DateTimeField;
24 import org.joda.time.DateTimeZone;
25 import org.joda.time.DurationField;
26 import org.joda.time.IllegalFieldValueException;
27 import org.joda.time.IllegalInstantException;
28 import org.joda.time.ReadablePartial;
29 import org.joda.time.field.BaseDateTimeField;
30 import org.joda.time.field.BaseDurationField;
31
32
33
34
35
36
37
38
39
40
41 public final class ZonedChronology extends AssembledChronology {
42
43
44 private static final long serialVersionUID = -1079258847191166848L;
45
46
47
48
49
50
51
52
53
54 public static ZonedChronology getInstance(Chronology base, DateTimeZone zone) {
55 if (base == null) {
56 throw new IllegalArgumentException("Must supply a chronology");
57 }
58 base = base.withUTC();
59 if (base == null) {
60 throw new IllegalArgumentException("UTC chronology must not be null");
61 }
62 if (zone == null) {
63 throw new IllegalArgumentException("DateTimeZone must not be null");
64 }
65 return new ZonedChronology(base, zone);
66 }
67
68 static boolean useTimeArithmetic(DurationField field) {
69
70
71 return field != null && field.getUnitMillis() < DateTimeConstants.MILLIS_PER_HOUR * 12;
72 }
73
74
75
76
77
78
79
80 private ZonedChronology(Chronology base, DateTimeZone zone) {
81 super(base, zone);
82 }
83
84 public DateTimeZone getZone() {
85 return (DateTimeZone)getParam();
86 }
87
88 public Chronology withUTC() {
89 return getBase();
90 }
91
92 public Chronology withZone(DateTimeZone zone) {
93 if (zone == null) {
94 zone = DateTimeZone.getDefault();
95 }
96 if (zone == getParam()) {
97 return this;
98 }
99 if (zone == DateTimeZone.UTC) {
100 return getBase();
101 }
102 return new ZonedChronology(getBase(), zone);
103 }
104
105 public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
106 int millisOfDay)
107 throws IllegalArgumentException
108 {
109 return localToUTC(getBase().getDateTimeMillis
110 (year, monthOfYear, dayOfMonth, millisOfDay));
111 }
112
113 public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth,
114 int hourOfDay, int minuteOfHour,
115 int secondOfMinute, int millisOfSecond)
116 throws IllegalArgumentException
117 {
118 return localToUTC(getBase().getDateTimeMillis
119 (year, monthOfYear, dayOfMonth,
120 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond));
121 }
122
123 public long getDateTimeMillis(long instant,
124 int hourOfDay, int minuteOfHour,
125 int secondOfMinute, int millisOfSecond)
126 throws IllegalArgumentException
127 {
128 return localToUTC(getBase().getDateTimeMillis
129 (instant + getZone().getOffset(instant),
130 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond));
131 }
132
133
134
135
136
137 private long localToUTC(long localInstant) {
138 DateTimeZone zone = getZone();
139 int offset = zone.getOffsetFromLocal(localInstant);
140 localInstant -= offset;
141 if (offset != zone.getOffset(localInstant)) {
142 throw new IllegalInstantException(localInstant, zone.getID());
143 }
144 return localInstant;
145 }
146
147 protected void assemble(Fields fields) {
148
149
150 HashMap<Object, Object> converted = new HashMap<Object, Object>();
151
152
153
154 fields.eras = convertField(fields.eras, converted);
155 fields.centuries = convertField(fields.centuries, converted);
156 fields.years = convertField(fields.years, converted);
157 fields.months = convertField(fields.months, converted);
158 fields.weekyears = convertField(fields.weekyears, converted);
159 fields.weeks = convertField(fields.weeks, converted);
160 fields.days = convertField(fields.days, converted);
161
162 fields.halfdays = convertField(fields.halfdays, converted);
163 fields.hours = convertField(fields.hours, converted);
164 fields.minutes = convertField(fields.minutes, converted);
165 fields.seconds = convertField(fields.seconds, converted);
166 fields.millis = convertField(fields.millis, converted);
167
168
169
170 fields.year = convertField(fields.year, converted);
171 fields.yearOfEra = convertField(fields.yearOfEra, converted);
172 fields.yearOfCentury = convertField(fields.yearOfCentury, converted);
173 fields.centuryOfEra = convertField(fields.centuryOfEra, converted);
174 fields.era = convertField(fields.era, converted);
175 fields.dayOfWeek = convertField(fields.dayOfWeek, converted);
176 fields.dayOfMonth = convertField(fields.dayOfMonth, converted);
177 fields.dayOfYear = convertField(fields.dayOfYear, converted);
178 fields.monthOfYear = convertField(fields.monthOfYear, converted);
179 fields.weekOfWeekyear = convertField(fields.weekOfWeekyear, converted);
180 fields.weekyear = convertField(fields.weekyear, converted);
181 fields.weekyearOfCentury = convertField(fields.weekyearOfCentury, converted);
182
183 fields.millisOfSecond = convertField(fields.millisOfSecond, converted);
184 fields.millisOfDay = convertField(fields.millisOfDay, converted);
185 fields.secondOfMinute = convertField(fields.secondOfMinute, converted);
186 fields.secondOfDay = convertField(fields.secondOfDay, converted);
187 fields.minuteOfHour = convertField(fields.minuteOfHour, converted);
188 fields.minuteOfDay = convertField(fields.minuteOfDay, converted);
189 fields.hourOfDay = convertField(fields.hourOfDay, converted);
190 fields.hourOfHalfday = convertField(fields.hourOfHalfday, converted);
191 fields.clockhourOfDay = convertField(fields.clockhourOfDay, converted);
192 fields.clockhourOfHalfday = convertField(fields.clockhourOfHalfday, converted);
193 fields.halfdayOfDay = convertField(fields.halfdayOfDay, converted);
194 }
195
196 private DurationField convertField(DurationField field, HashMap<Object, Object> converted) {
197 if (field == null || !field.isSupported()) {
198 return field;
199 }
200 if (converted.containsKey(field)) {
201 return (DurationField)converted.get(field);
202 }
203 ZonedDurationField zonedField = new ZonedDurationField(field, getZone());
204 converted.put(field, zonedField);
205 return zonedField;
206 }
207
208 private DateTimeField convertField(DateTimeField field, HashMap<Object, Object> converted) {
209 if (field == null || !field.isSupported()) {
210 return field;
211 }
212 if (converted.containsKey(field)) {
213 return (DateTimeField)converted.get(field);
214 }
215 ZonedDateTimeField zonedField =
216 new ZonedDateTimeField(field, getZone(),
217 convertField(field.getDurationField(), converted),
218 convertField(field.getRangeDurationField(), converted),
219 convertField(field.getLeapDurationField(), converted));
220 converted.put(field, zonedField);
221 return zonedField;
222 }
223
224
225
226
227
228
229
230
231
232
233 public boolean equals(Object obj) {
234 if (this == obj) {
235 return true;
236 }
237 if (obj instanceof ZonedChronology == false) {
238 return false;
239 }
240 ZonedChronology chrono = (ZonedChronology) obj;
241 return
242 getBase().equals(chrono.getBase()) &&
243 getZone().equals(chrono.getZone());
244 }
245
246
247
248
249
250
251
252 public int hashCode() {
253 return 326565 + getZone().hashCode() * 11 + getBase().hashCode() * 7;
254 }
255
256
257
258
259
260
261 public String toString() {
262 return "ZonedChronology[" + getBase() + ", " + getZone().getID() + ']';
263 }
264
265
266
267
268
269
270
271
272
273 static class ZonedDurationField extends BaseDurationField {
274 private static final long serialVersionUID = -485345310999208286L;
275
276 final DurationField iField;
277 final boolean iTimeField;
278 final DateTimeZone iZone;
279
280 ZonedDurationField(DurationField field, DateTimeZone zone) {
281 super(field.getType());
282 if (!field.isSupported()) {
283 throw new IllegalArgumentException();
284 }
285 iField = field;
286 iTimeField = useTimeArithmetic(field);
287 iZone = zone;
288 }
289
290 public boolean isPrecise() {
291 return iTimeField ? iField.isPrecise() : iField.isPrecise() && this.iZone.isFixed();
292 }
293
294 public long getUnitMillis() {
295 return iField.getUnitMillis();
296 }
297
298 public int getValue(long duration, long instant) {
299 return iField.getValue(duration, addOffset(instant));
300 }
301
302 public long getValueAsLong(long duration, long instant) {
303 return iField.getValueAsLong(duration, addOffset(instant));
304 }
305
306 public long getMillis(int value, long instant) {
307 return iField.getMillis(value, addOffset(instant));
308 }
309
310 public long getMillis(long value, long instant) {
311 return iField.getMillis(value, addOffset(instant));
312 }
313
314 public long add(long instant, int value) {
315 int offset = getOffsetToAdd(instant);
316 instant = iField.add(instant + offset, value);
317 return instant - (iTimeField ? offset : getOffsetFromLocalToSubtract(instant));
318 }
319
320 public long add(long instant, long value) {
321 int offset = getOffsetToAdd(instant);
322 instant = iField.add(instant + offset, value);
323 return instant - (iTimeField ? offset : getOffsetFromLocalToSubtract(instant));
324 }
325
326 public int getDifference(long minuendInstant, long subtrahendInstant) {
327 int offset = getOffsetToAdd(subtrahendInstant);
328 return iField.getDifference
329 (minuendInstant + (iTimeField ? offset : getOffsetToAdd(minuendInstant)),
330 subtrahendInstant + offset);
331 }
332
333 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
334 int offset = getOffsetToAdd(subtrahendInstant);
335 return iField.getDifferenceAsLong
336 (minuendInstant + (iTimeField ? offset : getOffsetToAdd(minuendInstant)),
337 subtrahendInstant + offset);
338 }
339
340 private int getOffsetToAdd(long instant) {
341 int offset = this.iZone.getOffset(instant);
342 long sum = instant + offset;
343
344 if ((instant ^ sum) < 0 && (instant ^ offset) >= 0) {
345 throw new ArithmeticException("Adding time zone offset caused overflow");
346 }
347 return offset;
348 }
349
350 private int getOffsetFromLocalToSubtract(long instant) {
351 int offset = this.iZone.getOffsetFromLocal(instant);
352 long diff = instant - offset;
353
354 if ((instant ^ diff) < 0 && (instant ^ offset) < 0) {
355 throw new ArithmeticException("Subtracting time zone offset caused overflow");
356 }
357 return offset;
358 }
359
360 private long addOffset(long instant) {
361 return iZone.convertUTCToLocal(instant);
362 }
363 }
364
365
366
367
368
369
370
371 static final class ZonedDateTimeField extends BaseDateTimeField {
372 private static final long serialVersionUID = -3968986277775529794L;
373
374 final DateTimeField iField;
375 final DateTimeZone iZone;
376 final DurationField iDurationField;
377 final boolean iTimeField;
378 final DurationField iRangeDurationField;
379 final DurationField iLeapDurationField;
380
381 ZonedDateTimeField(DateTimeField field,
382 DateTimeZone zone,
383 DurationField durationField,
384 DurationField rangeDurationField,
385 DurationField leapDurationField) {
386 super(field.getType());
387 if (!field.isSupported()) {
388 throw new IllegalArgumentException();
389 }
390 iField = field;
391 iZone = zone;
392 iDurationField = durationField;
393 iTimeField = useTimeArithmetic(durationField);
394 iRangeDurationField = rangeDurationField;
395 iLeapDurationField = leapDurationField;
396 }
397
398 public boolean isLenient() {
399 return iField.isLenient();
400 }
401
402 public int get(long instant) {
403 long localInstant = iZone.convertUTCToLocal(instant);
404 return iField.get(localInstant);
405 }
406
407 public String getAsText(long instant, Locale locale) {
408 long localInstant = iZone.convertUTCToLocal(instant);
409 return iField.getAsText(localInstant, locale);
410 }
411
412 public String getAsShortText(long instant, Locale locale) {
413 long localInstant = iZone.convertUTCToLocal(instant);
414 return iField.getAsShortText(localInstant, locale);
415 }
416
417 public String getAsText(int fieldValue, Locale locale) {
418 return iField.getAsText(fieldValue, locale);
419 }
420
421 public String getAsShortText(int fieldValue, Locale locale) {
422 return iField.getAsShortText(fieldValue, locale);
423 }
424
425 public long add(long instant, int value) {
426 if (iTimeField) {
427 int offset = getOffsetToAdd(instant);
428 long localInstant = iField.add(instant + offset, value);
429 return localInstant - offset;
430 } else {
431 long localInstant = iZone.convertUTCToLocal(instant);
432 localInstant = iField.add(localInstant, value);
433 return iZone.convertLocalToUTC(localInstant, false, instant);
434 }
435 }
436
437 public long add(long instant, long value) {
438 if (iTimeField) {
439 int offset = getOffsetToAdd(instant);
440 long localInstant = iField.add(instant + offset, value);
441 return localInstant - offset;
442 } else {
443 long localInstant = iZone.convertUTCToLocal(instant);
444 localInstant = iField.add(localInstant, value);
445 return iZone.convertLocalToUTC(localInstant, false, instant);
446 }
447 }
448
449 public long addWrapField(long instant, int value) {
450 if (iTimeField) {
451 int offset = getOffsetToAdd(instant);
452 long localInstant = iField.addWrapField(instant + offset, value);
453 return localInstant - offset;
454 } else {
455 long localInstant = iZone.convertUTCToLocal(instant);
456 localInstant = iField.addWrapField(localInstant, value);
457 return iZone.convertLocalToUTC(localInstant, false, instant);
458 }
459 }
460
461 public long set(long instant, int value) {
462 long localInstant = iZone.convertUTCToLocal(instant);
463 localInstant = iField.set(localInstant, value);
464 long result = iZone.convertLocalToUTC(localInstant, false, instant);
465 if (get(result) != value) {
466 IllegalInstantException cause = new IllegalInstantException(localInstant, iZone.getID());
467 IllegalFieldValueException ex = new IllegalFieldValueException(iField.getType(), Integer.valueOf(value), cause.getMessage());
468 ex.initCause(cause);
469 throw ex;
470 }
471 return result;
472 }
473
474 public long set(long instant, String text, Locale locale) {
475
476 long localInstant = iZone.convertUTCToLocal(instant);
477 localInstant = iField.set(localInstant, text, locale);
478 return iZone.convertLocalToUTC(localInstant, false, instant);
479 }
480
481 public int getDifference(long minuendInstant, long subtrahendInstant) {
482 int offset = getOffsetToAdd(subtrahendInstant);
483 return iField.getDifference
484 (minuendInstant + (iTimeField ? offset : getOffsetToAdd(minuendInstant)),
485 subtrahendInstant + offset);
486 }
487
488 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
489 int offset = getOffsetToAdd(subtrahendInstant);
490 return iField.getDifferenceAsLong
491 (minuendInstant + (iTimeField ? offset : getOffsetToAdd(minuendInstant)),
492 subtrahendInstant + offset);
493 }
494
495 public final DurationField getDurationField() {
496 return iDurationField;
497 }
498
499 public final DurationField getRangeDurationField() {
500 return iRangeDurationField;
501 }
502
503 public boolean isLeap(long instant) {
504 long localInstant = iZone.convertUTCToLocal(instant);
505 return iField.isLeap(localInstant);
506 }
507
508 public int getLeapAmount(long instant) {
509 long localInstant = iZone.convertUTCToLocal(instant);
510 return iField.getLeapAmount(localInstant);
511 }
512
513 public final DurationField getLeapDurationField() {
514 return iLeapDurationField;
515 }
516
517 public long roundFloor(long instant) {
518 if (iTimeField) {
519 int offset = getOffsetToAdd(instant);
520 instant = iField.roundFloor(instant + offset);
521 return instant - offset;
522 } else {
523 long localInstant = iZone.convertUTCToLocal(instant);
524 localInstant = iField.roundFloor(localInstant);
525 return iZone.convertLocalToUTC(localInstant, false, instant);
526 }
527 }
528
529 public long roundCeiling(long instant) {
530 if (iTimeField) {
531 int offset = getOffsetToAdd(instant);
532 instant = iField.roundCeiling(instant + offset);
533 return instant - offset;
534 } else {
535 long localInstant = iZone.convertUTCToLocal(instant);
536 localInstant = iField.roundCeiling(localInstant);
537 return iZone.convertLocalToUTC(localInstant, false, instant);
538 }
539 }
540
541 public long remainder(long instant) {
542 long localInstant = iZone.convertUTCToLocal(instant);
543 return iField.remainder(localInstant);
544 }
545
546 public int getMinimumValue() {
547 return iField.getMinimumValue();
548 }
549
550 public int getMinimumValue(long instant) {
551 long localInstant = iZone.convertUTCToLocal(instant);
552 return iField.getMinimumValue(localInstant);
553 }
554
555 public int getMinimumValue(ReadablePartial instant) {
556 return iField.getMinimumValue(instant);
557 }
558
559 public int getMinimumValue(ReadablePartial instant, int[] values) {
560 return iField.getMinimumValue(instant, values);
561 }
562
563 public int getMaximumValue() {
564 return iField.getMaximumValue();
565 }
566
567 public int getMaximumValue(long instant) {
568 long localInstant = iZone.convertUTCToLocal(instant);
569 return iField.getMaximumValue(localInstant);
570 }
571
572 public int getMaximumValue(ReadablePartial instant) {
573 return iField.getMaximumValue(instant);
574 }
575
576 public int getMaximumValue(ReadablePartial instant, int[] values) {
577 return iField.getMaximumValue(instant, values);
578 }
579
580 public int getMaximumTextLength(Locale locale) {
581 return iField.getMaximumTextLength(locale);
582 }
583
584 public int getMaximumShortTextLength(Locale locale) {
585 return iField.getMaximumShortTextLength(locale);
586 }
587
588 private int getOffsetToAdd(long instant) {
589 int offset = this.iZone.getOffset(instant);
590 long sum = instant + offset;
591
592 if ((instant ^ sum) < 0 && (instant ^ offset) >= 0) {
593 throw new ArithmeticException("Adding time zone offset caused overflow");
594 }
595 return offset;
596 }
597 }
598
599 }