blob: f25f66a8f00995c2bdb0ccaf6a37db18a6346dbc [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 1996-2000, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/util/Attic/IBMCalendar.java,v $
* $Date: 2000/03/10 04:17:58 $
* $Revision: 1.5 $
*
*****************************************************************************************
*/
package com.ibm.util;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.TimeZone;
import java.text.MessageFormat;
import java.text.DateFormat;
import com.ibm.text.DateFormatSymbols;
import com.ibm.text.SimpleDateFormat;
/**
* Calendar utility class.
*
* The API methods on this class fall into several categories:
* <ul>
* <li>Static methods for retrieving {@link java.text.DateFormat} and
* {@link java.text.DateFormatSymbols} objects that match a particular
* subclass of <code>Calendar</code>.
*
* <li>Methods that were added to {@link java.util.Calendar Calendar} in JDK 1.2 or that
* we hope to add in a later release.
*
* <li>Methods that make <code>Calendar</code> easier to subclass. This includes
* default implementations for methods declared abstract on Calendar,
* so that subclasses are not forced to implement them. In addition,
* there are several new utility methods that make it easier for
* subclassers to implement methods such as roll, add, computeTime,
* and computeFields.
* </ul>
* <p>
* <b>Note:</b> You should always use {@link #roll roll} and {@link #add add} rather
* than attempting to perform arithmetic operations directly on the fields
* of a <tt>Calendar</tt>. It is quite possible for <tt>Calendar</tt> subclasses
* to have fields with non-linear behavior, for example missing months
* or days during non-leap years. The subclasses' <tt>add</tt> and <tt>roll</tt>
* methods will take this into account, while simple arithmetic manipulations
* may give invalid results.
* <p>
* <b>Subclassing</b>
* <br>
* It is possible to create subclasses of IBMCalendar that interpret
* dates according to other calendar systems that are not yet supported
* by Sun or IBM. In order to do so, you must do the following:
* <ul>
* <li>Override {@link #getMinimum getMinimum},
* {@link #getGreatestMinimum getGreatestMinimum},
* {@link #getMaximum getMaximum}, and
* {@link #getLeastMaximum getLeastMaximum} to return
* appropriate values for the new calendar system.
*
* <li>If there are any fields whose minimum or maximum value can vary
* depending on the actual date set in the calendar (e.g. the number
* of days in a month
* varies depending on the month and/or year), you must override
* {@link #getActualMaximum getActualMaximum} and/or
* {@link #getActualMinimum getActualMinimum}
* to take this into account.
*
* <li>Override {@link #computeTime computeTime} and {@link #computeFields computeFields}
* to perform the conversion from field values to milliseconds and
* vice-versa. These two methods are the real heart of any subclass
* and are typically the ones that require the most work. The protected
* utility method {@link #weekNumber weekNumber} is often helpful when implementing
* these two methods.
*
* <li>If there are any fields whose ranges are not always continuous, you
* must override {@link #roll roll} and {@link #add add} to take this
* into account. For example, in the Hebrew calendar the month "Adar I"
* only occurs in leap years; in other years the calendar jumps from
* Shevat (month #4) to Adar (month #6). The
* {@link HebrewCalendar#add HebrewCalendar.add} and
* {@link HebrewCalendar#roll HebrewCalendar.roll}
* methods take this into account, so that adding
* 1 month to Shevat gives the proper result (Adar) in a non-leap year.
* The protected utility method {@link #pinField pinField} is often useful
* when implementing these two methods.
* </ul>
* See the individual descriptions of the above methods for more information
* on their implications for subclassing.
*
* @author Laura Werner
*/
public abstract class IBMCalendar extends java.util.Calendar {
private static String copyright = "Copyright \u00a9 1997-1998 IBM Corp. All Rights Reserved.";
protected IBMCalendar(TimeZone zone, Locale aLocale) {
super(zone, aLocale);
}
/**
* Compare this object to another {@link Calendar} or to a {@link Date}.
* <p>
* @param when the {@link Calendar} or {@link Date} object to
* be compared against.
*
* @return true if this object's absolute time is before that
* represented by <code>when</code>, false otherwise.
*
* @throws IllegalArgumentException if <code>when</code> is null or is
* not a <code>Calendar</code> or <code>Date</code>.
*/
public boolean before(Object when)
{
if (this == when) {
return false;
}
else if (when instanceof Calendar) {
return getTime().before(((Calendar)when).getTime());
}
else if (when instanceof Date) {
return getTime().before((Date)when);
} else {
throw new IllegalArgumentException("argument must be a non-null Calendar or Date");
}
}
/**
* Compare this object to another {@link Calendar} or to a {@link Date}.
* <p>
* @param when the {@link Calendar} or {@link Date} object to be compared against.
*
* @return true if this object's absolute time is after that
* represented by <code>when</code>, false otherwise.
*
* @throws IllegalArgumentException if <code>when</code> is null or is
* not a <code>Calendar</code> or <code>Date</code>.
*/
public boolean after(Object when)
{
if (this == when) {
return false;
}
else if (when instanceof Calendar) {
return getTime().after(((Calendar)when).getTime());
}
else if (when instanceof Date) {
return getTime().after((Date)when);
} else {
throw new IllegalArgumentException("argument must be a non-null Calendar or Date");
}
}
/**
* Compares this object to another {@link Calendar} and returns <code>true</code>
* if they are the same. Unlike {@link #before before} and {@link #after after},
* this method
* compares all of the properties of the calendar rather than comparing only
* its time in milliseconds.
* <p>
* @param obj the {@link Calendar} object to compair against.
*/
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (this.getClass() != obj.getClass()) return false;
Calendar that = (Calendar) obj;
return
getTimeInMillis() == that.getTime().getTime() &&
isLenient() == that.isLenient() &&
getFirstDayOfWeek() == that.getFirstDayOfWeek() &&
getMinimalDaysInFirstWeek() == that.getMinimalDaysInFirstWeek() &&
getTimeZone().equals(that.getTimeZone());
}
/**
* Return the maximum value that this field could have, given the current date.
* For example, with the Gregorian date "Feb 3, 1997" and the
* {@link #DAY_OF_MONTH DAY_OF_MONTH} field, the actual
* maximum is 28; for "Feb 3, 1996" it is 29.
* <p>
* <b>Note:</b>This method has been added to {@link java.util.Calendar} in JDK 1.2; it
* is provided here so that 1.1 clients can take advantage of it as well.
* <p>
* <b>Subclassing:</b><br>
* For the {@link #WEEK_OF_YEAR WEEK_OF_YEAR}, {@link #WEEK_OF_MONTH WEEK_OF_MONTH},
* and {@link #DAY_OF_WEEK_IN_MONTH DAY_OF_WEEK_IN_MONTH} fields, this method
* calls <code>getActualMaximum(DAY_OF_YEAR)</code> or
* <code>getActualMaximum(DAY_OF_MONTH)</code> and then uses that value in a call to
* {@link #weekNumber weekNumber} to determine the result. However, for all other
* fields it iterates between the values returned by
* {@link #getLeastMaximum getLeastMaximum} and {@link #getMaximum getMaximum}
* to determine the actual maximum value for the field.
* <p>
* There is almost always a more efficient way to accomplish this,
* and thus you should almost always override this method in your subclass.
* For most fields, you can simply return the same thing as
* {@link #getMaximum getMaximum}. If your class has an internal method that
* calculates the number of days in a month or year, your getActualMaximum override
* can use those functions in its implementation. For fields that you cannot
* handle efficiently, you can simply call <code>super.getActualMaximum(field)</code>.
* <p>
* @param field the field whose maximum is desired
*
* @return the maximum of the given field for the current date of this calendar
*
* @see #getMaximum
* @see #getLeastMaximum
*/
public int getActualMaximum(int field) {
int result;
switch (field) {
//
// For the week-related fields, there's a shortcut. Since we know the
// current DAY_OF_WEEK and DAY_OF_MONTH or DAY_OF_YEAR, we can compute
// the the maximum for this field in terms of the maximum DAY_OF_*,
// on the theory that's easier to determine.
//
case WEEK_OF_YEAR:
result = weekNumber(getActualMaximum(DAY_OF_YEAR),
get(DAY_OF_YEAR), get(DAY_OF_WEEK));
break;
case WEEK_OF_MONTH:
result = weekNumber(getActualMaximum(DAY_OF_MONTH),
get(DAY_OF_MONTH), get(DAY_OF_WEEK));
break;
case DAY_OF_WEEK_IN_MONTH:
int weekLength = getMaximum(DAY_OF_WEEK) - getMinimum(DAY_OF_WEEK) + 1;
result = (getActualMaximum(DAY_OF_MONTH) - 1) / weekLength + 1;
break;
// For all other fields, do it the hard way....
default:
result = getActualHelper(field, getLeastMaximum(field), getMaximum(field));
break;
}
return result;
}
/**
* Return the minimum value that this field could have, given the current date.
* For most fields, this is the same as {@link #getMinimum getMinimum}
* and {@link #getGreatestMinimum getGreatestMinimum}. However, some fields,
* especially those related to week number, are more complicated.
* <p>
* For example, assume {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek}
* returns 4 and {@link #getFirstDayOfWeek getFirstDayOfWeek} returns SUNDAY.
* If the first day of the month is Sunday, Monday, Tuesday, or Wednesday
* there will be four or more days in the first week, so it will be week number 1,
* and <code>getActualMinimum(WEEK_OF_MONTH)</code> will return 1. However,
* if the first of the month is a Thursday, Friday, or Saturday, there are
* <em>not</em> four days in that week, so it is week number 0, and
* <code>getActualMinimum(WEEK_OF_MONTH)</code> will return 0.
* <p>
* <b>Note:</b>This method has been added to java.util.Calendar in JDK 1.2; it
* is provided here so that 1.1 clients can take advantage of it as well.
* <p>
* <b>Subclassing:</b><br>
* For the {@link #WEEK_OF_YEAR WEEK_OF_YEAR}, {@link #WEEK_OF_MONTH WEEK_OF_MONTH},
* and {@link #DAY_OF_WEEK_IN_MONTH DAY_OF_WEEK_IN_MONTH} fields, this method
* calls <code>getActualMinimum(DAY_OF_YEAR)</code> or
* <code>getActualMinimum(DAY_OF_MONTH)</code> and then uses that value in a call to
* {@link #weekNumber weekNumber} to determine the result. However, for all other
* fields it iterates between the values returned by
* {@link #getMinimum getMinimum} and {@link #getGreatestMinimum getGreatestMinimum}
* to determine the actual maximum value for the field.
* <p>
* There is almost always a more efficient way to accomplish this,
* and thus you should almost always override this method in your subclass.
* For most fields, you can simply return the same thing as
* {@link #getMinimum getMinimum}. For fields that you cannot
* handle efficiently, you can simply call <code>super.getActualMaximum(field)</code>.
* <p>
* @param field the field whose actual minimum value is desired.
* @return the minimum of the given field for the current date of this calendar
*
* @see #getMinimum
* @see #getGreatestMinimum
*/
public int getActualMinimum(int field) {
int result;
switch (field) {
//
// For the week-related fields, there's a shortcut. Since we know the
// current DAY_OF_WEEK and DAY_OF_MONTH or DAY_OF_YEAR, we can compute
// the the maximum for this field in terms of the maximum DAY_OF_*,
// on the theory that's easier to determine.
//
case WEEK_OF_YEAR:
result = weekNumber(getActualMinimum(DAY_OF_YEAR),
get(DAY_OF_YEAR), get(DAY_OF_WEEK));
break;
case WEEK_OF_MONTH:
result = weekNumber(getActualMinimum(DAY_OF_MONTH),
get(DAY_OF_MONTH), get(DAY_OF_WEEK));
break;
case DAY_OF_WEEK_IN_MONTH:
int weekLength = getMaximum(DAY_OF_WEEK) - getMinimum(DAY_OF_WEEK) + 1;
result = (getActualMinimum(DAY_OF_MONTH) - 1) / weekLength + 1;
break;
// For all other fields, do it the hard way....
default:
result = getActualHelper(field, getGreatestMinimum(field), getMinimum(field));
break;
}
return result;
}
private int getActualHelper(int field, int startValue, int endValue)
{
int result = startValue;
if (startValue == endValue) {
// if we know that the maximum value is always the same, just return it
result = startValue;
}
else if (startValue != endValue) {
// clone the calendar so we don't mess with the real one, and set it to
// accept anything for the field values
Calendar work = (Calendar)this.clone();
work.setLenient(true);
//
// now try each value from the start to the end one by one until
// we get a value that normalizes to another value. The last value that
// normalizes to itself is the actual maximum for the current date
//
int delta = (endValue > startValue) ? 1 : -1;
startValue += delta;
result = startValue;
while (result != endValue) {
work.set(field, startValue);
if (work.get(field) != startValue) {
break;
} else {
result = startValue;
startValue += delta;
}
}
}
return result;
}
/**
* Rolls (up/down) a single unit of time on the given field. If the
* field is rolled past its maximum allowable value, it will "wrap" back
* to its minimum and continue rolling. For
* example, to roll the current date up by one day, you can call:
* <p>
* <code>roll({@link #DATE}, true)</code>
* <p>
* When rolling on the {@link #YEAR} field, it will roll the year
* value in the range between 1 and the value returned by calling
* {@link #getMaximum getMaximum}({@link #YEAR}).
* <p>
* When rolling on certain fields, the values of other fields may conflict and
* need to be changed. For example, when rolling the <code>MONTH</code> field
* for the Gregorian date 1/31/96 upward, the <code>DAY_OF_MONTH</code> field
* must be adjusted so that the result is 2/29/96 rather than the invalid
* 2/31/96.
* <p>
* <b>Note:</b> Calling <tt>roll(field, true)</tt> N times is <em>not</em>
* necessarily equivalent to calling <tt>roll(field, N)</tt>. For example,
* imagine that you start with the date Gregorian date January 31, 1995. If you call
* <tt>roll(Calendar.MONTH, 2)</tt>, the result will be March 31, 1995.
* But if you call <tt>roll(Calendar.MONTH, true)</tt>, the result will be
* February 28, 1995. Calling it one more time will give March 28, 1995, which
* is usually not the desired result.
* <p>
* <b>Note:</b> You should always use <tt>roll</tt> and <tt>add</tt> rather
* than attempting to perform arithmetic operations directly on the fields
* of a <tt>Calendar</tt>. It is quite possible for <tt>Calendar</tt> subclasses
* to have fields with non-linear behavior, for example missing months
* or days during non-leap years. The subclasses' <tt>add</tt> and <tt>roll</tt>
* methods will take this into account, while simple arithmetic manipulations
* may give invalid results.
* <p>
* @param field the calendar field to roll.
*
* @param up indicates if the value of the specified time field is to be
* rolled up or rolled down. Use <code>true</code> if rolling up,
* <code>false</code> otherwise.
*
* @exception IllegalArgumentException if the field is invalid or refers
* to a field that cannot be handled by this method.
* @see #roll(int, int)
* @see #add
*/
public final void roll(int field, boolean up)
{
roll(field, up ? +1 : -1);
}
/**
* Rolls (up/down) a specified amount time on the given field. For
* example, to roll the current date up by three days, you can call
* <code>roll(Calendar.DATE, 3)</code>. If the
* field is rolled past its maximum allowable value, it will "wrap" back
* to its minimum and continue rolling.
* For example, calling <code>roll(Calendar.DATE, 10)</code>
* on a Gregorian calendar set to 4/25/96 will result in the date 4/5/96.
* <p>
* When rolling on certain fields, the values of other fields may conflict and
* need to be changed. For example, when rolling the {@link #MONTH MONTH} field
* for the Gregorian date 1/31/96 by +1, the {@link #DAY_OF_MONTH DAY_OF_MONTH} field
* must be adjusted so that the result is 2/29/96 rather than the invalid
* 2/31/96.
* <p>
* The <code>IBMCalendar</code> implementation of this method is able to roll
* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET},
* and {@link #ZONE_OFFSET ZONE_OFFSET}. Subclasses may, of course, add support for
* additional fields in their overrides of <code>roll</code>.
* <p>
* <b>Note:</b> You should always use <tt>roll</tt> and <tt>add</tt> rather
* than attempting to perform arithmetic operations directly on the fields
* of a <tt>Calendar</tt>. It is quite possible for <tt>Calendar</tt> subclasses
* to have fields with non-linear behavior, for example missing months
* or days during non-leap years. The subclasses' <tt>add</tt> and <tt>roll</tt>
* methods will take this into account, while simple arithmetic manipulations
* may give invalid results.
* <p>
* <b>Subclassing:</b><br>
* This implementation of <code>roll</code> assumes that the behavior of the
* field is continuous between its minimum and maximum, which are found by
* calling {@link #getActualMinimum getActualMinimum} and {@link #getActualMaximum getActualMaximum}.
* For most such fields, simple addition, subtraction, and modulus operations
* are sufficient to perform the roll. For week-related fields,
* the results of {@link #getFirstDayOfWeek getFirstDayOfWeek} and
* {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek} are also necessary.
* Subclasses can override these two methods if their values differ from the defaults.
* <p>
* Subclasses that have fields for which the assumption of continuity breaks
* down must overide <code>roll</code> to handle those fields specially.
* For example, in the Hebrew calendar the month "Adar I"
* only occurs in leap years; in other years the calendar jumps from
* Shevat (month #4) to Adar (month #6). The
* {@link HebrewCalendar#roll HebrewCalendar.roll} method takes this into account,
* so that rolling the month of Shevat by one gives the proper result (Adar) in a
* non-leap year.
* <p>
* @param field the calendar field to roll.
* @param amount the amount by which the field should be rolled.
*
* @exception IllegalArgumentException if the field is invalid or refers
* to a field that cannot be handled by this method.
* @see #roll(int, boolean)
* @see #add
*/
public void roll(int field, int amount)
{
if (amount == 0) return; // Nothing to do
complete();
switch (field) {
case DAY_OF_MONTH:
case AM_PM:
case MINUTE:
case SECOND:
case MILLISECOND:
// These are the standard roll instructions. These work for all
// simple cases, that is, cases in which the limits are fixed, such
// as the hour, the day of the month, and the era.
{
int min = getActualMinimum(field);
int max = getActualMaximum(field);
int gap = max - min + 1;
int value = internalGet(field) + amount;
value = (value - min) % gap;
if (value < 0) value += gap;
value += min;
set(field, value);
return;
}
case HOUR:
case HOUR_OF_DAY:
// Rolling the hour is difficult on the ONSET and CEASE days of
// daylight savings. For example, if the change occurs at
// 2 AM, we have the following progression:
// ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
// CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
// To get around this problem we don't use fields; we manipulate
// the time in millis directly.
{
// Assume min == 0 in calculations below
Date start = getTime();
int oldHour = internalGet(field);
int max = getActualMaximum(field);
int newHour = (oldHour + amount) % (max + 1);
if (newHour < 0) {
newHour += max + 1;
}
setTime(new Date(start.getTime() + HOUR_MS * (newHour - oldHour)));
return;
}
case MONTH:
// Rolling the month involves both pinning the final value
// and adjusting the DAY_OF_MONTH if necessary. We only adjust the
// DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
// E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
{
int max = getActualMaximum(MONTH);
int mon = (internalGet(MONTH) + amount) % (max+1);
if (mon < 0) mon += (max + 1);
set(MONTH, mon);
// Keep the day of month in range. We don't want to spill over
// into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
// mar3.
pinField(DAY_OF_MONTH);
return;
}
case YEAR:
// Rolling the year can involve pinning the DAY_OF_MONTH.
set(YEAR, internalGet(YEAR) + amount);
pinField(MONTH);
pinField(DAY_OF_MONTH);
return;
case WEEK_OF_MONTH:
{
// This is tricky, because during the roll we may have to shift
// to a different day of the week. For example:
// s m t w r f s
// 1 2 3 4 5
// 6 7 8 9 10 11 12
// When rolling from the 6th or 7th back one week, we go to the
// 1st (assuming that the first partial week counts). The same
// thing happens at the end of the month.
// The other tricky thing is that we have to figure out whether
// the first partial week actually counts or not, based on the
// minimal first days in the week. And we have to use the
// correct first day of the week to delineate the week
// boundaries.
// Here's our algorithm. First, we find the real boundaries of
// the month. Then we discard the first partial week if it
// doesn't count in this locale. Then we fill in the ends with
// phantom days, so that the first partial week and the last
// partial week are full weeks. We then have a nice square
// block of weeks. We do the usual rolling within this block,
// as is done elsewhere in this method. If we wind up on one of
// the phantom days that we added, we recognize this and pin to
// the first or the last day of the month. Easy, eh?
// Normalize the DAY_OF_WEEK so that 0 is the first day of the week
// in this locale. We have dow in 0..6.
int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek();
if (dow < 0) dow += 7;
// Find the day of the week (normalized for locale) for the first
// of the month.
int fdm = (dow - internalGet(DAY_OF_MONTH) + 1) % 7;
if (fdm < 0) fdm += 7;
// Get the first day of the first full week of the month,
// including phantom days, if any. Figure out if the first week
// counts or not; if it counts, then fill in phantom days. If
// not, advance to the first real full week (skip the partial week).
int start;
if ((7 - fdm) < getMinimalDaysInFirstWeek())
start = 8 - fdm; // Skip the first partial week
else
start = 1 - fdm; // This may be zero or negative
// Get the day of the week (normalized for locale) for the last
// day of the month.
int monthLen = getActualMaximum(DAY_OF_MONTH);
int ldm = (monthLen - internalGet(DAY_OF_MONTH) + dow) % 7;
// We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.
// Get the limit day for the blocked-off rectangular month; that
// is, the day which is one past the last day of the month,
// after the month has already been filled in with phantom days
// to fill out the last week. This day has a normalized DOW of 0.
int limit = monthLen + 7 - ldm;
// Now roll between start and (limit - 1).
int gap = limit - start;
int day_of_month = (internalGet(DAY_OF_MONTH) + amount*7 -
start) % gap;
if (day_of_month < 0) day_of_month += gap;
day_of_month += start;
// Finally, pin to the real start and end of the month.
if (day_of_month < 1) day_of_month = 1;
if (day_of_month > monthLen) day_of_month = monthLen;
// Set the DAY_OF_MONTH. We rely on the fact that this field
// takes precedence over everything else (since all other fields
// are also set at this point). If this fact changes (if the
// disambiguation algorithm changes) then we will have to unset
// the appropriate fields here so that DAY_OF_MONTH is attended
// to.
set(DAY_OF_MONTH, day_of_month);
return;
}
case WEEK_OF_YEAR:
{
// This follows the outline of WEEK_OF_MONTH, except it applies
// to the whole year. Please see the comment for WEEK_OF_MONTH
// for general notes.
// Normalize the DAY_OF_WEEK so that 0 is the first day of the week
// in this locale. We have dow in 0..6.
int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek();
if (dow < 0) dow += 7;
// Find the day of the week (normalized for locale) for the first
// of the year.
int fdy = (dow - internalGet(DAY_OF_YEAR) + 1) % 7;
if (fdy < 0) fdy += 7;
// Get the first day of the first full week of the year,
// including phantom days, if any. Figure out if the first week
// counts or not; if it counts, then fill in phantom days. If
// not, advance to the first real full week (skip the partial week).
int start;
if ((7 - fdy) < getMinimalDaysInFirstWeek())
start = 8 - fdy; // Skip the first partial week
else
start = 1 - fdy; // This may be zero or negative
// Get the day of the week (normalized for locale) for the last
// day of the year.
int yearLen = getActualMaximum(DAY_OF_YEAR);
int ldy = (yearLen - internalGet(DAY_OF_YEAR) + dow) % 7;
// We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here.
// Get the limit day for the blocked-off rectangular year; that
// is, the day which is one past the last day of the year,
// after the year has already been filled in with phantom days
// to fill out the last week. This day has a normalized DOW of 0.
int limit = yearLen + 7 - ldy;
// Now roll between start and (limit - 1).
int gap = limit - start;
int day_of_year = (internalGet(DAY_OF_YEAR) + amount*7 -
start) % gap;
if (day_of_year < 0) day_of_year += gap;
day_of_year += start;
// Finally, pin to the real start and end of the month.
if (day_of_year < 1) day_of_year = 1;
if (day_of_year > yearLen) day_of_year = yearLen;
// Make sure that the year and day of year are attended to by
// clearing other fields which would normally take precedence.
// If the disambiguation algorithm is changed, this section will
// have to be updated as well.
set(DAY_OF_YEAR, day_of_year);
clear(MONTH);
return;
}
case DAY_OF_YEAR:
{
// Roll the day of year using millis. Compute the millis for
// the start of the year, and get the length of the year.
long delta = amount * DAY_MS; // Scale up from days to millis
long min2 = time - (internalGet(DAY_OF_YEAR) - 1) * DAY_MS;
int yearLength = getActualMaximum(DAY_OF_YEAR);
time = (time + delta - min2) % (yearLength*DAY_MS);
if (time < 0) time += yearLength*DAY_MS;
setTimeInMillis(time + min2);
return;
}
case DAY_OF_WEEK:
{
// Roll the day of week using millis. Compute the millis for
// the start of the week, using the first day of week setting.
// Restrict the millis to [start, start+7days).
long delta = amount * DAY_MS; // Scale up from days to millis
// Compute the number of days before the current day in this
// week. This will be a value 0..6.
int leadDays = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek();
if (leadDays < 0) leadDays += 7;
long min2 = time - leadDays * DAY_MS;
time = (time + delta - min2) % WEEK_MS;
if (time < 0) time += WEEK_MS;
setTimeInMillis(time + min2);
return;
}
case DAY_OF_WEEK_IN_MONTH:
{
// Roll the day of week in the month using millis. Determine
// the first day of the week in the month, and then the last,
// and then roll within that range.
long delta = amount * WEEK_MS; // Scale up from weeks to millis
// Find the number of same days of the week before this one
// in this month.
int preWeeks = (internalGet(DAY_OF_MONTH) - 1) / 7;
// Find the number of same days of the week after this one
// in this month.
int postWeeks = (getActualMaximum(DAY_OF_MONTH) -
internalGet(DAY_OF_MONTH)) / 7;
// From these compute the min and gap millis for rolling.
long min2 = time - preWeeks * WEEK_MS;
long gap2 = WEEK_MS * (preWeeks + postWeeks + 1); // Must add 1!
// Roll within this range
time = (time + delta - min2) % gap2;
if (time < 0) time += gap2;
setTimeInMillis(time + min2);
return;
}
default:
// Other fields cannot be rolled by this method
throw new IllegalArgumentException("IBMCalendar.roll: field not supported");
}
}
/**
* Add a signed amount to a specified field, using this calendar's rules.
* For example, to add three days to the current date, you can call
* <code>add(Calendar.DATE, 3)</code>.
* <p>
* When adding to certain fields, the values of other fields may conflict and
* need to be changed. For example, when adding one to the {@link #MONTH MONTH} field
* for the Gregorian date 1/31/96, the {@link #DAY_OF_MONTH DAY_OF_MONTH} field
* must be adjusted so that the result is 2/29/96 rather than the invalid
* 2/31/96.
* <p>
* The <code>IBMCalendar</code> implementation of this method is able to add to
* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET},
* and {@link #ZONE_OFFSET ZONE_OFFSET}. Subclasses may, of course, add support for
* additional fields in their overrides of <code>add</code>.
* <p>
* <b>Note:</b> You should always use <tt>roll</tt> and <tt>add</tt> rather
* than attempting to perform arithmetic operations directly on the fields
* of a <tt>Calendar</tt>. It is quite possible for <tt>Calendar</tt> subclasses
* to have fields with non-linear behavior, for example missing months
* or days during non-leap years. The subclasses' <tt>add</tt> and <tt>roll</tt>
* methods will take this into account, while simple arithmetic manipulations
* may give invalid results.
* <p>
* <b>Subclassing:</b><br>
* This implementation of <code>add</code> assumes that the behavior of the
* field is continuous between its minimum and maximum, which are found by
* calling {@link #getActualMinimum getActualMinimum} and
* {@link #getActualMaximum getActualMaximum}.
* For such fields, simple arithmetic operations are sufficient to
* perform the add.
* <p>
* Subclasses that have fields for which this assumption of continuity breaks
* down must overide <code>add</code> to handle those fields specially.
* For example, in the Hebrew calendar the month "Adar I"
* only occurs in leap years; in other years the calendar jumps from
* Shevat (month #4) to Adar (month #6). The
* {@link HebrewCalendar#add HebrewCalendar.add} method takes this into account,
* so that adding one month
* to a date in Shevat gives the proper result (Adar) in a non-leap year.
* <p>
* @param field the time field.
* @param amount the amount to add to the field.
*
* @exception IllegalArgumentException if the field is invalid or refers
* to a field that cannot be handled by this method.
* @see #roll(int, int)
*/
public void add(int field, int amount)
{
if (amount == 0) return; // Do nothing!
long delta = amount;
switch (field) {
case YEAR:
{
int year = get(YEAR) + amount;
set(YEAR, year);
pinField(DAY_OF_MONTH);
return;
}
case MONTH:
// About the best we can do for a default implementation is
// assume a constant number of months per year. Subclasses
// with a variable number of months will have to do this
// one themselves
{
int month = this.get(MONTH) + amount;
int year = get(YEAR);
int length = getMaximum(MONTH) + 1;
if (month >= 0) {
set(YEAR, year + month/length);
set(MONTH, month % length);
} else {
set(YEAR, year + (month + 1)/length - 1);
month %= length;
if (month < 0) {
month += length;
}
set(MONTH, month);
}
pinField(DAY_OF_MONTH);
return;
}
case WEEK_OF_YEAR:
case WEEK_OF_MONTH:
case DAY_OF_WEEK_IN_MONTH:
delta *= WEEK_MS;
break;
case AM_PM:
delta *= 12 * HOUR_MS;
break;
case DAY_OF_MONTH:
case DAY_OF_YEAR:
case DAY_OF_WEEK:
delta *= DAY_MS;
break;
case HOUR_OF_DAY:
case HOUR:
delta *= HOUR_MS;
break;
case MINUTE:
delta *= MINUTE_MS;
break;
case SECOND:
delta *= SECOND_MS;
break;
case MILLISECOND:
break;
default:
throw new IllegalArgumentException("IBMCalendar.add: field not supported");
}
setTimeInMillis(getTimeInMillis() + delta);
}
/**
* Return the name of this calendar in the language of the given locale.
*/
public String getDisplayName(Locale loc) {
return this.getClass().getName();
}
//-------------------------------------------------------------------------
// Public static interface for creating custon DateFormats for different
// types of Calendars.
//-------------------------------------------------------------------------
/**
* Create a {@link DateFormat} object that can be used to format dates in
* the calendar system specified by <code>cal</code>.
* <p>
* <b>Note:</b> When this functionality is moved into the core JDK, this method
* will probably be replaced by a new overload of {@link DateFormat#getInstance}.
* <p>
* @param cal The calendar system for which a date format is desired.
*
* @param dateStyle The type of date format desired. This can be
* {@link DateFormat#SHORT}, {@link DateFormat#MEDIUM},
* etc.
*
* @param locale The locale for which the date format is desired.
*
* @see java.text.DateFormat#getDateInstance
*/
static public DateFormat getDateFormat(Calendar cal, int dateStyle, Locale locale)
{
return getDateTimeFormat(cal, locale, dateStyle, -1);
}
/**
* Create a {@link DateFormat} object that can be used to format times in
* the calendar system specified by <code>cal</code>.
* <p>
* <b>Note:</b> When this functionality is moved into the core JDK, this method
* will probably be replaced by a new overload of {@link DateFormat#getInstance}.
* <p>
* @param cal The calendar system for which a time format is desired.
*
* @param timeStyle The type of time format desired. This can be
* {@link DateFormat#SHORT}, {@link DateFormat#MEDIUM},
* etc.
*
* @param locale The locale for which the time format is desired.
*
* @see java.text.DateFormat#getTimeInstance
*/
static public DateFormat getTimeFormat(Calendar cal, int timeStyle, Locale locale)
{
return getDateTimeFormat(cal, locale, -1, timeStyle);
}
/**
* Create a {@link DateFormat} object that can be used to format dates and times in
* the calendar system specified by <code>cal</code>.
* <p>
* <b>Note:</b> When this functionality is moved into the core JDK, this method
* will probably be replaced by a new overload of {@link DateFormat#getInstance}.
* <p>
* @param cal The calendar system for which a date/time format is desired.
*
* @param dateStyle The type of date format desired. This can be
* {@link DateFormat#SHORT}, {@link DateFormat#MEDIUM},
* etc.
*
* @param timeStyle The type of time format desired. This can be
* {@link DateFormat#SHORT}, {@link DateFormat#MEDIUM},
* etc.
*
* @param locale The locale for which the date/time format is desired.
*
* @see java.text.DateFormat#getDateTimeInstance
*/
static public DateFormat getDateTimeFormat(Calendar cal, int dateStyle,
int timeStyle, Locale locale)
{
return getDateTimeFormat(cal, locale, dateStyle, timeStyle);
}
/**
* Get the {@link DateFormatSymbols} object that should be used to format a
* calendar system's dates in the given locale.
* <p>
* <b>Note:</b> When this functionality is moved into the core JDK, this method
* will probably be replace by a new constructor on <tt>DateFormatSymbols</tt>.
* <p>
* <b>Subclassing:</b><br>
* When creating a new Calendar subclass, you must create the
* {@link ResourceBundle ResourceBundle}
* containing its {@link DateFormatSymbols DateFormatSymbols} in a specific place.
* The resource bundle name is based on the calendar's fully-specified
* class name, with ".resources" inserted at the end of the package name
* (just before the class name) and "Symbols" appended to the end.
* For example, the bundle corresponding to "com.ibm.util.HebrewCalendar"
* is "com.ibm.util.resources.HebrewCalendarSymbols".
* <p>
* Within the ResourceBundle, this method searches for five keys:
* <ul>
* <li><b>DayNames</b> -
* An array of strings corresponding to each possible
* value of the <code>DAY_OF_WEEK</code> field. Even though
* <code>DAY_OF_WEEK</code> starts with <code>SUNDAY</code> = 1,
* This array is 0-based; the name for Sunday goes in the
* first position, at index 0. If this key is not found
* in the bundle, the day names are inherited from the
* default <code>DateFormatSymbols</code> for the requested locale.
*
* <li><b>DayAbbreviations</b> -
* An array of abbreviated day names corresponding
* to the values in the "DayNames" array. If this key
* is not found in the resource bundle, the "DayNames"
* values are used instead. If neither key is found,
* the day abbreviations are inherited from the default
* <code>DateFormatSymbols</code> for the locale.
*
* <li><b>MonthNames</b> -
* An array of strings corresponding to each possible
* value of the <code>MONTH</code> field. If this key is not found
* in the bundle, the month names are inherited from the
* default <code>DateFormatSymbols</code> for the requested locale.
*
* <li><b>MonthAbbreviations</b> -
* An array of abbreviated day names corresponding
* to the values in the "MonthNames" array. If this key
* is not found in the resource bundle, the "MonthNames"
* values are used instead. If neither key is found,
* the day abbreviations are inherited from the default
* <code>DateFormatSymbols</code> for the locale.
*
* <li><b>Eras</b> -
* An array of strings corresponding to each possible
* value of the <code>ERA</code> field. If this key is not found
* in the bundle, the era names are inherited from the
* default <code>DateFormatSymbols</code> for the requested locale.
* </ul>
* <p>
* @param cal The calendar system whose date format symbols are desired.
* @param locale The locale whose symbols are desired.
*
* @see java.text.DateFormatSymbols#DateFormatSymbols(java.util.Locale)
*/
static public DateFormatSymbols getDateFormatSymbols(Calendar cal,
Locale locale)
{
ResourceBundle bundle = null;
try {
bundle = getDateFormatBundle(cal, locale);
}
catch (MissingResourceException e) {
if (!(cal instanceof GregorianCalendar)) {
// Ok for symbols to be missing for a Gregorian calendar, but
// not for any other type.
throw e;
}
}
return getDateFormatSymbols(null, bundle, locale);
}
/**
* Fetch a custom calendar's DateFormatSymbols out of the given resource
* bundle. Symbols that are not overridden are inherited from the
* default DateFormatSymbols for the locale.
* @see java.text.DateFormatSymbols#DateFormatSymbols
*/
static protected DateFormatSymbols getDateFormatSymbols(DateFormatSymbols result,
ResourceBundle bundle,
Locale locale)
{
// Get the default symbols for the locale, since most calendars will only
// need to override month names and will want everything else the same
if (result == null) {
result = new DateFormatSymbols(locale);
}
//
// Fetch the day names from the resource bundle. If they're not found,
// it's ok; we'll just use the default ones.
// Allow a null ResourceBundle just for the sake of completeness;
// this is useful for calendars that don't have any overridden symbols
//
if (bundle != null) {
try {
String[] temp = bundle.getStringArray("DayNames");
result.setWeekdays(temp);
result.setShortWeekdays(temp);
temp = bundle.getStringArray("DayAbbreviations");
result.setShortWeekdays( temp );
}
catch (MissingResourceException e) {
}
try {
String[] temp = bundle.getStringArray("MonthNames");
result.setMonths( temp );
result.setShortMonths( temp );
temp = bundle.getStringArray("MonthAbbreviations");
result.setShortMonths( temp );
}
catch (MissingResourceException e) {
}
try {
String[] temp = bundle.getStringArray("Eras");
result.setEras( temp );
}
catch (MissingResourceException e) {
}
}
return result;
}
protected DateFormatSymbols getDateFormatSymbols(Locale locale) {
return getDateFormatSymbols(null, getDateFormatBundle(this, locale), locale);
}
/**
* Private utility method to retrive a date and/or time format
* for the specified calendar and locale. This method has knowledge of
* (and is partly copied from) the corresponding code in SimpleDateFormat,
* but it knows how to find the right resource bundle based on the calendar class.
* <p>
* @param cal The calendar system whose date/time format is desired.
*
* @param timeStyle The type of time format desired. This can be
* <code>DateFormat.SHORT</code>, etc, or -1 if the time
* of day should not be included in the format.
*
* @param dateStyle The type of date format desired. This can be
* <code>DateFormat.SHORT</code>, etc, or -1 if the date
* should not be included in the format.
*
* @param loc The locale for which the date/time format is desired.
*
* @see java.text.DateFormat#getDateTimeInstance
*/
static private DateFormat getDateTimeFormat(Calendar cal, Locale loc,
int dateStyle, int timeStyle)
{
if (cal instanceof IBMCalendar) {
return ((IBMCalendar)cal).getDateTimeFormat(dateStyle,timeStyle,loc);
} else {
return formatHelper(cal, loc, dateStyle, timeStyle);
}
}
protected DateFormat getDateTimeFormat(int dateStyle, int timeStyle, Locale loc) {
return formatHelper(this, loc, dateStyle, timeStyle);
}
static private DateFormat formatHelper(Calendar cal, Locale loc,
int dateStyle, int timeStyle)
{
// See if there are any custom resources for this calendar
// If not, just use the default DateFormat
DateFormat result = null;
DateFormatSymbols symbols = null;
ResourceBundle bundle = getDateFormatBundle(cal, loc);
if (bundle != null) {
if (cal instanceof IBMCalendar) {
symbols = ((IBMCalendar)cal).getDateFormatSymbols(loc);
} else {
symbols = getDateFormatSymbols(null, bundle, loc);
}
try {
String[] patterns = bundle.getStringArray("DateTimePatterns");
String pattern = null;
if ((timeStyle >= 0) && (dateStyle >= 0)) {
Object[] dateTimeArgs = { patterns[timeStyle],
patterns[dateStyle + 4] };
pattern = MessageFormat.format(patterns[8], dateTimeArgs);
}
else if (timeStyle >= 0) {
pattern = patterns[timeStyle];
}
else if (dateStyle >= 0) {
pattern = patterns[dateStyle + 4];
}
else {
throw new IllegalArgumentException("No date or time style specified");
}
result = new SimpleDateFormat(pattern, symbols);
} catch (MissingResourceException e) {
// No custom patterns
if (dateStyle == -1) {
result = SimpleDateFormat.getTimeInstance(timeStyle, loc);
} else if (timeStyle == -1) {
result = SimpleDateFormat.getDateInstance(dateStyle, loc);
} else {
result = SimpleDateFormat.getDateTimeInstance(dateStyle, timeStyle, loc);
}
((java.text.SimpleDateFormat)result).setDateFormatSymbols(oldStyleSymbols(symbols, loc));
}
} else {
if (dateStyle == -1) {
result = SimpleDateFormat.getTimeInstance(timeStyle, loc);
} else if (timeStyle == -1) {
result = SimpleDateFormat.getDateInstance(dateStyle, loc);
} else {
result = SimpleDateFormat.getDateTimeInstance(dateStyle, timeStyle, loc);
}
}
result.setCalendar(cal);
return result;
}
private static final java.text.DateFormatSymbols oldStyleSymbols(DateFormatSymbols syms, Locale loc) {
java.text.DateFormatSymbols result = new java.text.DateFormatSymbols(loc);
result.setAmPmStrings(syms.getAmPmStrings());
result.setEras(syms.getEras());
result.setLocalPatternChars(syms.getLocalPatternChars());
result.setMonths(syms.getMonths());
result.setShortMonths(syms.getShortMonths());
result.setShortWeekdays(syms.getShortWeekdays());
result.setWeekdays(syms.getWeekdays());
result.setZoneStrings(syms.getZoneStrings());
return result;
}
/**
* Find the ResourceBundle containing the date format information for
* a specified calendar subclass in a given locale.
* <p>
* The resource bundle name is based on the calendar's fully-specified
* class name, with ".resources" inserted at the end of the package name
* (just before the class name) and "Symbols" appended to the end.
* For example, the bundle corresponding to "com.ibm.util.HebrewCalendar"
* is "com.ibm.util.resources.HebrewCalendarSymbols".
*/
static protected ResourceBundle getDateFormatBundle(Calendar cal, Locale locale)
throws MissingResourceException
{
// Find the calendar's class name, which we're going to use to construct the
// resource bundle name.
String fullName = cal.getClass().getName();
int lastDot = fullName.lastIndexOf('.');
String className = fullName.substring(lastDot+1);
// The name of the ResourceBundle itself is the calendar's fully-qualified
// name, with ".resources" inserted in the package and "Symbols" appended
String bundleName = fullName.substring(0, lastDot+1) + "resources."
+ className + "Symbols";
ResourceBundle result = null;
try {
result = ResourceBundle.getBundle(bundleName, locale);
}
catch (MissingResourceException e) {
if (!(cal instanceof GregorianCalendar)) {
// Ok for symbols to be missing for a Gregorian calendar, but
// not for any other type.
throw e;
}
}
return result;
}
//-------------------------------------------------------------------------
// Protected utility methods for use by subclasses. These are very handy
// for implementing add, roll, and computeFields.
//-------------------------------------------------------------------------
/**
* Adjust the specified field so that it is within
* the allowable range for the date to which this calendar is set.
* For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH}
* field for a calendar set to April 31 would cause it to be set
* to April 30.
* <p>
* <b>Subclassing:</b>
* <br>
* This utility method is intended for use by subclasses that need to implement
* their own overrides of {@link #roll roll} and {@link #add add}.
* <p>
* <b>Note:</b>
* <code>pinField</code> is implemented in terms of
* {@link #getActualMinimum getActualMinimum}
* and {@link #getActualMaximum getActualMaximum}. If either of those methods uses
* a slow, iterative algorithm for a particular field, it would be
* unwise to attempt to call <code>pinField</code> for that field. If you
* really do need to do so, you should override this method to do
* something more efficient for that field.
* <p>
* @param field The calendar field whose value should be pinned.
*
* @see #getActualMinimum
* @see #getActualMaximum
*/
protected void pinField(int field) {
int max = getActualMaximum(field);
int min = getActualMinimum(field);
if (fields[field] > max) {
set(field, max);
} else if (fields[field] < min) {
set(fields[field], min);
}
}
/**
* Return the week number of a day, within a period. This may be the week number in
* a year or the week number in a month. Usually this will be a value >= 1, but if
* some initial days of the period are excluded from week 1, because
* {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek} is > 1, then
* the week number will be zero for those
* initial days. This method requires the day number and day of week for some
* known date in the period in order to determine the day of week
* on the desired day.
* <p>
* <b>Subclassing:</b>
* <br>
* This method is intended for use by subclasses in implementing their
* {@link #computeTime computeTime} and/or {@link #computeFields computeFields} methods.
* It is often useful in {@link #getActualMinimum getActualMinimum} and
* {@link #getActualMaximum getActualMaximum} as well.
* <p>
* This variant is handy for computing the week number of some other
* day of a period (often the first or last day of the period) when its day
* of the week is not known but the day number and day of week for some other
* day in the period (e.g. the current date) <em>is</em> known.
* <p>
* @param desiredDay The {@link #DAY_OF_YEAR DAY_OF_YEAR} or
* {@link #DAY_OF_MONTH DAY_OF_MONTH} whose week number is desired.
* Should be 1 for the first day of the period.
*
* @param knownDayOfPeriod The {@link #DAY_OF_YEAR DAY_OF_YEAR}
* or {@link #DAY_OF_MONTH DAY_OF_MONTH} for a day in the period whose
* {@link #DAY_OF_WEEK DAY_OF_WEEK} is specified by the
* <code>knownDayOfWeek</code> parameter.
* Should be 1 for first day of period.
*
* @param knownDayOfWeek The {@link #DAY_OF_WEEK DAY_OF_WEEK} for the day
* corresponding to the <code>knownDayOfPeriod</code> parameter.
* 1-based with 1=Sunday.
*
* @return The week number (one-based), or zero if the day falls before
* the first week because
* {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek}
* is more than one.
*/
protected int weekNumber(int desiredDay, int dayOfPeriod, int dayOfWeek)
{
int length = getMaximum(DAY_OF_WEEK) - getMinimum(DAY_OF_WEEK) + 1;
// Determine the day of the week of the first day of the period
// in question (either a year or a month). Zero represents the
// first day of the week on this calendar.
int periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % length;
if (periodStartDayOfWeek < 0) periodStartDayOfWeek += length;
// Compute the week number. Initially, ignore the first week, which
// may be fractional (or may not be). We add periodStartDayOfWeek in
// order to fill out the first week, if it is fractional.
int weekNo = (desiredDay + periodStartDayOfWeek - 1)/length;
// If the first week is long enough, then count it. If
// the minimal days in the first week is one, or if the period start
// is zero, we always increment weekNo.
if ((length - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo;
return weekNo;
}
/**
* Return the week number of a day, within a period. This may be the week number in
* a year, or the week number in a month. Usually this will be a value >= 1, but if
* some initial days of the period are excluded from week 1, because
* {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek} is > 1,
* then the week number will be zero for those
* initial days. This method requires the day of week for the given date in order to
* determine the result.
* <p>
* <b>Subclassing:</b>
* <br>
* This method is intended for use by subclasses in implementing their
* {@link #computeTime computeTime} and/or {@link #computeFields computeFields} methods.
* It is often useful in {@link #getActualMinimum getActualMinimum} and
* {@link #getActualMaximum getActualMaximum} as well.
* <p>
* @param dayOfPeriod The {@link #DAY_OF_YEAR DAY_OF_YEAR} or
* {@link #DAY_OF_MONTH DAY_OF_MONTH} whose week number is desired.
* Should be 1 for the first day of the period.
*
* @param dayofWeek The {@link #DAY_OF_WEEK DAY_OF_WEEK} for the day
* corresponding to the <code>dayOfPeriod</code> parameter.
* 1-based with 1=Sunday.
*
* @return The week number (one-based), or zero if the day falls before
* the first week because
* {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek}
* is more than one.
*/
protected final int weekNumber(int dayOfPeriod, int dayOfWeek)
{
return weekNumber(dayOfPeriod, dayOfPeriod, dayOfWeek);
}
//-------------------------------------------------------------------------
// Constants
//-------------------------------------------------------------------------
private static final int SECOND_MS = 1000;
private static final int MINUTE_MS = 60*SECOND_MS;
private static final int HOUR_MS = 60*MINUTE_MS;
private static final long DAY_MS = 24*HOUR_MS;
private static final long WEEK_MS = 7*DAY_MS;
}