+
+
+/**
+ * Ensure that each field is within its valid range by calling {@link
+ * #validateField(int)} on each field that has been set. This method
+ * should only be called if this calendar is not lenient.
+ * @see #isLenient
+ * @see #validateField(int)
+ * @draft ICU 2.8
+ */
+void Calendar::validateFields(UErrorCode &status) {
+ for (int32_t field = 0; U_SUCCESS(status) && (field < UCAL_FIELD_COUNT); field++) {
+ if (isSet((UCalendarDateFields)field)) {
+ validateField((UCalendarDateFields)field, status);
+ }
+ }
+}
+
+/**
+ * Validate a single field of this calendar. Subclasses should
+ * override this method to validate any calendar-specific fields.
+ * Generic fields can be handled by
+ * <code>Calendar.validateField()</code>.
+ * @see #validateField(int, int, int)
+ * @draft ICU 2.8
+ */
+void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) {
+ int32_t y;
+ switch (field) {
+ case UCAL_DAY_OF_MONTH:
+ y = handleGetExtendedYear();
+ validateField(field, 1, handleGetMonthLength(y, internalGet(UCAL_MONTH)), status);
+ break;
+ case UCAL_DAY_OF_YEAR:
+ y = handleGetExtendedYear();
+ validateField(field, 1, handleGetYearLength(y), status);
+ break;
+ case UCAL_DAY_OF_WEEK_IN_MONTH:
+ if (internalGet(field) == 0) {
+#if defined (U_DEBUG_CAL)
+ fprintf(stderr, "%s:%d: ILLEGAL ARG because DOW in month cannot be 0\n",
+ __FILE__, __LINE__);
+#endif
+ status = U_ILLEGAL_ARGUMENT_ERROR; // "DAY_OF_WEEK_IN_MONTH cannot be zero"
+ return;
+ }
+ validateField(field, getMinimum(field), getMaximum(field), status);
+ break;
+ default:
+ validateField(field, getMinimum(field), getMaximum(field), status);
+ break;
+ }
+}
+
+/**
+ * Validate a single field of this calendar given its minimum and
+ * maximum allowed value. If the field is out of range, throw a
+ * descriptive <code>IllegalArgumentException</code>. Subclasses may
+ * use this method in their implementation of {@link
+ * #validateField(int)}.
+ * @draft ICU 2.8
+ */
+void Calendar::validateField(UCalendarDateFields field, int32_t min, int32_t max, UErrorCode& status)
+{
+ int32_t value = fFields[field];
+ if (value < min || value > max) {
+#if defined (U_DEBUG_CAL)
+ fprintf(stderr, "%s:%d: ILLEGAL ARG because of field %s out of range %d..%d at %d\n",
+ __FILE__, __LINE__,fldName(field),min,max,value);
+#endif
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+}
+
+// -------------------------
+
+const UFieldResolutionTable* Calendar::getFieldResolutionTable() const {
+ return kDatePrecedence;
+}
+
+
+UCalendarDateFields Calendar::newerField(UCalendarDateFields defaultField, UCalendarDateFields alternateField) const
+{
+ if (fStamp[alternateField] > fStamp[defaultField]) {
+ return alternateField;
+ }
+ return defaultField;
+}
+
+UCalendarDateFields Calendar::resolveFields(const UFieldResolutionTable* precedenceTable) {
+ int32_t bestField = UCAL_FIELD_COUNT;
+ for (int32_t g=0; precedenceTable[g][0][0] != -1 && (bestField == UCAL_FIELD_COUNT); ++g) {
+ int32_t bestStamp = kUnset;
+ for (int32_t l=0; precedenceTable[g][l][0] != -1; ++l) {
+ int32_t lineStamp = kUnset;
+ // Skip over first entry if it is negative
+ for (int32_t i=((precedenceTable[g][l][0]>=kResolveRemap)?1:0); precedenceTable[g][l][i]!=-1; ++i) {
+ int32_t s = fStamp[precedenceTable[g][l][i]];
+ // If any field is unset then don't use this line
+ if (s == kUnset) {
+ goto linesInGroup;
+ } else if(s > lineStamp) {
+ lineStamp = s;
+ }
+ }
+ // Record new maximum stamp & field no.
+ if (lineStamp > bestStamp) {
+ bestStamp = lineStamp;
+ bestField = precedenceTable[g][l][0]; // First field refers to entire line
+ }
+ linesInGroup:
+ ;
+ }
+ }
+ return (UCalendarDateFields)( (bestField>=kResolveRemap)?(bestField&(kResolveRemap-1)):bestField );
+}
+
+const UFieldResolutionTable Calendar::kDatePrecedence[] =
+ {
+ {
+ { UCAL_DAY_OF_MONTH, kResolveSTOP },
+ { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP },
+ { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
+ { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
+ { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP },
+ { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
+ { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
+ { UCAL_DAY_OF_YEAR, kResolveSTOP },
+ { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_YEAR, kResolveSTOP }, // if YEAR is set over YEAR_WOY use DAY_OF_MONTH
+ { kResolveRemap | UCAL_WEEK_OF_YEAR, UCAL_YEAR_WOY, kResolveSTOP }, // if YEAR_WOY is set, calc based on WEEK_OF_YEAR
+ { kResolveSTOP }
+ },
+ {
+ { UCAL_WEEK_OF_YEAR, kResolveSTOP },
+ { UCAL_WEEK_OF_MONTH, kResolveSTOP },
+ { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP },
+ { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
+ { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
+ { kResolveSTOP }
+ },
+ {{kResolveSTOP}}
+ };
+
+
+const UFieldResolutionTable Calendar::kDOWPrecedence[] =
+{
+ {
+ { UCAL_DAY_OF_WEEK,kResolveSTOP, kResolveSTOP },
+ { UCAL_DOW_LOCAL,kResolveSTOP, kResolveSTOP },
+ {kResolveSTOP}
+ },
+ {{kResolveSTOP}}
+};
+
+// precedence for calculating a year
+const UFieldResolutionTable Calendar::kYearPrecedence[] =
+{
+ {
+ { UCAL_YEAR, kResolveSTOP },
+ { UCAL_EXTENDED_YEAR, kResolveSTOP },
+ { UCAL_YEAR_WOY, UCAL_WEEK_OF_YEAR, kResolveSTOP }, // YEAR_WOY is useless without WEEK_OF_YEAR
+ { kResolveSTOP }
+ },
+ {{kResolveSTOP}}
+};
+
+
+// -------------------------
+
+
+void Calendar::computeTime(UErrorCode& status) {
+ if (!isLenient()) {
+ validateFields(status);
+ }
+
+ // Compute the Julian day
+ int32_t julianDay = computeJulianDay();
+
+ double millis = Grego::julianDayToMillis(julianDay);
+
+#if defined (U_DEBUG_CAL)
+ // int32_t julianInsanityCheck = (int32_t)Math::floorDivide(millis, kOneDay);
+ // julianInsanityCheck += kEpochStartAsJulianDay;
+ // if(1 || julianInsanityCheck != julianDay) {
+ // fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n",
+ // __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck);
+ // }
+#endif
+
+ int32_t millisInDay;
+
+ // We only use MILLISECONDS_IN_DAY if it has been set by the user.
+ // This makes it possible for the caller to set the calendar to a
+ // time and call clear(MONTH) to reset the MONTH to January. This
+ // is legacy behavior. Without this, clear(MONTH) has no effect,
+ // since the internally set JULIAN_DAY is used.
+ if (fStamp[UCAL_MILLISECONDS_IN_DAY] >= ((int32_t)kMinimumUserStamp) &&
+ newestStamp(UCAL_AM_PM, UCAL_MILLISECOND, kUnset) <= fStamp[UCAL_MILLISECONDS_IN_DAY]) {
+ millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY);
+ } else {
+ millisInDay = computeMillisInDay();
+ }
+
+ // 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, unless the user has explicitly set the ZONE_OFFSET
+ // or DST_OFFSET fields; then we use those fields.
+ if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) ||
+ fStamp[UCAL_DST_OFFSET] >= ((int32_t)kMinimumUserStamp)) {
+ millisInDay -= internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET);
+ } else {
+ millisInDay -= computeZoneOffset(millis, millisInDay,status);
+ }
+
+ internalSetTime(millis + millisInDay);
+}
+
+/**
+ * Compute the milliseconds in the day from the fields. This is a
+ * value from 0 to 23:59:59.999 inclusive, unless fields are out of
+ * range, in which case it can be an arbitrary value. This value
+ * reflects local zone wall time.
+ * @stable ICU 2.0
+ */
+int32_t Calendar::computeMillisInDay() {
+ // Do the time portion of the conversion.
+
+ int32_t millisInDay = 0;
+
+ // Find the best set of fields specifying the time of day. There
+ // are only two possibilities here; the HOUR_OF_DAY or the
+ // AM_PM and the HOUR.
+ int32_t hourOfDayStamp = fStamp[UCAL_HOUR_OF_DAY];
+ int32_t hourStamp = (fStamp[UCAL_HOUR] > fStamp[UCAL_AM_PM])?fStamp[UCAL_HOUR]:fStamp[UCAL_AM_PM];
+ int32_t bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp;
+
+ // Hours
+ if (bestStamp != kUnset) {
+ if (bestStamp == hourOfDayStamp) {
+ // Don't normalize here; let overflow bump into the next period.
+ // This is consistent with how we handle other fields.
+ millisInDay += internalGet(UCAL_HOUR_OF_DAY);
+ } else {
+ // Don't normalize here; let overflow bump into the next period.
+ // This is consistent with how we handle other fields.
+ millisInDay += internalGet(UCAL_HOUR);
+ millisInDay += 12 * internalGet(UCAL_AM_PM); // Default works for unset AM_PM
+ }
+ }
+
+ // We use the fact that unset == 0; we start with millisInDay
+ // == HOUR_OF_DAY.
+ millisInDay *= 60;
+ millisInDay += internalGet(UCAL_MINUTE); // now have minutes
+ millisInDay *= 60;
+ millisInDay += internalGet(UCAL_SECOND); // now have seconds
+ millisInDay *= 1000;
+ millisInDay += internalGet(MILLISECOND); // now have millis
+
+ return millisInDay;
+}
+
+/**
+ * This method can assume EXTENDED_YEAR has been set.
+ * @param millis milliseconds of the date fields
+ * @param millisInDay milliseconds of the time fields; may be out
+ * or range.
+ * @stable ICU 2.0
+ */
+int32_t Calendar::computeZoneOffset(double millis, int32_t millisInDay, UErrorCode &ec) {
+ int32_t rawOffset, dstOffset;
+ getTimeZone().getOffset(millis+millisInDay, TRUE, rawOffset, dstOffset, ec);
+ return rawOffset + dstOffset;
+ // 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).
+}
+
+int32_t Calendar::computeJulianDay()
+{
+ // We want to see if any of the date fields is newer than the
+ // JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do
+ // the normal resolution. We only use JULIAN_DAY if it has been
+ // set by the user. This makes it possible for the caller to set
+ // the calendar to a time and call clear(MONTH) to reset the MONTH
+ // to January. This is legacy behavior. Without this,
+ // clear(MONTH) has no effect, since the internally set JULIAN_DAY
+ // is used.
+ if (fStamp[UCAL_JULIAN_DAY] >= (int32_t)kMinimumUserStamp) {
+ int32_t bestStamp = newestStamp(UCAL_ERA, UCAL_DAY_OF_WEEK_IN_MONTH, kUnset);
+ bestStamp = newestStamp(UCAL_YEAR_WOY, UCAL_EXTENDED_YEAR, bestStamp);
+ if (bestStamp <= fStamp[UCAL_JULIAN_DAY]) {
+ return internalGet(UCAL_JULIAN_DAY);
+ }
+ }
+
+ UCalendarDateFields bestField = resolveFields(getFieldResolutionTable());
+ if (bestField == UCAL_FIELD_COUNT) {
+ bestField = UCAL_DAY_OF_MONTH;
+ }
+
+ return handleComputeJulianDay(bestField);
+}
+
+// -------------------------------------------
+
+int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) {
+ UBool useMonth = (bestField == UCAL_DAY_OF_MONTH ||
+ bestField == UCAL_WEEK_OF_MONTH ||
+ bestField == UCAL_DAY_OF_WEEK_IN_MONTH);
+ int32_t year;
+
+ if (bestField == UCAL_WEEK_OF_YEAR) {
+ year = internalGet(UCAL_YEAR_WOY, handleGetExtendedYear());
+ internalSet(UCAL_EXTENDED_YEAR, year);
+ } else {
+ year = handleGetExtendedYear();
+ internalSet(UCAL_EXTENDED_YEAR, year);
+ }
+
+#if defined (U_DEBUG_CAL)
+ fprintf(stderr, "%s:%d - bf= %s - y=%d\n", __FILE__, __LINE__, fldName(bestField), year);
+#endif
+
+ // Get the Julian day of the day BEFORE the start of this year.
+ // If useMonth is true, get the day before the start of the month.
+
+ // give calendar subclass a chance to have a default 'first' month
+ int32_t month;
+
+ if(isSet(UCAL_MONTH)) {
+ month = internalGet(UCAL_MONTH);
+ } else {
+ month = getDefaultMonthInYear();
+ }
+
+ int32_t julianDay = handleComputeMonthStart(year, useMonth ? month : 0, useMonth);
+
+ if (bestField == UCAL_DAY_OF_MONTH) {
+
+ // give calendar subclass a chance to have a default 'first' dom
+ int32_t dayOfMonth;
+ if(isSet(UCAL_DAY_OF_MONTH)) {
+ dayOfMonth = internalGet(UCAL_DAY_OF_MONTH,1);
+ } else {
+ dayOfMonth = getDefaultDayInMonth(month);
+ }
+ return julianDay + dayOfMonth;
+ }
+
+ if (bestField == UCAL_DAY_OF_YEAR) {
+ return julianDay + internalGet(UCAL_DAY_OF_YEAR);
+ }
+
+ int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw
+
+ // At this point julianDay is the 0-based day BEFORE the first day of
+ // January 1, year 1 of the given calendar. If julianDay == 0, it
+ // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
+ // or Gregorian). (or it is before the month we are in, if useMonth is True)
+
+ // At this point we need to process the WEEK_OF_MONTH or
+ // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
+ // First, perform initial shared computations. These locate the
+ // first week of the period.
+
+ // Get the 0-based localized DOW of day one of the month or year.
+ // Valid range 0..6.
+ int32_t first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek;
+ if (first < 0) {
+ first += 7;
+ }
+
+ int32_t dowLocal = getLocalDOW();
+
+ // Find the first target DOW (dowLocal) in the month or year.
+ // Actually, it may be just before the first of the month or year.
+ // It will be an integer from -5..7.
+ int32_t date = 1 - first + dowLocal;
+
+ if (bestField == UCAL_DAY_OF_WEEK_IN_MONTH) {
+ // Adjust the target DOW to be in the month or year.
+ if (date < 1) {
+ date += 7;
+ }
+
+ // The only trickiness occurs if the day-of-week-in-month is
+ // negative.
+ int32_t dim = internalGet(UCAL_DAY_OF_WEEK_IN_MONTH, 1);
+ if (dim >= 0) {
+ date += 7*(dim - 1);
+
+ } else {
+ // Move date to the last of this day-of-week in this month,
+ // then back up as needed. If dim==-1, we don't back up at
+ // all. If dim==-2, we back up once, etc. Don't back up
+ // past the first of the given day-of-week in this month.
+ // Note that we handle -2, -3, etc. correctly, even though
+ // values < -1 are technically disallowed.
+ int32_t m = internalGet(UCAL_MONTH, UCAL_JANUARY);
+ int32_t monthLength = handleGetMonthLength(year, m);
+ date += ((monthLength - date) / 7 + dim + 1) * 7;
+ }
+ } else {
+#if defined (U_DEBUG_CAL)
+ fprintf(stderr, "%s:%d - bf= %s\n", __FILE__, __LINE__, fldName(bestField));
+#endif
+
+ if(bestField == UCAL_WEEK_OF_YEAR) { // ------------------------------------- WOY -------------
+ if(!isSet(UCAL_YEAR_WOY) || // YWOY not set at all or
+ ( (resolveFields(kYearPrecedence) != UCAL_YEAR_WOY) // YWOY doesn't have precedence
+ && (fStamp[UCAL_YEAR_WOY]!=kInternallySet) ) ) { // (excluding where all fields are internally set - then YWOY is used)
+ // need to be sure to stay in 'real' year.
+ int32_t woy = internalGet(bestField);
+
+ int32_t nextJulianDay = handleComputeMonthStart(year+1, 0, FALSE); // jd of day before jan 1
+ int32_t nextFirst = julianDayToDayOfWeek(nextJulianDay + 1) - firstDayOfWeek;
+
+ if (nextFirst < 0) { // 0..6 ldow of Jan 1
+ nextFirst += 7;
+ }
+
+ if(woy==1) { // FIRST WEEK ---------------------------------
+#if defined (U_DEBUG_CAL)
+ fprintf(stderr, "%s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d", __FILE__, __LINE__,
+ internalGet(bestField), resolveFields(kYearPrecedence), year+1,
+ nextJulianDay, nextFirst);
+
+ fprintf(stderr, " next: %d DFW, min=%d \n", (7-nextFirst), getMinimalDaysInFirstWeek() );
+#endif
+
+ // nextFirst is now the localized DOW of Jan 1 of y-woy+1
+ if((nextFirst > 0) && // Jan 1 starts on FDOW
+ (7-nextFirst) >= getMinimalDaysInFirstWeek()) { // or enough days in the week
+ // Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year
+#if defined (U_DEBUG_CAL)
+ fprintf(stderr, "%s:%d - was going to move JD from %d to %d [d%d]\n", __FILE__, __LINE__,
+ julianDay, nextJulianDay, (nextJulianDay-julianDay));
+#endif
+ julianDay = nextJulianDay;
+
+ // recalculate 'first' [0-based local dow of jan 1]
+ first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek;
+ if (first < 0) {
+ first += 7;
+ }
+ // recalculate date.
+ date = 1 - first + dowLocal;
+ }
+ } else if(woy>=getLeastMaximum(bestField)) {
+ // could be in the last week- find out if this JD would overstep
+ int32_t testDate = date;
+ if ((7 - first) < getMinimalDaysInFirstWeek()) {
+ testDate += 7;
+ }
+
+ // Now adjust for the week number.
+ testDate += 7 * (woy - 1);
+
+#if defined (U_DEBUG_CAL)
+ fprintf(stderr, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n",
+ __FILE__, __LINE__, year, year-1, testDate, julianDay+testDate, nextJulianDay);
+#endif
+ if(julianDay+testDate > nextJulianDay) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1)
+ // Fire up the calculating engines.. retry YWOY = (year-1)
+ julianDay = handleComputeMonthStart(year-1, 0, FALSE); // jd before Jan 1 of previous year
+ first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; // 0 based local dow of first week
+
+ if(first < 0) { // 0..6
+ first += 7;
+ }
+ date = 1 - first + dowLocal;
+
+#if defined (U_DEBUG_CAL)
+ fprintf(stderr, "%s:%d - date now %d, jd%d, ywoy%d\n",
+ __FILE__, __LINE__, date, julianDay, year-1);
+#endif
+
+
+ } /* correction needed */
+ } /* leastmaximum */
+ } /* resolvefields(year) != year_woy */
+ } /* bestfield != week_of_year */
+
+ // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR)
+ // Adjust for minimal days in first week
+ if ((7 - first) < getMinimalDaysInFirstWeek()) {
+ date += 7;
+ }
+
+ // Now adjust for the week number.
+ date += 7 * (internalGet(bestField) - 1);
+ }
+
+ return julianDay + date;
+}
+
+int32_t
+Calendar::getDefaultMonthInYear()
+{
+ return 0;
+}
+
+int32_t
+Calendar::getDefaultDayInMonth(int32_t /*month*/)
+{
+ return 1;
+}
+
+
+int32_t Calendar::getLocalDOW()
+{
+ // Get zero-based localized DOW, valid range 0..6. This is the DOW
+ // we are looking for.
+ int32_t dowLocal = 0;
+ switch (resolveFields(kDOWPrecedence)) {
+ case DAY_OF_WEEK:
+ dowLocal = internalGet(UCAL_DAY_OF_WEEK) - fFirstDayOfWeek;
+ break;
+ case DOW_LOCAL:
+ dowLocal = internalGet(UCAL_DOW_LOCAL) - 1;
+ break;
+ default:
+ break;
+ }
+ dowLocal = dowLocal % 7;
+ if (dowLocal < 0) {
+ dowLocal += 7;
+ }
+ return dowLocal;
+}
+
+int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy)
+{
+ // We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine
+ // what year we fall in, so that other code can set it properly.
+ // (code borrowed from computeWeekFields and handleComputeJulianDay)
+ //return yearWoy;
+
+ // First, we need a reliable DOW.
+ UCalendarDateFields bestField = resolveFields(kDatePrecedence); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields
+
+ // Now, a local DOW
+ int32_t dowLocal = getLocalDOW(); // 0..6
+ int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw
+ int32_t jan1Start = handleComputeMonthStart(yearWoy, 0, FALSE);
+ int32_t nextJan1Start = handleComputeMonthStart(yearWoy+1, 0, FALSE); // next year's Jan1 start
+
+ // At this point julianDay is the 0-based day BEFORE the first day of
+ // January 1, year 1 of the given calendar. If julianDay == 0, it
+ // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
+ // or Gregorian). (or it is before the month we are in, if useMonth is True)
+
+ // At this point we need to process the WEEK_OF_MONTH or
+ // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
+ // First, perform initial shared computations. These locate the
+ // first week of the period.
+
+ // Get the 0-based localized DOW of day one of the month or year.
+ // Valid range 0..6.
+ int32_t first = julianDayToDayOfWeek(jan1Start + 1) - firstDayOfWeek;
+ if (first < 0) {
+ first += 7;
+ }
+ int32_t nextFirst = julianDayToDayOfWeek(nextJan1Start + 1) - firstDayOfWeek;
+ if (nextFirst < 0) {
+ nextFirst += 7;
+ }
+
+ int32_t minDays = getMinimalDaysInFirstWeek();
+ UBool jan1InPrevYear = FALSE; // January 1st in the year of WOY is the 1st week? (i.e. first week is < minimal )
+ UBool nextJan1InPrevYear = FALSE; // January 1st of Year of WOY + 1 is in the first week?
+
+ if((7 - first) < minDays) {
+ jan1InPrevYear = TRUE;
+ }
+
+ if((7 - nextFirst) < minDays) {
+ nextJan1InPrevYear = TRUE;
+ }
+
+ switch(bestField) {
+ case UCAL_WEEK_OF_YEAR:
+ if(woy == 1) {
+ if(jan1InPrevYear == TRUE) {
+ // the first week of January is in the previous year
+ // therefore WOY1 is always solidly within yearWoy
+ return yearWoy;
+ } else {
+ // First WOY is split between two years
+ if( dowLocal < first) { // we are prior to Jan 1
+ return yearWoy-1; // previous year
+ } else {
+ return yearWoy; // in this year
+ }
+ }
+ } else if(woy >= getLeastMaximum(bestField)) {
+ // we _might_ be in the last week..
+ int32_t jd = // Calculate JD of our target day:
+ jan1Start + // JD of Jan 1
+ (7-first) + // days in the first week (Jan 1.. )
+ (woy-1)*7 + // add the weeks of the year
+ dowLocal; // the local dow (0..6) of last week
+ if(jan1InPrevYear==FALSE) {
+ jd -= 7; // woy already includes Jan 1's week.
+ }
+
+ if( (jd+1) >= nextJan1Start ) {
+ // we are in week 52 or 53 etc. - actual year is yearWoy+1
+ return yearWoy+1;
+ } else {
+ // still in yearWoy;
+ return yearWoy;
+ }
+ } else {
+ // we're not possibly in the last week -must be ywoy
+ return yearWoy;
+ }
+ break;
+
+ case UCAL_DATE:
+ if((internalGet(UCAL_MONTH)==0) &&
+ (woy >= getLeastMaximum(UCAL_WEEK_OF_YEAR))) {
+ return yearWoy+1; // month 0, late woy = in the next year
+ } else if(woy==1) {
+ //if(nextJan1InPrevYear) {
+ if(internalGet(UCAL_MONTH)==0) {
+ return yearWoy;
+ } else {
+ return yearWoy-1;
+ }
+ //}
+ }
+
+ //(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow */ ) {
+ //within 1st week and in this month..
+ //return yearWoy+1;
+ return yearWoy;
+ break;
+
+ default: // assume the year is appropriate
+ return yearWoy;
+ break;
+ }
+
+#if defined (U_DEBUG_CAL)
+ fprintf(stderr, "%s:%d - forgot a return on field %s\n", __FILE__, __LINE__, fldName(bestField));
+#endif
+
+ return yearWoy;
+}
+
+int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const
+{
+ return handleComputeMonthStart(extendedYear, month+1, TRUE) -
+ handleComputeMonthStart(extendedYear, month, TRUE);
+}
+
+int32_t Calendar::handleGetYearLength(int32_t eyear) const {
+ return handleComputeMonthStart(eyear+1, 0, FALSE) -
+ handleComputeMonthStart(eyear, 0, FALSE);
+}
+