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