/** * Converts time field values to UTC as milliseconds. * * @exception IllegalArgumentException if any fields are invalid. */ protected void computeTime() { correctTime(); // This function takes advantage of the fact that unset fields in // the time field list have a value of zero. // First, use the year to determine whether to use the Gregorian or the // Julian calendar. If the year is not the year of the cutover, this // computation will be correct. But if the year is the cutover year, // this may be incorrect. In that case, assume the Gregorian calendar, // make the computation, and then recompute if the resultant millis // indicate the wrong calendar has been assumed. // A date such as Oct. 10, 1582 does not exist in a Gregorian calendar // with the default changeover of Oct. 15, 1582, since in such a // calendar Oct. 4 (Julian) is followed by Oct. 15 (Gregorian). This // algorithm will interpret such a date using the Julian calendar, // yielding Oct. 20, 1582 (Gregorian). int year = this.fields[YEAR]; boolean isGregorian = year >= gregorianCutoverYear; long julianDay = calculateJulianDay(isGregorian, year); // if DAY_OF_WEEK was set more recently than DAY_OF_MONTH and is correct // then time is computed using current week and day of week if (isSet[DAY_OF_WEEK] && fields[DAY_OF_WEEK] >= SUNDAY && fields[DAY_OF_WEEK] <= SATURDAY) { julianDay += fields[DAY_OF_WEEK] - julianDayToDayOfWeek(julianDay); fields[DATE] += fields[DAY_OF_WEEK] - julianDayToDayOfWeek(julianDay); } long millis = julianDayToMillis(julianDay); // The following check handles portions of the cutover year BEFORE the // cutover itself happens. The check for the julianDate number is for a // rare case; it's a hardcoded number, but it's efficient. The given // Julian day number corresponds to Dec 3, 292269055 BC, which // corresponds to millis near Long.MIN_VALUE. The need for the check // arises because for extremely negative Julian day numbers, the millis // actually overflow to be positive values. Without the check, the // initial date is interpreted with the Gregorian calendar, even when // the cutover doesn't warrant it. if (isGregorian != (millis >= gregorianCutover) && julianDay != -106749550580L) { // See above julianDay = calculateJulianDay(!isGregorian, year); millis = julianDayToMillis(julianDay); } // Do the time portion of the conversion. int millisInDay = 0; // Hours // Don't normalize here; let overflow bump into the next period. // This is consistent with how we handle other fields. millisInDay += this.fields[HOUR_OF_DAY]; millisInDay *= 60; // now get minutes millisInDay += this.fields[MINUTE]; millisInDay *= 60; // now get seconds millisInDay += this.fields[SECOND]; millisInDay *= 1000; // now get millis millisInDay += this.fields[MILLISECOND]; // Compute the time zone offset and DST offset. There are two potential // ambiguities here. We'll assume a 2:00 am (wall time) switchover time // for discussion purposes here. // 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am // can be in standard or in DST depending. However, 2:00 am is an invalid // representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST). // We assume standard time. // 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am // can be in standard or DST. Both are valid representations (the rep // jumps from 1:59:59 DST to 1:00:00 Std). // Again, we assume standard time. // We use the TimeZone object to get the zone offset int zoneOffset = getTimeZone().getRawOffset(); // Now add date and millisInDay together, to make millis contain local wall // millis, with no zone or DST adjustments millis += millisInDay; // Normalize the millisInDay to 0..ONE_DAY-1. If the millis is out // of range, then we must call timeToFields() to recompute our // fields. int[] normalizedMillisInDay = new int[1]; floorDivide(millis, (int) ONE_DAY, normalizedMillisInDay); // We need to have the month, the day, and the day of the week. // Calling timeToFields will compute the MONTH and DATE fields. // // It's tempting to try to use DAY_OF_WEEK here, if it // is set, but we CAN'T. Even if it's set, it might have // been set wrong by the user. We should rely only on // the Julian day number, which has been computed correctly // using the disambiguation algorithm above. [LIU] int dow = julianDayToDayOfWeek(julianDay); // It's tempting to try to use DAY_OF_WEEK here, if it // is set, but we CAN'T. Even if it's set, it might have // been set wrong by the user. We should rely only on // the Julian day number, which has been computed correctly // using the disambiguation algorithm above. [LIU] int dstOffset = getTimeZone() .getOffset( AD, this.fields[YEAR], this.fields[MONTH], this.fields[DATE], dow, normalizedMillisInDay[0]) - zoneOffset; // Note: Because we pass in wall millisInDay, rather than // standard millisInDay, we interpret "1:00 am" on the day // of cessation of DST as "1:00 am Std" (assuming the time // of cessation is 2:00 am). // Store our final computed GMT time, with timezone adjustments. time = millis - zoneOffset - dstOffset; }