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; 364 try { 365 instant = iGregorianChronology.getDateTimeMillis 366 (year, monthOfYear, dayOfMonth, 367 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond); 368 } catch (IllegalFieldValueException ex) { 369 if (monthOfYear != 2 || dayOfMonth != 29) { 370 throw ex; 371 } 372 instant = iGregorianChronology.getDateTimeMillis 373 (year, monthOfYear, 28, 374 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond); 375 if (instant >= iCutoverMillis) { 376 throw ex; 377 } 378 } 379 if (instant < iCutoverMillis) { 380 // Maybe it's Julian. 381 instant = iJulianChronology.getDateTimeMillis 382 (year, monthOfYear, dayOfMonth, 383 hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond); 384 if (instant >= iCutoverMillis) { 385 // Okay, it's in the illegal cutover gap. 386 throw new IllegalArgumentException("Specified date does not exist"); 387 } 388 } 389 return instant; 390 } 391 392 /** 393 * Gets the cutover instant between Gregorian and Julian chronologies. 394 * @return the cutover instant 395 */ 396 public Instant getGregorianCutover() { 397 return iCutoverInstant; 398 } 399 400 /** 401 * Gets the minimum days needed for a week to be the first week in a year. 402 * 403 * @return the minimum days 404 */ 405 public int getMinimumDaysInFirstWeek() { 406 return iGregorianChronology.getMinimumDaysInFirstWeek(); 407 } 408 409 /** 410 * Checks if this chronology instance equals another. 411 * 412 * @param obj the object to compare to 413 * @return true if equal 414 * @since 1.6 415 */ 416 public boolean equals(Object obj) { 417 return super.equals(obj); 418 } 419 420 /** 421 * A suitable hash code for the chronology. 422 * 423 * @return the hash code 424 * @since 1.6 425 */ 426 public int hashCode() { 427 return "GJ".hashCode() * 11 + iJulianChronology.hashCode() + 428 iGregorianChronology.hashCode() + iCutoverInstant.hashCode(); 429 } 430 431 // Output 432 //----------------------------------------------------------------------- 433 /** 434 * Gets a debugging toString. 435 * 436 * @return a debugging string 437 */ 438 public String toString() { 439 StringBuffer sb = new StringBuffer(60); 440 sb.append("GJChronology"); 441 sb.append('['); 442 sb.append(getZone().getID()); 443 444 if (iCutoverMillis != DEFAULT_CUTOVER.getMillis()) { 445 sb.append(",cutover="); 446 DateTimeFormatter printer; 447 if (withUTC().dayOfYear().remainder(iCutoverMillis) == 0) { 448 printer = ISODateTimeFormat.date(); 449 } else { 450 printer = ISODateTimeFormat.dateTime(); 451 } 452 printer.withChronology(withUTC()).printTo(sb, iCutoverMillis); 453 } 454 455 if (getMinimumDaysInFirstWeek() != 4) { 456 sb.append(",mdfw="); 457 sb.append(getMinimumDaysInFirstWeek()); 458 } 459 sb.append(']'); 460 461 return sb.toString(); 462 } 463 464 protected void assemble(Fields fields) { 465 Object[] params = (Object[])getParam(); 466 467 JulianChronology julian = (JulianChronology)params[0]; 468 GregorianChronology gregorian = (GregorianChronology)params[1]; 469 Instant cutoverInstant = (Instant)params[2]; 470 iCutoverMillis = cutoverInstant.getMillis(); 471 472 iJulianChronology = julian; 473 iGregorianChronology = gregorian; 474 iCutoverInstant = cutoverInstant; 475 476 if (getBase() != null) { 477 return; 478 } 479 480 if (julian.getMinimumDaysInFirstWeek() != gregorian.getMinimumDaysInFirstWeek()) { 481 throw new IllegalArgumentException(); 482 } 483 484 // Compute difference between the chronologies at the cutover instant 485 iGapDuration = iCutoverMillis - julianToGregorianByYear(iCutoverMillis); 486 487 // Begin field definitions. 488 489 // First just copy all the Gregorian fields and then override those 490 // that need special attention. 491 fields.copyFieldsFrom(gregorian); 492 493 // Assuming cutover is at midnight, all time of day fields can be 494 // gregorian since they are unaffected by cutover. 495 496 // Verify assumption. 497 if (gregorian.millisOfDay().get(iCutoverMillis) == 0) { 498 // Cutover is sometime in the day, so cutover fields are required 499 // for time of day. 500 501 fields.millisOfSecond = new CutoverField(julian.millisOfSecond(), fields.millisOfSecond, iCutoverMillis); 502 fields.millisOfDay = new CutoverField(julian.millisOfDay(), fields.millisOfDay, iCutoverMillis); 503 fields.secondOfMinute = new CutoverField(julian.secondOfMinute(), fields.secondOfMinute, iCutoverMillis); 504 fields.secondOfDay = new CutoverField(julian.secondOfDay(), fields.secondOfDay, iCutoverMillis); 505 fields.minuteOfHour = new CutoverField(julian.minuteOfHour(), fields.minuteOfHour, iCutoverMillis); 506 fields.minuteOfDay = new CutoverField(julian.minuteOfDay(), fields.minuteOfDay, iCutoverMillis); 507 fields.hourOfDay = new CutoverField(julian.hourOfDay(), fields.hourOfDay, iCutoverMillis); 508 fields.hourOfHalfday = new CutoverField(julian.hourOfHalfday(), fields.hourOfHalfday, iCutoverMillis); 509 fields.clockhourOfDay = new CutoverField(julian.clockhourOfDay(), fields.clockhourOfDay, iCutoverMillis); 510 fields.clockhourOfHalfday = new CutoverField(julian.clockhourOfHalfday(), 511 fields.clockhourOfHalfday, iCutoverMillis); 512 fields.halfdayOfDay = new CutoverField(julian.halfdayOfDay(), fields.halfdayOfDay, iCutoverMillis); 513 } 514 515 // These fields just require basic cutover support. 516 { 517 fields.era = new CutoverField(julian.era(), fields.era, iCutoverMillis); 518 } 519 520 // DayOfYear and weekOfWeekyear require special handling since cutover 521 // year has fewer days and weeks. Extend the cutover to the start of 522 // the next year or weekyear. This keeps the sequence unbroken during 523 // the cutover year. 524 525 { 526 long cutover = gregorian.year().roundCeiling(iCutoverMillis); 527 fields.dayOfYear = new CutoverField( 528 julian.dayOfYear(), fields.dayOfYear, cutover); 529 } 530 531 { 532 long cutover = gregorian.weekyear().roundCeiling(iCutoverMillis); 533 fields.weekOfWeekyear = new CutoverField( 534 julian.weekOfWeekyear(), fields.weekOfWeekyear, cutover, true); 535 } 536 537 // These fields are special because they have imprecise durations. The 538 // family of addition methods need special attention. Override affected 539 // duration fields as well. 540 { 541 fields.year = new ImpreciseCutoverField( 542 julian.year(), fields.year, iCutoverMillis); 543 fields.years = fields.year.getDurationField(); 544 fields.yearOfEra = new ImpreciseCutoverField( 545 julian.yearOfEra(), fields.yearOfEra, fields.years, iCutoverMillis); 546 fields.yearOfCentury = new ImpreciseCutoverField( 547 julian.yearOfCentury(), fields.yearOfCentury, fields.years, iCutoverMillis); 548 549 fields.centuryOfEra = new ImpreciseCutoverField( 550 julian.centuryOfEra(), fields.centuryOfEra, iCutoverMillis); 551 fields.centuries = fields.centuryOfEra.getDurationField(); 552 553 fields.monthOfYear = new ImpreciseCutoverField( 554 julian.monthOfYear(), fields.monthOfYear, iCutoverMillis); 555 fields.months = fields.monthOfYear.getDurationField(); 556 557 fields.weekyear = new ImpreciseCutoverField( 558 julian.weekyear(), fields.weekyear, null, iCutoverMillis, true); 559 fields.weekyearOfCentury = new ImpreciseCutoverField( 560 julian.weekyearOfCentury(), fields.weekyearOfCentury, fields.weekyears, iCutoverMillis); 561 fields.weekyears = fields.weekyear.getDurationField(); 562 } 563 564 // These fields require basic cutover support, except they must link to 565 // imprecise durations. 566 { 567 CutoverField cf = new CutoverField 568 (julian.dayOfMonth(), fields.dayOfMonth, iCutoverMillis); 569 cf.iRangeDurationField = fields.months; 570 fields.dayOfMonth = cf; 571 } 572 } 573 574 long julianToGregorianByYear(long instant) { 575 return convertByYear(instant, iJulianChronology, iGregorianChronology); 576 } 577 578 long gregorianToJulianByYear(long instant) { 579 return convertByYear(instant, iGregorianChronology, iJulianChronology); 580 } 581 582 long julianToGregorianByWeekyear(long instant) { 583 return convertByWeekyear(instant, iJulianChronology, iGregorianChronology); 584 } 585 586 long gregorianToJulianByWeekyear(long instant) { 587 return convertByWeekyear(instant, iGregorianChronology, iJulianChronology); 588 } 589 590 //----------------------------------------------------------------------- 591 /** 592 * This basic cutover field adjusts calls to 'get' and 'set' methods, and 593 * assumes that calls to add and addWrapField are unaffected by the cutover. 594 */ 595 private class CutoverField extends BaseDateTimeField { 596 private static final long serialVersionUID = 3528501219481026402L; 597 598 final DateTimeField iJulianField; 599 final DateTimeField iGregorianField; 600 final long iCutover; 601 final boolean iConvertByWeekyear; 602 603 protected DurationField iDurationField; 604 protected DurationField iRangeDurationField; 605 606 /** 607 * @param julianField field from the chronology used before the cutover instant 608 * @param gregorianField field from the chronology used at and after the cutover 609 * @param cutoverMillis the millis of the cutover 610 */ 611 CutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) { 612 this(julianField, gregorianField, cutoverMillis, false); 613 } 614 615 /** 616 * @param julianField field from the chronology used before the cutover instant 617 * @param gregorianField field from the chronology used at and after the cutover 618 * @param cutoverMillis the millis of the cutover 619 * @param convertByWeekyear 620 */ 621 CutoverField(DateTimeField julianField, DateTimeField gregorianField, 622 long cutoverMillis, boolean convertByWeekyear) { 623 super(gregorianField.getType()); 624 iJulianField = julianField; 625 iGregorianField = gregorianField; 626 iCutover = cutoverMillis; 627 iConvertByWeekyear = convertByWeekyear; 628 // Although average length of Julian and Gregorian years differ, 629 // use the Gregorian duration field because it is more accurate. 630 iDurationField = gregorianField.getDurationField(); 631 632 DurationField rangeField = gregorianField.getRangeDurationField(); 633 if (rangeField == null) { 634 rangeField = julianField.getRangeDurationField(); 635 } 636 iRangeDurationField = rangeField; 637 } 638 639 public boolean isLenient() { 640 return false; 641 } 642 643 public int get(long instant) { 644 if (instant >= iCutover) { 645 return iGregorianField.get(instant); 646 } else { 647 return iJulianField.get(instant); 648 } 649 } 650 651 public String getAsText(long instant, Locale locale) { 652 if (instant >= iCutover) { 653 return iGregorianField.getAsText(instant, locale); 654 } else { 655 return iJulianField.getAsText(instant, locale); 656 } 657 } 658 659 public String getAsText(int fieldValue, Locale locale) { 660 return iGregorianField.getAsText(fieldValue, locale); 661 } 662 663 public String getAsShortText(long instant, Locale locale) { 664 if (instant >= iCutover) { 665 return iGregorianField.getAsShortText(instant, locale); 666 } else { 667 return iJulianField.getAsShortText(instant, locale); 668 } 669 } 670 671 public String getAsShortText(int fieldValue, Locale locale) { 672 return iGregorianField.getAsShortText(fieldValue, locale); 673 } 674 675 public long add(long instant, int value) { 676 return iGregorianField.add(instant, value); 677 } 678 679 public long add(long instant, long value) { 680 return iGregorianField.add(instant, value); 681 } 682 683 public int[] add(ReadablePartial partial, int fieldIndex, int[] values, int valueToAdd) { 684 // overridden as superclass algorithm can't handle 685 // 2004-02-29 + 48 months -> 2008-02-29 type dates 686 if (valueToAdd == 0) { 687 return values; 688 } 689 if (DateTimeUtils.isContiguous(partial)) { 690 long instant = 0L; 691 for (int i = 0, isize = partial.size(); i < isize; i++) { 692 instant = partial.getFieldType(i).getField(GJChronology.this).set(instant, values[i]); 693 } 694 instant = add(instant, valueToAdd); 695 return GJChronology.this.get(partial, instant); 696 } else { 697 return super.add(partial, fieldIndex, values, valueToAdd); 698 } 699 } 700 701 public int getDifference(long minuendInstant, long subtrahendInstant) { 702 return iGregorianField.getDifference(minuendInstant, subtrahendInstant); 703 } 704 705 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) { 706 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 707 } 708 709 public long set(long instant, int value) { 710 if (instant >= iCutover) { 711 instant = iGregorianField.set(instant, value); 712 if (instant < iCutover) { 713 // Only adjust if gap fully crossed. 714 if (instant + iGapDuration < iCutover) { 715 instant = gregorianToJulian(instant); 716 } 717 // Verify that new value stuck. 718 if (get(instant) != value) { 719 throw new IllegalFieldValueException 720 (iGregorianField.getType(), Integer.valueOf(value), null, null); 721 } 722 } 723 } else { 724 instant = iJulianField.set(instant, value); 725 if (instant >= iCutover) { 726 // Only adjust if gap fully crossed. 727 if (instant - iGapDuration >= iCutover) { 728 instant = julianToGregorian(instant); 729 } 730 // Verify that new value stuck. 731 if (get(instant) != value) { 732 throw new IllegalFieldValueException 733 (iJulianField.getType(), Integer.valueOf(value), null, null); 734 } 735 } 736 } 737 return instant; 738 } 739 740 public long set(long instant, String text, Locale locale) { 741 if (instant >= iCutover) { 742 instant = iGregorianField.set(instant, text, locale); 743 if (instant < iCutover) { 744 // Only adjust if gap fully crossed. 745 if (instant + iGapDuration < iCutover) { 746 instant = gregorianToJulian(instant); 747 } 748 // Cannot verify that new value stuck because set may be lenient. 749 } 750 } else { 751 instant = iJulianField.set(instant, text, locale); 752 if (instant >= iCutover) { 753 // Only adjust if gap fully crossed. 754 if (instant - iGapDuration >= iCutover) { 755 instant = julianToGregorian(instant); 756 } 757 // Cannot verify that new value stuck because set may be lenient. 758 } 759 } 760 return instant; 761 } 762 763 public DurationField getDurationField() { 764 return iDurationField; 765 } 766 767 public DurationField getRangeDurationField() { 768 return iRangeDurationField; 769 } 770 771 public boolean isLeap(long instant) { 772 if (instant >= iCutover) { 773 return iGregorianField.isLeap(instant); 774 } else { 775 return iJulianField.isLeap(instant); 776 } 777 } 778 779 public int getLeapAmount(long instant) { 780 if (instant >= iCutover) { 781 return iGregorianField.getLeapAmount(instant); 782 } else { 783 return iJulianField.getLeapAmount(instant); 784 } 785 } 786 787 public DurationField getLeapDurationField() { 788 return iGregorianField.getLeapDurationField(); 789 } 790 791 792 public int getMinimumValue() { 793 // For all precise fields, the Julian and Gregorian limits are 794 // identical. Choose Julian to tighten up the year limits. 795 return iJulianField.getMinimumValue(); 796 } 797 798 public int getMinimumValue(ReadablePartial partial) { 799 return iJulianField.getMinimumValue(partial); 800 } 801 802 public int getMinimumValue(ReadablePartial partial, int[] values) { 803 return iJulianField.getMinimumValue(partial, values); 804 } 805 806 public int getMinimumValue(long instant) { 807 if (instant < iCutover) { 808 return iJulianField.getMinimumValue(instant); 809 } 810 811 int min = iGregorianField.getMinimumValue(instant); 812 813 // Because the cutover may reduce the length of this field, verify 814 // the minimum by setting it. 815 instant = iGregorianField.set(instant, min); 816 if (instant < iCutover) { 817 min = iGregorianField.get(iCutover); 818 } 819 820 return min; 821 } 822 823 public int getMaximumValue() { 824 // For all precise fields, the Julian and Gregorian limits are 825 // identical. 826 return iGregorianField.getMaximumValue(); 827 } 828 829 public int getMaximumValue(long instant) { 830 if (instant >= iCutover) { 831 return iGregorianField.getMaximumValue(instant); 832 } 833 834 int max = iJulianField.getMaximumValue(instant); 835 836 // Because the cutover may reduce the length of this field, verify 837 // the maximum by setting it. 838 instant = iJulianField.set(instant, max); 839 if (instant >= iCutover) { 840 max = iJulianField.get(iJulianField.add(iCutover, -1)); 841 } 842 843 return max; 844 } 845 846 public int getMaximumValue(ReadablePartial partial) { 847 long instant = GJChronology.getInstanceUTC().set(partial, 0L); 848 return getMaximumValue(instant); 849 } 850 851 public int getMaximumValue(ReadablePartial partial, int[] values) { 852 Chronology chrono = GJChronology.getInstanceUTC(); 853 long instant = 0L; 854 for (int i = 0, isize = partial.size(); i < isize; i++) { 855 DateTimeField field = partial.getFieldType(i).getField(chrono); 856 if (values[i] <= field.getMaximumValue(instant)) { 857 instant = field.set(instant, values[i]); 858 } 859 } 860 return getMaximumValue(instant); 861 } 862 863 public long roundFloor(long instant) { 864 if (instant >= iCutover) { 865 instant = iGregorianField.roundFloor(instant); 866 if (instant < iCutover) { 867 // Only adjust if gap fully crossed. 868 if (instant + iGapDuration < iCutover) { 869 instant = gregorianToJulian(instant); 870 } 871 } 872 } else { 873 instant = iJulianField.roundFloor(instant); 874 } 875 return instant; 876 } 877 878 public long roundCeiling(long instant) { 879 if (instant >= iCutover) { 880 instant = iGregorianField.roundCeiling(instant); 881 } else { 882 instant = iJulianField.roundCeiling(instant); 883 if (instant >= iCutover) { 884 // Only adjust if gap fully crossed. 885 if (instant - iGapDuration >= iCutover) { 886 instant = julianToGregorian(instant); 887 } 888 } 889 } 890 return instant; 891 } 892 893 public int getMaximumTextLength(Locale locale) { 894 return Math.max(iJulianField.getMaximumTextLength(locale), 895 iGregorianField.getMaximumTextLength(locale)); 896 } 897 898 public int getMaximumShortTextLength(Locale locale) { 899 return Math.max(iJulianField.getMaximumShortTextLength(locale), 900 iGregorianField.getMaximumShortTextLength(locale)); 901 } 902 903 protected long julianToGregorian(long instant) { 904 if (iConvertByWeekyear) { 905 return julianToGregorianByWeekyear(instant); 906 } else { 907 return julianToGregorianByYear(instant); 908 } 909 } 910 911 protected long gregorianToJulian(long instant) { 912 if (iConvertByWeekyear) { 913 return gregorianToJulianByWeekyear(instant); 914 } else { 915 return gregorianToJulianByYear(instant); 916 } 917 } 918 } 919 920 //----------------------------------------------------------------------- 921 /** 922 * Cutover field for variable length fields. These fields internally call 923 * set whenever add is called. As a result, the same correction applied to 924 * set must be applied to add and addWrapField. Knowing when to use this 925 * field requires specific knowledge of how the GJ fields are implemented. 926 */ 927 private final class ImpreciseCutoverField extends CutoverField { 928 private static final long serialVersionUID = 3410248757173576441L; 929 930 /** 931 * Creates a duration field that links back to this. 932 */ 933 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, long cutoverMillis) { 934 this(julianField, gregorianField, null, cutoverMillis, false); 935 } 936 937 /** 938 * Uses a shared duration field rather than creating a new one. 939 * 940 * @param durationField shared duration field 941 */ 942 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, 943 DurationField durationField, long cutoverMillis) 944 { 945 this(julianField, gregorianField, durationField, cutoverMillis, false); 946 } 947 948 /** 949 * Uses a shared duration field rather than creating a new one. 950 * 951 * @param durationField shared duration field 952 */ 953 ImpreciseCutoverField(DateTimeField julianField, DateTimeField gregorianField, 954 DurationField durationField, 955 long cutoverMillis, boolean convertByWeekyear) 956 { 957 super(julianField, gregorianField, cutoverMillis, convertByWeekyear); 958 if (durationField == null) { 959 durationField = new LinkedDurationField(iDurationField, this); 960 } 961 iDurationField = durationField; 962 } 963 964 public long add(long instant, int value) { 965 if (instant >= iCutover) { 966 instant = iGregorianField.add(instant, value); 967 if (instant < iCutover) { 968 // Only adjust if gap fully crossed. 969 if (instant + iGapDuration < iCutover) { 970 instant = gregorianToJulian(instant); 971 } 972 } 973 } else { 974 instant = iJulianField.add(instant, value); 975 if (instant >= iCutover) { 976 // Only adjust if gap fully crossed. 977 if (instant - iGapDuration >= iCutover) { 978 instant = julianToGregorian(instant); 979 } 980 } 981 } 982 return instant; 983 } 984 985 public long add(long instant, long value) { 986 if (instant >= iCutover) { 987 instant = iGregorianField.add(instant, value); 988 if (instant < iCutover) { 989 // Only adjust if gap fully crossed. 990 if (instant + iGapDuration < iCutover) { 991 instant = gregorianToJulian(instant); 992 } 993 } 994 } else { 995 instant = iJulianField.add(instant, value); 996 if (instant >= iCutover) { 997 // Only adjust if gap fully crossed. 998 if (instant - iGapDuration >= iCutover) { 999 instant = julianToGregorian(instant); 1000 } 1001 } 1002 } 1003 return instant; 1004 } 1005 1006 public int getDifference(long minuendInstant, long subtrahendInstant) { 1007 if (minuendInstant >= iCutover) { 1008 if (subtrahendInstant >= iCutover) { 1009 return iGregorianField.getDifference(minuendInstant, subtrahendInstant); 1010 } 1011 // Remember, the add is being reversed. Since subtrahend is 1012 // Julian, convert minuend to Julian to match. 1013 minuendInstant = gregorianToJulian(minuendInstant); 1014 return iJulianField.getDifference(minuendInstant, subtrahendInstant); 1015 } else { 1016 if (subtrahendInstant < iCutover) { 1017 return iJulianField.getDifference(minuendInstant, subtrahendInstant); 1018 } 1019 // Remember, the add is being reversed. Since subtrahend is 1020 // Gregorian, convert minuend to Gregorian to match. 1021 minuendInstant = julianToGregorian(minuendInstant); 1022 return iGregorianField.getDifference(minuendInstant, subtrahendInstant); 1023 } 1024 } 1025 1026 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) { 1027 if (minuendInstant >= iCutover) { 1028 if (subtrahendInstant >= iCutover) { 1029 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 1030 } 1031 // Remember, the add is being reversed. Since subtrahend is 1032 // Julian, convert minuend to Julian to match. 1033 minuendInstant = gregorianToJulian(minuendInstant); 1034 return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 1035 } else { 1036 if (subtrahendInstant < iCutover) { 1037 return iJulianField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 1038 } 1039 // Remember, the add is being reversed. Since subtrahend is 1040 // Gregorian, convert minuend to Gregorian to match. 1041 minuendInstant = julianToGregorian(minuendInstant); 1042 return iGregorianField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 1043 } 1044 } 1045 1046 // Since the imprecise fields have durations longer than the gap 1047 // duration, keep these methods simple. The inherited implementations 1048 // produce incorrect results. 1049 // 1050 // Degenerate case: If this field is a month, and the cutover is set 1051 // far into the future, then the gap duration may be so large as to 1052 // reduce the number of months in a year. If the missing month(s) are 1053 // at the beginning or end of the year, then the minimum and maximum 1054 // values are not 1 and 12. I don't expect this case to ever occur. 1055 1056 public int getMinimumValue(long instant) { 1057 if (instant >= iCutover) { 1058 return iGregorianField.getMinimumValue(instant); 1059 } else { 1060 return iJulianField.getMinimumValue(instant); 1061 } 1062 } 1063 1064 public int getMaximumValue(long instant) { 1065 if (instant >= iCutover) { 1066 return iGregorianField.getMaximumValue(instant); 1067 } else { 1068 return iJulianField.getMaximumValue(instant); 1069 } 1070 } 1071 } 1072 1073 //----------------------------------------------------------------------- 1074 /** 1075 * Links the duration back to a ImpreciseCutoverField. 1076 */ 1077 private static class LinkedDurationField extends DecoratedDurationField { 1078 private static final long serialVersionUID = 4097975388007713084L; 1079 1080 private final ImpreciseCutoverField iField; 1081 1082 LinkedDurationField(DurationField durationField, ImpreciseCutoverField dateTimeField) { 1083 super(durationField, durationField.getType()); 1084 iField = dateTimeField; 1085 } 1086 1087 public long add(long instant, int value) { 1088 return iField.add(instant, value); 1089 } 1090 1091 public long add(long instant, long value) { 1092 return iField.add(instant, value); 1093 } 1094 1095 public int getDifference(long minuendInstant, long subtrahendInstant) { 1096 return iField.getDifference(minuendInstant, subtrahendInstant); 1097 } 1098 1099 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) { 1100 return iField.getDifferenceAsLong(minuendInstant, subtrahendInstant); 1101 } 1102 } 1103 1104 }