/* ******************************************************************************* * Copyright (C) 1996-2010, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ package com.ibm.icu.util; import java.util.Date; /** * Note: The Holiday framework is a technology preview. * Despite its age, is still draft API, and clients should treat it as such. * * Simple implementation of DateRule. * @draft ICU 2.8 (retainAll) * @provisional This API might change or be removed in a future release. */ public class SimpleDateRule implements DateRule { /** * Construct a rule for a fixed date within a month * * @param month The month in which this rule occurs (0-based). * @param dayOfMonth The date in that month (1-based). * @draft ICU 2.8 * @provisional This API might change or be removed in a future release. */ public SimpleDateRule(int month, int dayOfMonth) { this.month = month; this.dayOfMonth = dayOfMonth; this.dayOfWeek = 0; } // temporary /* package */SimpleDateRule(int month, int dayOfMonth, Calendar cal) { this.month = month; this.dayOfMonth = dayOfMonth; this.dayOfWeek = 0; this.calendar = cal; } /** * Construct a rule for a weekday within a month, e.g. the first Monday. * * @param month The month in which this rule occurs (0-based). * @param dayOfMonth A date within that month (1-based). * @param dayOfWeek The day of the week on which this rule occurs. * @param after If true, this rule selects the first dayOfWeek * on or after dayOfMonth. If false, the rule selects * the first dayOfWeek on or before dayOfMonth. * @draft ICU 2.8 * @provisional This API might change or be removed in a future release. */ public SimpleDateRule(int month, int dayOfMonth, int dayOfWeek, boolean after) { this.month = month; this.dayOfMonth = dayOfMonth; this.dayOfWeek = after ? dayOfWeek : -dayOfWeek; } /** * Return the first occurrance of the event represented by this rule * that is on or after the given start date. * * @param start Only occurrances on or after this date are returned. * * @return The date on which this event occurs, or null if it * does not occur on or after the start date. * * @see #firstBetween * @draft ICU 2.8 * @provisional This API might change or be removed in a future release. */ public Date firstAfter(Date start) { return doFirstBetween(start, null); } /** * Return the first occurrance of the event represented by this rule * that is on or after the given start date and before the given * end date. * * @param start Only occurrances on or after this date are returned. * @param end Only occurrances before this date are returned. * * @return The date on which this event occurs, or null if it * does not occur between the start and end dates. * * @see #firstAfter * @draft ICU 2.8 * @provisional This API might change or be removed in a future release. */ public Date firstBetween(Date start, Date end) { // Pin to the min/max dates for this rule return doFirstBetween(start, end); } /** * Checks whether this event occurs on the given date. This does * not take time of day into account; instead it checks * whether this event and the given date are on the same day. * This is useful for applications such as determining whether a given * day is a holiday. * * @param date The date to check. * @return true if this event occurs on the given date. * @draft ICU 2.8 * @provisional This API might change or be removed in a future release. */ public boolean isOn(Date date) { Calendar c = calendar; synchronized(c) { c.setTime(date); int dayOfYear = c.get(Calendar.DAY_OF_YEAR); c.setTime(computeInYear(c.get(Calendar.YEAR), c)); // System.out.println(" isOn: dayOfYear = " + dayOfYear); // System.out.println(" holiday = " + c.get(Calendar.DAY_OF_YEAR)); return c.get(Calendar.DAY_OF_YEAR) == dayOfYear; } } /** * Check whether this event occurs at least once between the two * dates given. * @draft ICU 2.8 * @provisional This API might change or be removed in a future release. */ public boolean isBetween(Date start, Date end) { return firstBetween(start, end) != null; // TODO: optimize? } private Date doFirstBetween(Date start, Date end) { Calendar c = calendar; synchronized(c) { c.setTime(start); int year = c.get(Calendar.YEAR); int mon = c.get(Calendar.MONTH); // If the rule is earlier in the year than the start date // we have to go to the next year. if (mon > this.month) { year++; } // Figure out when the rule lands in the given year Date result = computeInYear(year, c); // If the rule is in the same month as the start date, it's possible // to get a result that's before the start. If so, go to next year. if (mon == this.month && result.before(start)) { result = computeInYear(year+1, c); } if (end != null && result.after(end)) { return null; } return result; } } private Date computeInYear(int year, Calendar c) { synchronized(c) { c.clear(); c.set(Calendar.ERA, c.getMaximum(Calendar.ERA)); c.set(Calendar.YEAR, year); c.set(Calendar.MONTH, month); c.set(Calendar.DATE, dayOfMonth); //System.out.println(" computeInYear: start at " + c.getTime().toString()); if (dayOfWeek != 0) { c.setTime(c.getTime()); // JDK 1.1.2 workaround int weekday = c.get(Calendar.DAY_OF_WEEK); //System.out.println(" weekday = " + weekday); //System.out.println(" dayOfYear = " + c.get(Calendar.DAY_OF_YEAR)); int delta = 0; if (dayOfWeek > 0) { // We want the first occurrance of the given day of the week // on or after the specified date in the month. delta = (dayOfWeek - weekday + 7) % 7; } else { // We want the first occurrance of the (-dayOfWeek) // on or before the specified date in the month. delta = -((dayOfWeek + weekday + 7) % 7); } //System.out.println(" adding " + delta + " days"); c.add(Calendar.DATE, delta); } return c.getTime(); } } /** * @draft ICU 2.8 * @provisional This API might change or be removed in a future release. */ // public void setCalendar(Calendar c) { // calendar = c; // } private static GregorianCalendar gCalendar = new GregorianCalendar(); private Calendar calendar = gCalendar; private int month; private int dayOfMonth; private int dayOfWeek; }