001: /*
002: *******************************************************************************
003: * Copyright (C) 1996-2006, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */
007:
008: package com.ibm.icu.util;
009:
010: import java.util.Date;
011:
012: /**
013: * Simple implementation of DateRule.
014: * @draft ICU 2.8 (retainAll)
015: * @provisional This API might change or be removed in a future release.
016: */
017: public class SimpleDateRule implements DateRule {
018: /**
019: * Construct a rule for a fixed date within a month
020: *
021: * @param month The month in which this rule occurs (0-based).
022: * @param dayOfMonth The date in that month (1-based).
023: * @draft ICU 2.8
024: * @provisional This API might change or be removed in a future release.
025: */
026: public SimpleDateRule(int month, int dayOfMonth) {
027: this .month = month;
028: this .dayOfMonth = dayOfMonth;
029: this .dayOfWeek = 0;
030: }
031:
032: // temporary
033: /* package */SimpleDateRule(int month, int dayOfMonth, Calendar cal) {
034: this .month = month;
035: this .dayOfMonth = dayOfMonth;
036: this .dayOfWeek = 0;
037: this .calendar = cal;
038: }
039:
040: /**
041: * Construct a rule for a weekday within a month, e.g. the first Monday.
042: *
043: * @param month The month in which this rule occurs (0-based).
044: * @param dayOfMonth A date within that month (1-based).
045: * @param dayOfWeek The day of the week on which this rule occurs.
046: * @param after If true, this rule selects the first dayOfWeek
047: * on or after dayOfMonth. If false, the rule selects
048: * the first dayOfWeek on or before dayOfMonth.
049: * @draft ICU 2.8
050: * @provisional This API might change or be removed in a future release.
051: */
052: public SimpleDateRule(int month, int dayOfMonth, int dayOfWeek,
053: boolean after) {
054: this .month = month;
055: this .dayOfMonth = dayOfMonth;
056: this .dayOfWeek = after ? dayOfWeek : -dayOfWeek;
057: }
058:
059: /**
060: * Return the first occurrance of the event represented by this rule
061: * that is on or after the given start date.
062: *
063: * @param start Only occurrances on or after this date are returned.
064: *
065: * @return The date on which this event occurs, or null if it
066: * does not occur on or after the start date.
067: *
068: * @see #firstBetween
069: * @draft ICU 2.8
070: * @provisional This API might change or be removed in a future release.
071: */
072: public Date firstAfter(Date start) {
073: return doFirstBetween(start, null);
074: }
075:
076: /**
077: * Return the first occurrance of the event represented by this rule
078: * that is on or after the given start date and before the given
079: * end date.
080: *
081: * @param start Only occurrances on or after this date are returned.
082: * @param end Only occurrances before this date are returned.
083: *
084: * @return The date on which this event occurs, or null if it
085: * does not occur between the start and end dates.
086: *
087: * @see #firstAfter
088: * @draft ICU 2.8
089: * @provisional This API might change or be removed in a future release.
090: */
091: public Date firstBetween(Date start, Date end) {
092: // Pin to the min/max dates for this rule
093: return doFirstBetween(start, end);
094: }
095:
096: /**
097: * Checks whether this event occurs on the given date. This does
098: * <em>not</em> take time of day into account; instead it checks
099: * whether this event and the given date are on the same day.
100: * This is useful for applications such as determining whether a given
101: * day is a holiday.
102: *
103: * @param date The date to check.
104: * @return true if this event occurs on the given date.
105: * @draft ICU 2.8
106: * @provisional This API might change or be removed in a future release.
107: */
108: public boolean isOn(Date date) {
109: Calendar c = calendar;
110:
111: synchronized (c) {
112: c.setTime(date);
113:
114: int dayOfYear = c.get(Calendar.DAY_OF_YEAR);
115:
116: c.setTime(computeInYear(c.get(Calendar.YEAR), c));
117:
118: // System.out.println(" isOn: dayOfYear = " + dayOfYear);
119: // System.out.println(" holiday = " + c.get(Calendar.DAY_OF_YEAR));
120:
121: return c.get(Calendar.DAY_OF_YEAR) == dayOfYear;
122: }
123: }
124:
125: /**
126: * Check whether this event occurs at least once between the two
127: * dates given.
128: * @draft ICU 2.8
129: * @provisional This API might change or be removed in a future release.
130: */
131: public boolean isBetween(Date start, Date end) {
132: return firstBetween(start, end) != null; // TODO: optimize?
133: }
134:
135: private Date doFirstBetween(Date start, Date end) {
136: Calendar c = calendar;
137:
138: synchronized (c) {
139: c.setTime(start);
140:
141: int year = c.get(Calendar.YEAR);
142: int month = c.get(Calendar.MONTH);
143:
144: // If the rule is earlier in the year than the start date
145: // we have to go to the next year.
146: if (month > this .month) {
147: year++;
148: }
149:
150: // Figure out when the rule lands in the given year
151: Date result = computeInYear(year, c);
152:
153: // If the rule is in the same month as the start date, it's possible
154: // to get a result that's before the start. If so, go to next year.
155: if (month == this .month && result.before(start)) {
156: result = computeInYear(year + 1, c);
157: }
158:
159: if (end != null && result.after(end)) {
160: return null;
161: }
162: return result;
163: }
164: }
165:
166: private Date computeInYear(int year, Calendar c) {
167: synchronized (c) {
168: c.clear();
169: c.set(Calendar.ERA, c.getMaximum(Calendar.ERA));
170: c.set(Calendar.YEAR, year);
171: c.set(Calendar.MONTH, month);
172: c.set(Calendar.DATE, dayOfMonth);
173:
174: //System.out.println(" computeInYear: start at " + c.getTime().toString());
175:
176: if (dayOfWeek != 0) {
177: c.setTime(c.getTime()); // JDK 1.1.2 workaround
178: int weekday = c.get(Calendar.DAY_OF_WEEK);
179:
180: //System.out.println(" weekday = " + weekday);
181: //System.out.println(" dayOfYear = " + c.get(Calendar.DAY_OF_YEAR));
182:
183: int delta = 0;
184: if (dayOfWeek > 0) {
185: // We want the first occurrance of the given day of the week
186: // on or after the specified date in the month.
187: delta = (dayOfWeek - weekday + 7) % 7;
188: } else {
189: // We want the first occurrance of the (-dayOfWeek)
190: // on or before the specified date in the month.
191: delta = -((dayOfWeek + weekday + 7) % 7);
192: }
193: //System.out.println(" adding " + delta + " days");
194: c.add(Calendar.DATE, delta);
195: }
196:
197: return c.getTime();
198: }
199: }
200:
201: /**
202: * @draft ICU 2.8
203: * @provisional This API might change or be removed in a future release.
204: */
205: // public void setCalendar(Calendar c) {
206: // calendar = c;
207: // }
208: private static GregorianCalendar gCalendar = new GregorianCalendar();
209:
210: private Calendar calendar = gCalendar;
211:
212: private int month;
213: private int dayOfMonth;
214: private int dayOfWeek;
215: };
|