1/* 2 ******************************************************************************* 3 * Copyright (C) 1996-2010, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 8package com.ibm.icu.util; 9 10import java.util.Date; 11 12/** 13 * <b>Note:</b> The Holiday framework is a technology preview. 14 * Despite its age, is still draft API, and clients should treat it as such. 15 * 16 * Simple implementation of DateRule. 17 * @draft ICU 2.8 (retainAll) 18 * @provisional This API might change or be removed in a future release. 19 */ 20public class SimpleDateRule implements DateRule 21{ 22 /** 23 * Construct a rule for a fixed date within a month 24 * 25 * @param month The month in which this rule occurs (0-based). 26 * @param dayOfMonth The date in that month (1-based). 27 * @draft ICU 2.8 28 * @provisional This API might change or be removed in a future release. 29 */ 30 public SimpleDateRule(int month, int dayOfMonth) 31 { 32 this.month = month; 33 this.dayOfMonth = dayOfMonth; 34 this.dayOfWeek = 0; 35 } 36 37 // temporary 38 /* package */SimpleDateRule(int month, int dayOfMonth, Calendar cal) 39 { 40 this.month = month; 41 this.dayOfMonth = dayOfMonth; 42 this.dayOfWeek = 0; 43 this.calendar = cal; 44 } 45 46 /** 47 * Construct a rule for a weekday within a month, e.g. the first Monday. 48 * 49 * @param month The month in which this rule occurs (0-based). 50 * @param dayOfMonth A date within that month (1-based). 51 * @param dayOfWeek The day of the week on which this rule occurs. 52 * @param after If true, this rule selects the first dayOfWeek 53 * on or after dayOfMonth. If false, the rule selects 54 * the first dayOfWeek on or before dayOfMonth. 55 * @draft ICU 2.8 56 * @provisional This API might change or be removed in a future release. 57 */ 58 public SimpleDateRule(int month, int dayOfMonth, int dayOfWeek, boolean after) 59 { 60 this.month = month; 61 this.dayOfMonth = dayOfMonth; 62 this.dayOfWeek = after ? dayOfWeek : -dayOfWeek; 63 } 64 65 /** 66 * Return the first occurrance of the event represented by this rule 67 * that is on or after the given start date. 68 * 69 * @param start Only occurrances on or after this date are returned. 70 * 71 * @return The date on which this event occurs, or null if it 72 * does not occur on or after the start date. 73 * 74 * @see #firstBetween 75 * @draft ICU 2.8 76 * @provisional This API might change or be removed in a future release. 77 */ 78 public Date firstAfter(Date start) 79 { 80 return doFirstBetween(start, null); 81 } 82 83 /** 84 * Return the first occurrance of the event represented by this rule 85 * that is on or after the given start date and before the given 86 * end date. 87 * 88 * @param start Only occurrances on or after this date are returned. 89 * @param end Only occurrances before this date are returned. 90 * 91 * @return The date on which this event occurs, or null if it 92 * does not occur between the start and end dates. 93 * 94 * @see #firstAfter 95 * @draft ICU 2.8 96 * @provisional This API might change or be removed in a future release. 97 */ 98 public Date firstBetween(Date start, Date end) 99 { 100 // Pin to the min/max dates for this rule 101 return doFirstBetween(start, end); 102 } 103 104 /** 105 * Checks whether this event occurs on the given date. This does 106 * <em>not</em> take time of day into account; instead it checks 107 * whether this event and the given date are on the same day. 108 * This is useful for applications such as determining whether a given 109 * day is a holiday. 110 * 111 * @param date The date to check. 112 * @return true if this event occurs on the given date. 113 * @draft ICU 2.8 114 * @provisional This API might change or be removed in a future release. 115 */ 116 public boolean isOn(Date date) 117 { 118 Calendar c = calendar; 119 120 synchronized(c) { 121 c.setTime(date); 122 123 int dayOfYear = c.get(Calendar.DAY_OF_YEAR); 124 125 c.setTime(computeInYear(c.get(Calendar.YEAR), c)); 126 127// System.out.println(" isOn: dayOfYear = " + dayOfYear); 128// System.out.println(" holiday = " + c.get(Calendar.DAY_OF_YEAR)); 129 130 return c.get(Calendar.DAY_OF_YEAR) == dayOfYear; 131 } 132 } 133 134 /** 135 * Check whether this event occurs at least once between the two 136 * dates given. 137 * @draft ICU 2.8 138 * @provisional This API might change or be removed in a future release. 139 */ 140 public boolean isBetween(Date start, Date end) 141 { 142 return firstBetween(start, end) != null; // TODO: optimize? 143 } 144 145 private Date doFirstBetween(Date start, Date end) 146 { 147 Calendar c = calendar; 148 149 synchronized(c) { 150 c.setTime(start); 151 152 int year = c.get(Calendar.YEAR); 153 int mon = c.get(Calendar.MONTH); 154 155 // If the rule is earlier in the year than the start date 156 // we have to go to the next year. 157 if (mon > this.month) { 158 year++; 159 } 160 161 // Figure out when the rule lands in the given year 162 Date result = computeInYear(year, c); 163 164 // If the rule is in the same month as the start date, it's possible 165 // to get a result that's before the start. If so, go to next year. 166 if (mon == this.month && result.before(start)) { 167 result = computeInYear(year+1, c); 168 } 169 170 if (end != null && result.after(end)) { 171 return null; 172 } 173 return result; 174 } 175 } 176 177 private Date computeInYear(int year, Calendar c) 178 { 179 synchronized(c) { 180 c.clear(); 181 c.set(Calendar.ERA, c.getMaximum(Calendar.ERA)); 182 c.set(Calendar.YEAR, year); 183 c.set(Calendar.MONTH, month); 184 c.set(Calendar.DATE, dayOfMonth); 185 186 //System.out.println(" computeInYear: start at " + c.getTime().toString()); 187 188 if (dayOfWeek != 0) { 189 c.setTime(c.getTime()); // JDK 1.1.2 workaround 190 int weekday = c.get(Calendar.DAY_OF_WEEK); 191 192 //System.out.println(" weekday = " + weekday); 193 //System.out.println(" dayOfYear = " + c.get(Calendar.DAY_OF_YEAR)); 194 195 int delta = 0; 196 if (dayOfWeek > 0) { 197 // We want the first occurrance of the given day of the week 198 // on or after the specified date in the month. 199 delta = (dayOfWeek - weekday + 7) % 7; 200 } 201 else { 202 // We want the first occurrance of the (-dayOfWeek) 203 // on or before the specified date in the month. 204 delta = -((dayOfWeek + weekday + 7) % 7); 205 } 206 //System.out.println(" adding " + delta + " days"); 207 c.add(Calendar.DATE, delta); 208 } 209 210 return c.getTime(); 211 } 212 } 213 214 /** 215 * @draft ICU 2.8 216 * @provisional This API might change or be removed in a future release. 217 */ 218// public void setCalendar(Calendar c) { 219// calendar = c; 220// } 221 222 private static GregorianCalendar gCalendar = new GregorianCalendar(); 223 224 private Calendar calendar = gCalendar; 225 226 private int month; 227 private int dayOfMonth; 228 private int dayOfWeek; 229} 230