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