001 /* 002 * Copyright 2001-2009 Stephen Colebourne 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.joda.time.chrono; 017 018 import java.util.ArrayList; 019 import java.util.HashMap; 020 import java.util.Locale; 021 import java.util.Map; 022 023 import org.joda.time.Chronology; 024 import org.joda.time.DateTimeField; 025 import org.joda.time.DateTimeUtils; 026 import org.joda.time.DateTimeZone; 027 import org.joda.time.DurationField; 028 import org.joda.time.IllegalFieldValueException; 029 import org.joda.time.Instant; 030 import org.joda.time.ReadableInstant; 031 import org.joda.time.ReadablePartial; 032 import org.joda.time.field.BaseDateTimeField; 033 import org.joda.time.field.DecoratedDurationField; 034 import org.joda.time.format.DateTimeFormatter; 035 import org.joda.time.format.ISODateTimeFormat; 036 037 /** 038 * Implements the Gregorian/Julian calendar system which is the calendar system 039 * used in most of the world. Wherever possible, it is recommended to use the 040 * {@link ISOChronology} instead. 041 * <p> 042 * The Gregorian calendar replaced the Julian calendar, and the point in time 043 * when this chronology switches can be controlled using the second parameter 044 * of the getInstance method. By default this cutover is set to the date the 045 * Gregorian calendar was first instituted, October 15, 1582. 046 * <p> 047 * Before this date, this chronology uses the proleptic Julian calendar 048 * (proleptic means extending indefinitely). The Julian calendar has leap years 049 * every four years, whereas the Gregorian has special rules for 100 and 400 050 * years. A meaningful result will thus be obtained for all input values. 051 * However before 8 CE, Julian leap years were irregular, and before 45 BCE 052 * there was no Julian calendar. 053 * <p> 054 * This chronology differs from 055 * {@link java.util.GregorianCalendar GregorianCalendar} in that years 056 * in BCE are returned correctly. Thus year 1 BCE is returned as -1 instead of 1. 057 * The yearOfEra field produces results compatible with GregorianCalendar. 058 * <p> 059 * The Julian calendar does not have a year zero, and so year -1 is followed by 060 * year 1. If the Gregorian cutover date is specified at or before year -1 061 * (Julian), year zero is defined. In other words, the proleptic Gregorian 062 * chronology used by this class has a year zero. 063 * <p> 064 * To create a pure proleptic Julian chronology, use {@link JulianChronology}, 065 * and to create a pure proleptic Gregorian chronology, use 066 * {@link GregorianChronology}. 067 * <p> 068 * GJChronology is thread-safe and immutable. 069 * 070 * @author Brian S O'Neill 071 * @author Stephen Colebourne 072 * @since 1.0 073 */ 074 public final class GJChronology extends AssembledChronology { 075 076 /** Serialization lock */ 077 private static final long serialVersionUID = -2545574827706931671L; 078 079 /** 080 * Convert a datetime from one chronology to another. 081 */ 082 private static long convertByYear(long instant, Chronology from, Chronology to) { 083 return to.getDateTimeMillis 084 (from.year().get(instant), 085 from.monthOfYear().get(instant), 086 from.dayOfMonth().get(instant), 087 from.millisOfDay().get(instant)); 088 } 089 090 /** 091 * Convert a datetime from one chronology to another. 092 */ 093 private static long convertByWeekyear(final long instant, Chronology from, Chronology to) { 094 long newInstant; 095 newInstant = to.weekyear().set(0, from.weekyear().get(instant)); 096 newInstant = to.weekOfWeekyear().set(newInstant, from.weekOfWeekyear().get(instant)); 097 newInstant = to.dayOfWeek().set(newInstant, from.dayOfWeek().get(instant)); 098 newInstant = to.millisOfDay().set(newInstant, from.millisOfDay().get(instant)); 099 return newInstant; 100 } 101 102 /** 103 * The default GregorianJulian cutover point. 104 */ 105 static final Instant DEFAULT_CUTOVER = new Instant(-12219292800000L); 106 107 /** Cache of zone to chronology list */ 108 private static final Map<DateTimeZone, ArrayList<GJChronology>> cCache = new HashMap<DateTimeZone, ArrayList<GJChronology>>(); 109 110 /** 111 * Factory method returns instances of the default GJ cutover 112 * chronology. This uses a cutover date of October 15, 1582 (Gregorian) 113 * 00:00:00 UTC. For this value, October 4, 1582 (Julian) is followed by 114 * October 15, 1582 (Gregorian). 115 * 116 * <p>The first day of the week is designated to be 117 * {@link org.joda.time.DateTimeConstants#MONDAY Monday}, 118 * and the minimum days in the first week of the year is 4. 119 * 120 * <p>The time zone of the returned instance is UTC. 121 */ 122 public static GJChronology getInstanceUTC() { 123 return getInstance(DateTimeZone.UTC, DEFAULT_CUTOVER, 4); 124 } 125 126 /** 127 * Factory method returns instances of the default GJ cutover 128 * chronology. This uses a cutover date of October 15, 1582 (Gregorian) 129 * 00:00:00 UTC. For this value, October 4, 1582 (Julian) is followed by 130 * October 15, 1582 (Gregorian). 131 * 132 * <p>The first day of the week is designated to be 133 * {@link org.joda.time.DateTimeConstants#MONDAY Monday}, 134 * and the minimum days in the first week of the year is 4. 135 * 136 * <p>The returned chronology is in the default time zone. 137 */ 138 public static GJChronology getInstance() { 139 return getInstance(DateTimeZone.getDefault(), DEFAULT_CUTOVER, 4); 140 } 141 142 /** 143 * Factory method returns instances of the GJ cutover chronology. This uses 144 * a cutover date of October 15, 1582 (Gregorian) 00:00:00 UTC. For this 145 * value, October 4, 1582 (Julian) is followed by October 15, 1582 146 * (Gregorian). 147 * 148 * <p>The first day of the week is designated to be 149 * {@link org.joda.time.DateTimeConstants#MONDAY Monday}, 150 * and the minimum days in the first week of the year is 4. 151 * 152 * @param zone the time zone to use, null is default 153 */ 154 public static GJChronology getInstance(DateTimeZone zone) { 155 return getInstance(zone, DEFAULT_CUTOVER, 4); 156 } 157 158 /** 159 * Factory method returns instances of the GJ cutover chronology. Any 160 * cutover date may be specified. 161 * 162 * <p>The first day of the week is designated to be 163 * {@link org.joda.time.DateTimeConstants#MONDAY Monday}, 164 * and the minimum days in the first week of the year is 4. 165 * 166 * @param zone the time zone to use, null is default 167 * @param gregorianCutover the cutover to use, null means default 168 */ 169 public static GJChronology getInstance( 170 DateTimeZone zone, 171 ReadableInstant gregorianCutover) { 172 173 return getInstance(zone, gregorianCutover, 4); 174 } 175 176 /** 177 * Factory method returns instances of the GJ cutover chronology. Any 178 * cutover date may be specified. 179 * 180 * @param zone the time zone to use, null is default 181 * @param gregorianCutover the cutover to use, null means default 182 * @param minDaysInFirstWeek minimum number of days in first week of the year; default is 4 183 */ 184 public static synchronized GJChronology getInstance( 185 DateTimeZone zone, 186 ReadableInstant gregorianCutover, 187 int minDaysInFirstWeek) { 188 189 zone = DateTimeUtils.getZone(zone); 190 Instant cutoverInstant; 191 if (gregorianCutover == null) { 192 cutoverInstant = DEFAULT_CUTOVER; 193 } else { 194 cutoverInstant = gregorianCutover.toInstant(); 195 } 196 197 GJChronology chrono; 198 199 ArrayList<GJChronology> chronos = cCache.get(zone); 200 if (chronos == null) { 201 chronos = new ArrayList<GJChronology>(2); 202 cCache.put(zone, chronos); 203 } else { 204 for (int i=chronos.size(); --i>=0; ) { 205 chrono = chronos.get(i); 206 if (minDaysInFirstWeek == chrono.getMinimumDaysInFirstWeek() && 207 cutoverInstant.equals(chrono.getGregorianCutover())) { 208 209 return chrono; 210 } 211 } 212 } 213 214 if (zone == DateTimeZone.UTC) { 215 chrono = new GJChronology 216 (JulianChronology.getInstance(zone, minDaysInFirstWeek), 217 GregorianChronology.getInstance(zone, minDaysInFirstWeek), 218 cutoverInstant); 219 } else { 220 chrono = getInstance(DateTimeZone.UTC, cutoverInstant, minDaysInFirstWeek); 221 chrono = new GJChronology 222 (ZonedChronology.getInstance(chrono, zone), 223 chrono.iJulianChronology, 224 chrono.iGregorianChronology, 225 chrono.iCutoverInstant); 226 } 227 228 chronos.add(chrono); 229 230 return chrono; 231 } 232 233 /** 234 * Factory method returns instances of the GJ cutover chronology. Any 235 * cutover date may be specified. 236 * 237 * @param zone the time zone to use, null is default 238 * @param gregorianCutover the cutover to use 239 * @param minDaysInFirstWeek minimum number of days in first week of the year; default is 4 240 */ 241 public static GJChronology getInstance( 242 DateTimeZone zone, 243 long gregorianCutover, 244 int minDaysInFirstWeek) { 245 246 Instant cutoverInstant; 247 if (gregorianCutover == DEFAULT_CUTOVER.getMillis()) { 248 cutoverInstant = null; 249 } else { 250 cutoverInstant = new Instant(gregorianCutover); 251 } 252 return getInstance(zone, cutoverInstant, minDaysInFirstWeek); 253 } 254 255 //----------------------------------------------------------------------- 256 private JulianChronology iJulianChronology; 257 private GregorianChronology iGregorianChronology; 258 private Instant iCutoverInstant; 259 260 private long iCutoverMillis; 261 private long iGapDuration; 262 263 /** 264 * @param julian chronology used before the cutover instant 265 * @param gregorian chronology used at and after the cutover instant 266 * @param cutoverInstant instant when the gregorian chronology began 267 */ 268 private GJChronology(JulianChronology julian, 269 GregorianChronology gregorian, 270 Instant cutoverInstant) { 271 super(null, new Object[] {julian, gregorian, cutoverInstant}); 272 } 273 274 /** 275 * Called when applying a time zone. 276 */ 277 private GJChronology(Chronology base, 278 JulianChronology julian, 279 GregorianChronology gregorian, 280 Instant cutoverInstant) { 281 super(base, new Object[] {julian, gregorian, cutoverInstant}); 282 } 283 284 /** 285 * Serialization singleton 286 */ 287 private Object readResolve() { 288 return getInstance(getZone(), iCutoverInstant, getMinimumDaysInFirstWeek()); 289 } 290 291 public DateTimeZone getZone() { 292 Chronology base; 293 if ((base = getBase()) != null) { 294 return base.getZone(); 295 } 296 return DateTimeZone.UTC; 297 } 298 299 // Conversion 300 //----------------------------------------------------------------------- 301 /** 302 * Gets the Chronology in the UTC time zone. 303 * 304 * @return the chronology in UTC 305 */ 306 public Chronology withUTC() { 307 return withZone(DateTimeZone.UTC); 308 } 309 310 /** 311 * Gets the Chronology in a specific time zone. 312 * 313 * @param zone the zone to get the chronology in, null is default 314 * @return the chronology 315 */ 316 public Chronology withZone(DateTimeZone zone) { 317 if (zone == null) { 318 zone = DateTimeZone.getDefault(); 319 } 320 if (zone == getZone()) { 321 return this; 322 } 323 return getInstance(zone, iCutoverInstant, getMinimumDaysInFirstWeek()); 324 } 325 326 public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth, 327 int millisOfDay) 328 throws IllegalArgumentException 329 { 330 Chronology base; 331 if ((base = getBase()) != null) { 332 return base.getDateTimeMillis(year, monthOfYear, dayOfMonth, millisOfDay); 333 } 334 335 // Assume date is Gregorian. 336 long instant = iGregorianChronology.getDateTimeMillis 337 (year, monthOfYear, dayOfMonth, millisOfDay); 338 if (instant < iCutoverMillis) { 339 // Maybe it's Julian. 340 instant = iJulianChronology.getDateTimeMillis 341 (year, monthOfYear, dayOfMonth, millisOfDay); 342 if (instant >= iCutoverMillis) { 343 // Okay, it's in the illegal cutover gap. 344 throw new IllegalArgumentException("Specified date does not exist"); 345 } 346 } 347 return instant; 348 } 349 350 public long getDateTimeMillis(int year, int monthOfYear, int dayOfMonth, 351 int hourOfDay, int minuteOfHour, 352 int secondOfMinute, int millisOfSecond) 353 throws IllegalArgumentException 354 { 355 Chronology base; 356 if ((base = getBase()) != null) { 357 return base.getDateTimeMillis 358 (year, monthOfYear, dayOfMonth, 359 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond); 360 } 361 362 // Assume date is Gregorian. 363 long instant = iGregorianChronology.getDateTimeMillis 364 (year, monthOfYear, dayOfMonth, 365 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond); 366 if (instant < iCutoverMillis) { 367 // Maybe it's Julian. 368 instant = iJulianChronology.getDateTimeMillis 369 (year, monthOfYear, dayOfMonth, 370 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond); 371 if (instant >= iCutoverMillis) { 372 // Okay, it's in the illegal cutover gap. 373 throw new IllegalArgumentException("Specified date does not exist"); 374 } 375 } 376 return instant; 377 } 378 379 /** 380 * Gets the cutover instant between Gregorian and Julian chronologies. 381 * @return the cutover instant 382 */ 383 public Instant getGregorianCutover() { 384 return iCutoverInstant; 385 } 386 387 /** 388 * Gets the minimum days needed for a week to be the first week in a year. 389 * 390 * @return the minimum days 391 */ 392 public int getMinimumDaysInFirstWeek() { 393 return iGregorianChronology.getMinimumDaysInFirstWeek(); 394 } 395 396 /** 397 * Checks if this chronology instance equals another. 398 * 399 * @param obj the object to compare to 400 * @return true if equal 401 * @since 1.6 402 */ 403 public boolean equals(Object obj) { 404 return super.equals(obj); 405 } 406 407 /** 408 * A suitable hash code for the chronology. 409 * 410 * @return the hash code 411 * @since 1.6 412 */ 413 public int hashCode() { 414 return "GJ".hashCode() * 11 + iJulianChronology.hashCode() + 415 iGregorianChronology.hashCode() + iCutoverInstant.hashCode(); 416 } 417 418 // Output 419 //----------------------------------------------------------------------- 420 /** 421 * Gets a debugging toString. 422 * 423 * @return a debugging string 424 */ 425 public String toString() { 426 StringBuffer sb = new StringBuffer(60); 427 sb.append("GJChronology"); 428 sb.append('['); 429 sb.append(getZone().getID()); 430 431 if (iCutoverMillis != DEFAULT_CUTOVER.getMillis()) { 432 sb.append(",cutover="); 433 DateTimeFormatter printer; 434 if (withUTC().dayOfYear().remainder(iCutoverMillis) == 0) { 435 printer = ISODateTimeFormat.date(); 436 } else { 437 printer = ISODateTimeFormat.dateTime(); 438 } 439 printer.withChronology(withUTC()).printTo(sb, iCutoverMillis); 440 } 441 442 if (getMinimumDaysInFirstWeek() != 4) { 443 sb.append(",mdfw="); 444 sb.append(getMinimumDaysInFirstWeek()); 445 } 446 sb.append(']'); 447 448 return sb.toString(); 449 } 450 451 protected void assemble(Fields fields) { 452 Object[] params = (Object[])getParam(); 453 454 JulianChronology julian = (JulianChronology)params[0]; 455 GregorianChronology gregorian = (GregorianChronology)params[1]; 456 Instant cutoverInstant = (Instant)params[2]; 457 iCutoverMillis = cutoverInstant.getMillis(); 458 459 iJulianChronology = julian; 460 iGregorianChronology = gregorian; 461 iCutoverInstant = cutoverInstant; 462 463 if (getBase() != null) { 464 return; 465 } 466 467 if (julian.getMinimumDaysInFirstWeek() != gregorian.getMinimumDaysInFirstWeek()) { 468 throw new IllegalArgumentException(); 469 } 470 471 // Compute difference between the chronologies at the cutover instant 472 iGapDuration = iCutoverMillis - julianToGregorianByYear(iCutoverMillis); 473 474 // Begin field definitions. 475 476 // First just copy all the Gregorian fields and then override those 477 // that need special attention. 478 fields.copyFieldsFrom(gregorian); 479 480 // Assuming cutover is at midnight, all time of day fields can be 481 // gregorian since they are unaffected by cutover. 482 483 // Verify assumption. 484 if (gregorian.millisOfDay().get(iCutoverMillis) == 0) { 485 // Cutover is sometime in the day, so cutover fields are required 486 // for time of day. 487 488 fields.millisOfSecond = new CutoverField(julian.millisOfSecond(), fields.millisOfSecond, iCutoverMillis); 489 fields.millisOfDay = new CutoverField(julian.millisOfDay(), fields.millisOfDay, iCutoverMillis); 490 fields.secondOfMinute = new CutoverField(julian.secondOfMinute(), fields.secondOfMinute, iCutoverMillis); 491 fields.secondOfDay = new CutoverField(julian.secondOfDay(), fields.secondOfDay, iCutoverMillis); 492 fields.minuteOfHour = new CutoverField(julian.minuteOfHour(), fields.minuteOfHour, iCutoverMillis); 493 fields.minuteOfDay = new CutoverField(julian.minuteOfDay(), fields.minuteOfDay, iCutoverMillis); 494 fields.hourOfDay = new CutoverField(julian.hourOfDay(), fields.hourOfDay, iCutoverMillis); 495 fields.hourOfHalfday = new CutoverField(julian.hourOfHalfday(), fields.hourOfHalfday, iCutoverMillis); 496 fields.clockhourOfDay = new CutoverField(julian.clockhourOfDay(), fields.clockhourOfDay, iCutoverMillis); 497 fields.clockhourOfHalfday = new CutoverField(julian.clockhourOfHalfday(), 498 fields.clockhourOfHalfday, iCutoverMillis); 499 fields.halfdayOfDay = new CutoverField(julian.halfdayOfDay(), fields.halfdayOfDay, iCutoverMillis); 500 } 501 502 // These fields just require basic cutover support. 503 { 504 fields.era = new CutoverField(julian.era(), fields.era, iCutoverMillis); 505 } 506 507 // DayOfYear and weekOfWeekyear require special handling since cutover 508 // year has fewer days and weeks. Extend the cutover to the start of 509 // the next year or weekyear. This keeps the sequence unbroken during 510 // the cutover year. 511 512 { 513 long cutover = gregorian.year().roundCeiling(iCutoverMillis); 514 fields.dayOfYear = new CutoverField( 515 julian.dayOfYear(), fields.dayOfYear, cutover); 516 } 517 518 { 519 long cutover = gregorian.weekyear().roundCeiling(iCutoverMillis); 520 fields.weekOfWeekyear = new CutoverField( 521 julian.weekOfWeekyear(), fields.weekOfWeekyear, cutover, true); 522 } 523 524 // These fields are special because they have imprecise durations. The 525 // family of addition methods need special attention. Override affected 526 // duration fields as well. 527 { 528 fields.year = new ImpreciseCutoverField( 529 julian.year(), fields.year, iCutoverMillis); 530 fields.years = fields.year.getDurationField(); 531 fields.yearOfEra = new ImpreciseCutoverField( 532 julian.yearOfEra(), fields.yearOfEra, fields.years, iCutoverMillis); 533 fields.yearOfCentury = new ImpreciseCutoverField( 534 julian.yearOfCentury(), fields.yearOfCentury, fields.years, iCutoverMillis); 535 536 fields.centuryOfEra = new ImpreciseCutoverField( 537 julian.centuryOfEra(), fields.centuryOfEra, iCutoverMillis); 538 fields.centuries = fields.centuryOfEra.getDurationField(); 539 540 fields.monthOfYear = new ImpreciseCutoverField( 541 julian.monthOfYear(), fields.monthOfYear, iCutoverMillis); 542 fields.months = fields.monthOfYear.getDurationField(); 543 544 fields.weekyear = new ImpreciseCutoverField( 545 julian.weekyear(), fields.weekyear, null, iCutoverMillis, true); 546 fields.weekyearOfCentury = new ImpreciseCutoverField( 547 julian.weekyearOfCentury(), fields.weekyearOfCentury, fields.weekyears, iCutoverMillis); 548 fields.weekyears = fields.weekyear.getDurationField(); 549 } 550 551 // These fields require basic cutover support, except they must link to 552 // imprecise durations. 553 { 554 CutoverField cf = new CutoverField 555 (julian.dayOfMonth(), fields.dayOfMonth, iCutoverMillis); 556 cf.iRangeDurationField = fields.months; 557 fields.dayOfMonth = cf; 558 } 559 } 560 561 long julianToGregorianByYear(long instant) { 562 return convertByYear(instant, iJulianChronology, iGregorianChronology); 563 } 564 565 long gregorianToJulianByYear(long instant) { 566 return convertByYear(instant, iGregorianChronology, iJulianChronology); 567 } 568 569 long julianToGregorianByWeekyear(long instant) { 570 return convertByWeekyear(instant, iJulianChronology, iGregorianChronology); 571 } 572 573 long gregorianToJulianByWeekyear(long instant) { 574 return convertByWeekyear(instant, iGregorianChronology, iJulianChronology); 575 } 576 577 //----------------------------------------------------------------------- 578 /** 579 * This basic cutover field adjusts calls to 'get' and 'set' methods, and 580 * assumes that calls to add and addWrapField are unaffected by the cutover. 581 */ 582 private class CutoverField extends BaseDateTimeField { 583 private static final long serialVersionUID = 3528501219481026402L; 584 585 final DateTimeField iJulianField; 586 final DateTimeField iGregorianField; 587 final long iCutover; 588 final boolean iConvertByWeekyear; 589 590 protected DurationField iDurationField; 591 protected DurationField iRangeDurationField; 592 593 /** 594 * @param julianField field from the chronology used before the cutover instant 595 * @param gregorianField field from the chronology used at and after the cutover 596 * @param cutoverMillis the millis of the cutover 597 */ 598 CutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) { 599 this(julianField, gregorianField, cutoverMillis, false); 600 } 601 602 /** 603 * @param julianField field from the chronology used before the cutover instant 604 * @param gregorianField field from the chronology used at and after the cutover 605 * @param cutoverMillis the millis of the cutover 606 * @param convertByWeekyear 607 */ 608 CutoverField(DateTimeField julianField, DateTimeField gregorianField, 609 long cutoverMillis, boolean convertByWeekyear) { 610 super(gregorianField.getType()); 611 iJulianField = julianField; 612 iGregorianField = gregorianField; 613 iCutover = cutoverMillis; 614 iConvertByWeekyear = convertByWeekyear; 615 // Although average length of Julian and Gregorian years differ, 616 // use the Gregorian duration field because it is more accurate. 617 iDurationField = gregorianField.getDurationField(); 618 619 DurationField rangeField = gregorianField.getRangeDurationField(); 620 if (rangeField == null) { 621 rangeField = julianField.getRangeDurationField(); 622 } 623 iRangeDurationField = rangeField; 624 } 625 626 public boolean isLenient() { 627 return false; 628 } 629 630 public int get(long instant) { 631 if (instant >= iCutover) { 632 return iGregorianField.get(instant); 633 } else { 634 return iJulianField.get(instant); 635 } 636 } 637 638 public String getAsText(long instant, Locale locale) { 639 if (instant >= iCutover) { 640 return iGregorianField.getAsText(instant, locale); 641 } else { 642 return iJulianField.getAsText(instant, locale); 643 } 644 } 645 646 public String getAsText(int fieldValue, Locale locale) { 647 return iGregorianField.getAsText(fieldValue, locale); 648 } 649 650 public String getAsShortText(long instant, Locale locale) { 651 if (instant >= iCutover) { 652 return iGregorianField.getAsShortText(instant, locale); 653 } else { 654 return iJulianField.getAsShortText(instant, locale); 655 } 656 } 657 658 public String getAsShortText(int fieldValue, Locale locale) { 659 return iGregorianField.getAsShortText(fieldValue, locale); 660 } 661 662 public long add(long instant, int value) { 663 return iGregorianField.add(instant, value); 664 } 665 666 public long add(long instant, long value) { 667 return iGregorianField.add(instant, value); 668 } 669 670 public int[] add(ReadablePartial partial, int fieldIndex, int[] values, int valueToAdd) { 671 // overridden as superclass algorithm can't handle 672 // 2004-02-29 + 48 months -> 2008-02-29 type dates 673 if (valueToAdd == 0) { 674 return values; 675 } 676 if (DateTimeUtils.isContiguous(partial)) { 677 long instant = 0L; 678 for (int i = 0, isize = partial.size(); i < isize; i++) { 679 instant = partial.getFieldType(i).getField(GJChronology.this).set(instant, values[i]); 680 } 681 instant = add(instant, valueToAdd); 682 return GJChronology.this.get(partial, instant); 683 } else { 684 return super.add(partial, fieldIndex, values, valueToAdd); 685 } 686 } 687 688 public int getDifference(long minuendInstant, long subtrahendInstant) { 689 return iGregorianField.getDifference(minuendInstant, subtrahendInstant); 690 } 691 692 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) { 693 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 694 } 695 696 public long set(long instant, int value) { 697 if (instant >= iCutover) { 698 instant = iGregorianField.set(instant, value); 699 if (instant < iCutover) { 700 // Only adjust if gap fully crossed. 701 if (instant + iGapDuration < iCutover) { 702 instant = gregorianToJulian(instant); 703 } 704 // Verify that new value stuck. 705 if (get(instant) != value) { 706 throw new IllegalFieldValueException 707 (iGregorianField.getType(), Integer.valueOf(value), null, null); 708 } 709 } 710 } else { 711 instant = iJulianField.set(instant, value); 712 if (instant >= iCutover) { 713 // Only adjust if gap fully crossed. 714 if (instant - iGapDuration >= iCutover) { 715 instant = julianToGregorian(instant); 716 } 717 // Verify that new value stuck. 718 if (get(instant) != value) { 719 throw new IllegalFieldValueException 720 (iJulianField.getType(), Integer.valueOf(value), null, null); 721 } 722 } 723 } 724 return instant; 725 } 726 727 public long set(long instant, String text, Locale locale) { 728 if (instant >= iCutover) { 729 instant = iGregorianField.set(instant, text, locale); 730 if (instant < iCutover) { 731 // Only adjust if gap fully crossed. 732 if (instant + iGapDuration < iCutover) { 733 instant = gregorianToJulian(instant); 734 } 735 // Cannot verify that new value stuck because set may be lenient. 736 } 737 } else { 738 instant = iJulianField.set(instant, text, locale); 739 if (instant >= iCutover) { 740 // Only adjust if gap fully crossed. 741 if (instant - iGapDuration >= iCutover) { 742 instant = julianToGregorian(instant); 743 } 744 // Cannot verify that new value stuck because set may be lenient. 745 } 746 } 747 return instant; 748 } 749 750 public DurationField getDurationField() { 751 return iDurationField; 752 } 753 754 public DurationField getRangeDurationField() { 755 return iRangeDurationField; 756 } 757 758 public boolean isLeap(long instant) { 759 if (instant >= iCutover) { 760 return iGregorianField.isLeap(instant); 761 } else { 762 return iJulianField.isLeap(instant); 763 } 764 } 765 766 public int getLeapAmount(long instant) { 767 if (instant >= iCutover) { 768 return iGregorianField.getLeapAmount(instant); 769 } else { 770 return iJulianField.getLeapAmount(instant); 771 } 772 } 773 774 public DurationField getLeapDurationField() { 775 return iGregorianField.getLeapDurationField(); 776 } 777 778 779 public int getMinimumValue() { 780 // For all precise fields, the Julian and Gregorian limits are 781 // identical. Choose Julian to tighten up the year limits. 782 return iJulianField.getMinimumValue(); 783 } 784 785 public int getMinimumValue(ReadablePartial partial) { 786 return iJulianField.getMinimumValue(partial); 787 } 788 789 public int getMinimumValue(ReadablePartial partial, int[] values) { 790 return iJulianField.getMinimumValue(partial, values); 791 } 792 793 public int getMinimumValue(long instant) { 794 if (instant < iCutover) { 795 return iJulianField.getMinimumValue(instant); 796 } 797 798 int min = iGregorianField.getMinimumValue(instant); 799 800 // Because the cutover may reduce the length of this field, verify 801 // the minimum by setting it. 802 instant = iGregorianField.set(instant, min); 803 if (instant < iCutover) { 804 min = iGregorianField.get(iCutover); 805 } 806 807 return min; 808 } 809 810 public int getMaximumValue() { 811 // For all precise fields, the Julian and Gregorian limits are 812 // identical. 813 return iGregorianField.getMaximumValue(); 814 } 815 816 public int getMaximumValue(long instant) { 817 if (instant >= iCutover) { 818 return iGregorianField.getMaximumValue(instant); 819 } 820 821 int max = iJulianField.getMaximumValue(instant); 822 823 // Because the cutover may reduce the length of this field, verify 824 // the maximum by setting it. 825 instant = iJulianField.set(instant, max); 826 if (instant >= iCutover) { 827 max = iJulianField.get(iJulianField.add(iCutover, -1)); 828 } 829 830 return max; 831 } 832 833 public int getMaximumValue(ReadablePartial partial) { 834 long instant = GJChronology.getInstanceUTC().set(partial, 0L); 835 return getMaximumValue(instant); 836 } 837 838 public int getMaximumValue(ReadablePartial partial, int[] values) { 839 Chronology chrono = GJChronology.getInstanceUTC(); 840 long instant = 0L; 841 for (int i = 0, isize = partial.size(); i < isize; i++) { 842 DateTimeField field = partial.getFieldType(i).getField(chrono); 843 if (values[i] <= field.getMaximumValue(instant)) { 844 instant = field.set(instant, values[i]); 845 } 846 } 847 return getMaximumValue(instant); 848 } 849 850 public long roundFloor(long instant) { 851 if (instant >= iCutover) { 852 instant = iGregorianField.roundFloor(instant); 853 if (instant < iCutover) { 854 // Only adjust if gap fully crossed. 855 if (instant + iGapDuration < iCutover) { 856 instant = gregorianToJulian(instant); 857 } 858 } 859 } else { 860 instant = iJulianField.roundFloor(instant); 861 } 862 return instant; 863 } 864 865 public long roundCeiling(long instant) { 866 if (instant >= iCutover) { 867 instant = iGregorianField.roundCeiling(instant); 868 } else { 869 instant = iJulianField.roundCeiling(instant); 870 if (instant >= iCutover) { 871 // Only adjust if gap fully crossed. 872 if (instant - iGapDuration >= iCutover) { 873 instant = julianToGregorian(instant); 874 } 875 } 876 } 877 return instant; 878 } 879 880 public int getMaximumTextLength(Locale locale) { 881 return Math.max(iJulianField.getMaximumTextLength(locale), 882 iGregorianField.getMaximumTextLength(locale)); 883 } 884 885 public int getMaximumShortTextLength(Locale locale) { 886 return Math.max(iJulianField.getMaximumShortTextLength(locale), 887 iGregorianField.getMaximumShortTextLength(locale)); 888 } 889 890 protected long julianToGregorian(long instant) { 891 if (iConvertByWeekyear) { 892 return julianToGregorianByWeekyear(instant); 893 } else { 894 return julianToGregorianByYear(instant); 895 } 896 } 897 898 protected long gregorianToJulian(long instant) { 899 if (iConvertByWeekyear) { 900 return gregorianToJulianByWeekyear(instant); 901 } else { 902 return gregorianToJulianByYear(instant); 903 } 904 } 905 } 906 907 //----------------------------------------------------------------------- 908 /** 909 * Cutover field for variable length fields. These fields internally call 910 * set whenever add is called. As a result, the same correction applied to 911 * set must be applied to add and addWrapField. Knowing when to use this 912 * field requires specific knowledge of how the GJ fields are implemented. 913 */ 914 private final class ImpreciseCutoverField extends CutoverField { 915 private static final long serialVersionUID = 3410248757173576441L; 916 917 /** 918 * Creates a duration field that links back to this. 919 */ 920 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) { 921 this(julianField, gregorianField, null, cutoverMillis, false); 922 } 923 924 /** 925 * Uses a shared duration field rather than creating a new one. 926 * 927 * @param durationField shared duration field 928 */ 929 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, 930 DurationField durationField, long cutoverMillis) 931 { 932 this(julianField, gregorianField, durationField, cutoverMillis, false); 933 } 934 935 /** 936 * Uses a shared duration field rather than creating a new one. 937 * 938 * @param durationField shared duration field 939 */ 940 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, 941 DurationField durationField, 942 long cutoverMillis, boolean convertByWeekyear) 943 { 944 super(julianField, gregorianField, cutoverMillis, convertByWeekyear); 945 if (durationField == null) { 946 durationField = new LinkedDurationField(iDurationField, this); 947 } 948 iDurationField = durationField; 949 } 950 951 public long add(long instant, int value) { 952 if (instant >= iCutover) { 953 instant = iGregorianField.add(instant, value); 954 if (instant < iCutover) { 955 // Only adjust if gap fully crossed. 956 if (instant + iGapDuration < iCutover) { 957 instant = gregorianToJulian(instant); 958 } 959 } 960 } else { 961 instant = iJulianField.add(instant, value); 962 if (instant >= iCutover) { 963 // Only adjust if gap fully crossed. 964 if (instant - iGapDuration >= iCutover) { 965 instant = julianToGregorian(instant); 966 } 967 } 968 } 969 return instant; 970 } 971 972 public long add(long instant, long value) { 973 if (instant >= iCutover) { 974 instant = iGregorianField.add(instant, value); 975 if (instant < iCutover) { 976 // Only adjust if gap fully crossed. 977 if (instant + iGapDuration < iCutover) { 978 instant = gregorianToJulian(instant); 979 } 980 } 981 } else { 982 instant = iJulianField.add(instant, value); 983 if (instant >= iCutover) { 984 // Only adjust if gap fully crossed. 985 if (instant - iGapDuration >= iCutover) { 986 instant = julianToGregorian(instant); 987 } 988 } 989 } 990 return instant; 991 } 992 993 public int getDifference(long minuendInstant, long subtrahendInstant) { 994 if (minuendInstant >= iCutover) { 995 if (subtrahendInstant >= iCutover) { 996 return iGregorianField.getDifference(minuendInstant, subtrahendInstant); 997 } 998 // Remember, the add is being reversed. Since subtrahend is 999 // Julian, convert minuend to Julian to match. 1000 minuendInstant = gregorianToJulian(minuendInstant); 1001 return iJulianField.getDifference(minuendInstant, subtrahendInstant); 1002 } else { 1003 if (subtrahendInstant < iCutover) { 1004 return iJulianField.getDifference(minuendInstant, subtrahendInstant); 1005 } 1006 // Remember, the add is being reversed. Since subtrahend is 1007 // Gregorian, convert minuend to Gregorian to match. 1008 minuendInstant = julianToGregorian(minuendInstant); 1009 return iGregorianField.getDifference(minuendInstant, subtrahendInstant); 1010 } 1011 } 1012 1013 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) { 1014 if (minuendInstant >= iCutover) { 1015 if (subtrahendInstant >= iCutover) { 1016 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 1017 } 1018 // Remember, the add is being reversed. Since subtrahend is 1019 // Julian, convert minuend to Julian to match. 1020 minuendInstant = gregorianToJulian(minuendInstant); 1021 return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 1022 } else { 1023 if (subtrahendInstant < iCutover) { 1024 return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 1025 } 1026 // Remember, the add is being reversed. Since subtrahend is 1027 // Gregorian, convert minuend to Gregorian to match. 1028 minuendInstant = julianToGregorian(minuendInstant); 1029 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 1030 } 1031 } 1032 1033 // Since the imprecise fields have durations longer than the gap 1034 // duration, keep these methods simple. The inherited implementations 1035 // produce incorrect results. 1036 // 1037 // Degenerate case: If this field is a month, and the cutover is set 1038 // far into the future, then the gap duration may be so large as to 1039 // reduce the number of months in a year. If the missing month(s) are 1040 // at the beginning or end of the year, then the minimum and maximum 1041 // values are not 1 and 12. I don't expect this case to ever occur. 1042 1043 public int getMinimumValue(long instant) { 1044 if (instant >= iCutover) { 1045 return iGregorianField.getMinimumValue(instant); 1046 } else { 1047 return iJulianField.getMinimumValue(instant); 1048 } 1049 } 1050 1051 public int getMaximumValue(long instant) { 1052 if (instant >= iCutover) { 1053 return iGregorianField.getMaximumValue(instant); 1054 } else { 1055 return iJulianField.getMaximumValue(instant); 1056 } 1057 } 1058 } 1059 1060 //----------------------------------------------------------------------- 1061 /** 1062 * Links the duration back to a ImpreciseCutoverField. 1063 */ 1064 private static class LinkedDurationField extends DecoratedDurationField { 1065 private static final long serialVersionUID = 4097975388007713084L; 1066 1067 private final ImpreciseCutoverField iField; 1068 1069 LinkedDurationField(DurationField durationField, ImpreciseCutoverField dateTimeField) { 1070 super(durationField, durationField.getType()); 1071 iField = dateTimeField; 1072 } 1073 1074 public long add(long instant, int value) { 1075 return iField.add(instant, value); 1076 } 1077 1078 public long add(long instant, long value) { 1079 return iField.add(instant, value); 1080 } 1081 1082 public int getDifference(long minuendInstant, long subtrahendInstant) { 1083 return iField.getDifference(minuendInstant, subtrahendInstant); 1084 } 1085 1086 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) { 1087 return iField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 1088 } 1089 } 1090 1091 }