diff options
Diffstat (limited to 'luni/src/main/java/java/util/GregorianCalendar.java')
-rw-r--r-- | luni/src/main/java/java/util/GregorianCalendar.java | 60 |
1 files changed, 47 insertions, 13 deletions
diff --git a/luni/src/main/java/java/util/GregorianCalendar.java b/luni/src/main/java/java/util/GregorianCalendar.java index 9ff9ccc..be96684 100644 --- a/luni/src/main/java/java/util/GregorianCalendar.java +++ b/luni/src/main/java/java/util/GregorianCalendar.java @@ -331,7 +331,16 @@ public class GregorianCalendar extends Calendar { setTimeInMillis(System.currentTimeMillis()); } - GregorianCalendar(boolean ignored) { + /** + * A minimum-cost constructor that does not initialize the current time or perform any date + * calculations. For use internally when the time will be set later. Other constructors, such as + * {@link GregorianCalendar#GregorianCalendar()}, set the time to the current system clock + * and recalculate the fields incurring unnecessary cost when the time or fields will be set + * later. + * + * @hide used internally + */ + public GregorianCalendar(boolean ignored) { super(TimeZone.getDefault()); setFirstDayOfWeek(SUNDAY); setMinimalDaysInFirstWeek(1); @@ -458,16 +467,17 @@ public class GregorianCalendar extends Calendar { complete(); } - private void fullFieldsCalc(long timeVal, int zoneOffset) { + private void fullFieldsCalc() { int millis = (int) (time % 86400000); - long days = timeVal / 86400000; + long days = time / 86400000; if (millis < 0) { millis += 86400000; days--; } - // Cannot add ZONE_OFFSET to time as it might overflow - millis += zoneOffset; + // Adding fields[ZONE_OFFSET] to time might make it overflow, so we add + // it to millis (the number of milliseconds in the current day) instead. + millis += fields[ZONE_OFFSET]; while (millis < 0) { millis += 86400000; days--; @@ -477,9 +487,9 @@ public class GregorianCalendar extends Calendar { days++; } - int dayOfYear = computeYearAndDay(days, timeVal + zoneOffset); + int dayOfYear = computeYearAndDay(days, time + fields[ZONE_OFFSET]); fields[DAY_OF_YEAR] = dayOfYear; - if(fields[YEAR] == changeYear && gregorianCutover <= timeVal + zoneOffset){ + if (fields[YEAR] == changeYear && gregorianCutover <= time + fields[ZONE_OFFSET]){ dayOfYear += currentYearSkew; } int month = dayOfYear / 32; @@ -493,7 +503,7 @@ public class GregorianCalendar extends Calendar { int dstOffset = fields[YEAR] <= 0 ? 0 : getTimeZone().getOffset(AD, fields[YEAR], month, date, fields[DAY_OF_WEEK], millis); if (fields[YEAR] > 0) { - dstOffset -= zoneOffset; + dstOffset -= fields[ZONE_OFFSET]; } fields[DST_OFFSET] = dstOffset; if (dstOffset != 0) { @@ -507,10 +517,10 @@ public class GregorianCalendar extends Calendar { days++; } if (oldDays != days) { - dayOfYear = computeYearAndDay(days, timeVal - zoneOffset + dayOfYear = computeYearAndDay(days, time - fields[ZONE_OFFSET] + dstOffset); fields[DAY_OF_YEAR] = dayOfYear; - if(fields[YEAR] == changeYear && gregorianCutover <= timeVal - zoneOffset + dstOffset){ + if(fields[YEAR] == changeYear && gregorianCutover <= time - fields[ZONE_OFFSET] + dstOffset){ dayOfYear += currentYearSkew; } month = dayOfYear / 32; @@ -567,10 +577,26 @@ public class GregorianCalendar extends Calendar { TimeZone timeZone = getTimeZone(); int dstOffset = timeZone.inDaylightTime(new Date(time)) ? timeZone.getDSTSavings() : 0; int zoneOffset = timeZone.getRawOffset(); + + // We unconditionally overwrite DST_OFFSET and ZONE_OFFSET with + // values from the timezone that's currently in use. This gives us + // much more consistent behavior, and matches ICU4J behavior (though + // it is inconsistent with the RI). + // + // Anything callers can do with ZONE_OFFSET they can do by constructing + // a SimpleTimeZone with the required offset. + // + // DST_OFFSET is a bit of a WTF, given that it's dependent on the rest + // of the fields. There's no sensible reason we'd want to allow it to + // be set, nor can we implement consistent full-fields calculation after + // this field is set without maintaining a large deal of additional state. + // + // At the very least, we will need isSet to differentiate between fields + // set by the user and fields set by our internal field calculation. fields[DST_OFFSET] = dstOffset; fields[ZONE_OFFSET] = zoneOffset; - fullFieldsCalc(time, zoneOffset); + fullFieldsCalc(); for (int i = 0; i < FIELD_COUNT; i++) { isSet[i] = true; @@ -670,8 +696,16 @@ public class GregorianCalendar extends Calendar { if (useMonth && (lastDateFieldSet == DAY_OF_WEEK || lastDateFieldSet == WEEK_OF_YEAR)) { if (isSet[WEEK_OF_YEAR] && isSet[DAY_OF_WEEK]) { - useMonth = lastDateFieldSet != WEEK_OF_YEAR && weekMonthSet - && isSet[DAY_OF_WEEK]; + if (lastDateFieldSet == WEEK_OF_YEAR) { + useMonth = false; + } else if (lastDateFieldSet == DAY_OF_WEEK) { + // DAY_OF_WEEK belongs to both the Month + Week + Day and the + // WeekOfYear + Day combinations. We're supposed to use the most + // recent combination, as specified by the single set field. We can't + // know for sure in this case, so we always prefer the week-month-day + // combination if week-month is already set. + useMonth = weekMonthSet; + } } else if (isSet[DAY_OF_YEAR]) { useMonth = isSet[DATE] && isSet[MONTH]; } |