001/*
002 * Copyright (c) 2007-2013, Stephen Colebourne & Michael Nascimento Santos
003 *
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 *  * Redistributions of source code must retain the above copyright notice,
010 *    this list of conditions and the following disclaimer.
011 *
012 *  * Redistributions in binary form must reproduce the above copyright notice,
013 *    this list of conditions and the following disclaimer in the documentation
014 *    and/or other materials provided with the distribution.
015 *
016 *  * Neither the name of JSR-310 nor the names of its contributors
017 *    may be used to endorse or promote products derived from this software
018 *    without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032package org.threeten.bp;
033
034import static org.threeten.bp.temporal.ChronoField.DAY_OF_WEEK;
035import static org.threeten.bp.temporal.ChronoUnit.DAYS;
036
037import java.util.Locale;
038
039import org.threeten.bp.format.DateTimeFormatterBuilder;
040import org.threeten.bp.format.TextStyle;
041import org.threeten.bp.temporal.ChronoField;
042import org.threeten.bp.temporal.Temporal;
043import org.threeten.bp.temporal.TemporalAccessor;
044import org.threeten.bp.temporal.TemporalAdjuster;
045import org.threeten.bp.temporal.TemporalField;
046import org.threeten.bp.temporal.TemporalQueries;
047import org.threeten.bp.temporal.TemporalQuery;
048import org.threeten.bp.temporal.ValueRange;
049import org.threeten.bp.temporal.WeekFields;
050
051/**
052 * A day-of-week, such as 'Tuesday'.
053 * <p>
054 * {@code DayOfWeek} is an enum representing the 7 days of the week -
055 * Monday, Tuesday, Wednesday, Thursday, Friday, Saturday and Sunday.
056 * <p>
057 * In addition to the textual enum name, each day-of-week has an {@code int} value.
058 * The {@code int} value follows the ISO-8601 standard, from 1 (Monday) to 7 (Sunday).
059 * It is recommended that applications use the enum rather than the {@code int} value
060 * to ensure code clarity.
061 * <p>
062 * This enum provides access to the localized textual form of the day-of-week.
063 * Some locales also assign different numeric values to the days, declaring
064 * Sunday to have the value 1, however this class provides no support for this.
065 * See {@link WeekFields} for localized week-numbering.
066 * <p>
067 * <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code DayOfWeek}.
068 * Use {@code getValue()} instead.</b>
069 * <p>
070 * This enum represents a common concept that is found in many calendar systems.
071 * As such, this enum may be used by any calendar system that has the day-of-week
072 * concept defined exactly equivalent to the ISO calendar system.
073 *
074 * <h3>Specification for implementors</h3>
075 * This is an immutable and thread-safe enum.
076 */
077public enum DayOfWeek implements TemporalAccessor, TemporalAdjuster {
078
079    /**
080     * The singleton instance for the day-of-week of Monday.
081     * This has the numeric value of {@code 1}.
082     */
083    MONDAY,
084    /**
085     * The singleton instance for the day-of-week of Tuesday.
086     * This has the numeric value of {@code 2}.
087     */
088    TUESDAY,
089    /**
090     * The singleton instance for the day-of-week of Wednesday.
091     * This has the numeric value of {@code 3}.
092     */
093    WEDNESDAY,
094    /**
095     * The singleton instance for the day-of-week of Thursday.
096     * This has the numeric value of {@code 4}.
097     */
098    THURSDAY,
099    /**
100     * The singleton instance for the day-of-week of Friday.
101     * This has the numeric value of {@code 5}.
102     */
103    FRIDAY,
104    /**
105     * The singleton instance for the day-of-week of Saturday.
106     * This has the numeric value of {@code 6}.
107     */
108    SATURDAY,
109    /**
110     * The singleton instance for the day-of-week of Sunday.
111     * This has the numeric value of {@code 7}.
112     */
113    SUNDAY;
114    /**
115     * Private cache of all the constants.
116     */
117    private static final DayOfWeek[] ENUMS = DayOfWeek.values();
118
119    //-----------------------------------------------------------------------
120    /**
121     * Obtains an instance of {@code DayOfWeek} from an {@code int} value.
122     * <p>
123     * {@code DayOfWeek} is an enum representing the 7 days of the week.
124     * This factory allows the enum to be obtained from the {@code int} value.
125     * The {@code int} value follows the ISO-8601 standard, from 1 (Monday) to 7 (Sunday).
126     *
127     * @param dayOfWeek  the day-of-week to represent, from 1 (Monday) to 7 (Sunday)
128     * @return the day-of-week singleton, not null
129     * @throws DateTimeException if the day-of-week is invalid
130     */
131    public static DayOfWeek of(int dayOfWeek) {
132        if (dayOfWeek < 1 || dayOfWeek > 7) {
133            throw new DateTimeException("Invalid value for DayOfWeek: " + dayOfWeek);
134        }
135        return ENUMS[dayOfWeek - 1];
136    }
137
138    //-----------------------------------------------------------------------
139    /**
140     * Obtains an instance of {@code DayOfWeek} from a temporal object.
141     * <p>
142     * A {@code TemporalAccessor} represents some form of date and time information.
143     * This factory converts the arbitrary temporal object to an instance of {@code DayOfWeek}.
144     * <p>
145     * The conversion extracts the {@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} field.
146     * <p>
147     * This method matches the signature of the functional interface {@link TemporalQuery}
148     * allowing it to be used as a query via method reference, {@code DayOfWeek::from}.
149     *
150     * @param temporal  the temporal object to convert, not null
151     * @return the day-of-week, not null
152     * @throws DateTimeException if unable to convert to a {@code DayOfWeek}
153     */
154    public static DayOfWeek from(TemporalAccessor temporal) {
155        if (temporal instanceof DayOfWeek) {
156            return (DayOfWeek) temporal;
157        }
158        return of(temporal.get(DAY_OF_WEEK));
159    }
160
161    //-----------------------------------------------------------------------
162    /**
163     * Gets the day-of-week {@code int} value.
164     * <p>
165     * The values are numbered following the ISO-8601 standard, from 1 (Monday) to 7 (Sunday).
166     * See {@link WeekFields#dayOfWeek} for localized week-numbering.
167     *
168     * @return the day-of-week, from 1 (Monday) to 7 (Sunday)
169     */
170    public int getValue() {
171        return ordinal() + 1;
172    }
173
174    //-----------------------------------------------------------------------
175    /**
176     * Gets the textual representation, such as 'Mon' or 'Friday'.
177     * <p>
178     * This returns the textual name used to identify the day-of-week.
179     * The parameters control the length of the returned text and the locale.
180     * <p>
181     * If no textual mapping is found then the {@link #getValue() numeric value} is returned.
182     *
183     * @param style  the length of the text required, not null
184     * @param locale  the locale to use, not null
185     * @return the text value of the day-of-week, not null
186     */
187    public String getText(TextStyle style, Locale locale) {
188        return new DateTimeFormatterBuilder().appendText(DAY_OF_WEEK, style).toFormatter(locale).print(this);
189    }
190
191    //-----------------------------------------------------------------------
192    /**
193     * Checks if the specified field is supported.
194     * <p>
195     * This checks if this day-of-week can be queried for the specified field.
196     * If false, then calling the {@link #range(TemporalField) range} and
197     * {@link #get(TemporalField) get} methods will throw an exception.
198     * <p>
199     * If the field is {@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} then
200     * this method returns true.
201     * All other {@code ChronoField} instances will return false.
202     * <p>
203     * If the field is not a {@code ChronoField}, then the result of this method
204     * is obtained by invoking {@code TemporalField.doIsSupported(TemporalAccessor)}
205     * passing {@code this} as the argument.
206     * Whether the field is supported is determined by the field.
207     *
208     * @param field  the field to check, null returns false
209     * @return true if the field is supported on this day-of-week, false if not
210     */
211    @Override
212    public boolean isSupported(TemporalField field) {
213        if (field instanceof ChronoField) {
214            return field == DAY_OF_WEEK;
215        }
216        return field != null && field.doIsSupported(this);
217    }
218
219    /**
220     * Gets the range of valid values for the specified field.
221     * <p>
222     * The range object expresses the minimum and maximum valid values for a field.
223     * This day-of-week is used to enhance the accuracy of the returned range.
224     * If it is not possible to return the range, because the field is not supported
225     * or for some other reason, an exception is thrown.
226     * <p>
227     * If the field is {@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} then the
228     * range of the day-of-week, from 1 to 7, will be returned.
229     * All other {@code ChronoField} instances will throw a {@code DateTimeException}.
230     * <p>
231     * If the field is not a {@code ChronoField}, then the result of this method
232     * is obtained by invoking {@code TemporalField.doRange(TemporalAccessor)}
233     * passing {@code this} as the argument.
234     * Whether the range can be obtained is determined by the field.
235     *
236     * @param field  the field to query the range for, not null
237     * @return the range of valid values for the field, not null
238     * @throws DateTimeException if the range for the field cannot be obtained
239     */
240    @Override
241    public ValueRange range(TemporalField field) {
242        if (field == DAY_OF_WEEK) {
243            return field.range();
244        } else if (field instanceof ChronoField) {
245            throw new DateTimeException("Unsupported field: " + field.getName());
246        }
247        return field.doRange(this);
248    }
249
250    /**
251     * Gets the value of the specified field from this day-of-week as an {@code int}.
252     * <p>
253     * This queries this day-of-week for the value for the specified field.
254     * The returned value will always be within the valid range of values for the field.
255     * If it is not possible to return the value, because the field is not supported
256     * or for some other reason, an exception is thrown.
257     * <p>
258     * If the field is {@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} then the
259     * value of the day-of-week, from 1 to 7, will be returned.
260     * All other {@code ChronoField} instances will throw a {@code DateTimeException}.
261     * <p>
262     * If the field is not a {@code ChronoField}, then the result of this method
263     * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)}
264     * passing {@code this} as the argument. Whether the value can be obtained,
265     * and what the value represents, is determined by the field.
266     *
267     * @param field  the field to get, not null
268     * @return the value for the field, within the valid range of values
269     * @throws DateTimeException if a value for the field cannot be obtained
270     * @throws DateTimeException if the range of valid values for the field exceeds an {@code int}
271     * @throws DateTimeException if the value is outside the range of valid values for the field
272     * @throws ArithmeticException if numeric overflow occurs
273     */
274    @Override
275    public int get(TemporalField field) {
276        if (field == DAY_OF_WEEK) {
277            return getValue();
278        }
279        return range(field).checkValidIntValue(getLong(field), field);
280    }
281
282    /**
283     * Gets the value of the specified field from this day-of-week as a {@code long}.
284     * <p>
285     * This queries this day-of-week for the value for the specified field.
286     * If it is not possible to return the value, because the field is not supported
287     * or for some other reason, an exception is thrown.
288     * <p>
289     * If the field is {@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} then the
290     * value of the day-of-week, from 1 to 7, will be returned.
291     * All other {@code ChronoField} instances will throw a {@code DateTimeException}.
292     * <p>
293     * If the field is not a {@code ChronoField}, then the result of this method
294     * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)}
295     * passing {@code this} as the argument. Whether the value can be obtained,
296     * and what the value represents, is determined by the field.
297     *
298     * @param field  the field to get, not null
299     * @return the value for the field
300     * @throws DateTimeException if a value for the field cannot be obtained
301     * @throws ArithmeticException if numeric overflow occurs
302     */
303    @Override
304    public long getLong(TemporalField field) {
305        if (field == DAY_OF_WEEK) {
306            return getValue();
307        } else if (field instanceof ChronoField) {
308            throw new DateTimeException("Unsupported field: " + field.getName());
309        }
310        return field.doGet(this);
311    }
312
313    //-----------------------------------------------------------------------
314    /**
315     * Returns the day-of-week that is the specified number of days after this one.
316     * <p>
317     * The calculation rolls around the end of the week from Sunday to Monday.
318     * The specified period may be negative.
319     * <p>
320     * This instance is immutable and unaffected by this method call.
321     *
322     * @param days  the days to add, positive or negative
323     * @return the resulting day-of-week, not null
324     */
325    public DayOfWeek plus(long days) {
326        int amount = (int) (days % 7);
327        return ENUMS[(ordinal() + (amount + 7)) % 7];
328    }
329
330    /**
331     * Returns the day-of-week that is the specified number of days before this one.
332     * <p>
333     * The calculation rolls around the start of the year from Monday to Sunday.
334     * The specified period may be negative.
335     * <p>
336     * This instance is immutable and unaffected by this method call.
337     *
338     * @param days  the days to subtract, positive or negative
339     * @return the resulting day-of-week, not null
340     */
341    public DayOfWeek minus(long days) {
342        return plus(-(days % 7));
343    }
344
345    //-----------------------------------------------------------------------
346    /**
347     * Queries this day-of-week using the specified query.
348     * <p>
349     * This queries this day-of-week using the specified query strategy object.
350     * The {@code TemporalQuery} object defines the logic to be used to
351     * obtain the result. Read the documentation of the query to understand
352     * what the result of this method will be.
353     * <p>
354     * The result of this method is obtained by invoking the
355     * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
356     * specified query passing {@code this} as the argument.
357     *
358     * @param <R> the type of the result
359     * @param query  the query to invoke, not null
360     * @return the query result, null may be returned (defined by the query)
361     * @throws DateTimeException if unable to query (defined by the query)
362     * @throws ArithmeticException if numeric overflow occurs (defined by the query)
363     */
364    @SuppressWarnings("unchecked")
365    @Override
366    public <R> R query(TemporalQuery<R> query) {
367        if (query == TemporalQueries.precision()) {
368            return (R) DAYS;
369        } else if (query == TemporalQueries.zoneId() || query == TemporalQueries.chrono()) {
370            return null;
371        }
372        return query.queryFrom(this);
373    }
374
375    /**
376     * Adjusts the specified temporal object to have this day-of-week.
377     * <p>
378     * This returns a temporal object of the same observable type as the input
379     * with the day-of-week changed to be the same as this.
380     * <p>
381     * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
382     * passing {@link ChronoField#DAY_OF_WEEK} as the field.
383     * Note that this adjusts forwards or backwards within a Monday to Sunday week.
384     * See {@link WeekFields#dayOfWeek} for localized week start days.
385     * See {@link TemporalAdjusters} for other adjusters
386     * with more control, such as {@code next(MONDAY)}.
387     * <p>
388     * In most cases, it is clearer to reverse the calling pattern by using
389     * {@link Temporal#with(TemporalAdjuster)}:
390     * <pre>
391     *   // these two lines are equivalent, but the second approach is recommended
392     *   temporal = thisDayOfWeek.adjustInto(temporal);
393     *   temporal = temporal.with(thisDayOfWeek);
394     * </pre>
395     * <p>
396     * For example, given a date that is a Wednesday, the following are output:
397     * <pre>
398     *   dateOnWed.with(MONDAY);     // two days earlier
399     *   dateOnWed.with(TUESDAY);    // one day earlier
400     *   dateOnWed.with(WEDNESDAY);  // same date
401     *   dateOnWed.with(THURSDAY);   // one day later
402     *   dateOnWed.with(FRIDAY);     // two days later
403     *   dateOnWed.with(SATURDAY);   // three days later
404     *   dateOnWed.with(SUNDAY);     // four days later
405     * </pre>
406     * <p>
407     * This instance is immutable and unaffected by this method call.
408     *
409     * @param temporal  the target object to be adjusted, not null
410     * @return the adjusted object, not null
411     * @throws DateTimeException if unable to make the adjustment
412     * @throws ArithmeticException if numeric overflow occurs
413     */
414    @Override
415    public Temporal adjustInto(Temporal temporal) {
416        return temporal.with(DAY_OF_WEEK, getValue());
417    }
418
419}